]> xenbits.xensource.com Git - libvirt.git/commitdiff
daemon: move libvirtd code into src/remote/ directory
authorDaniel P. Berrangé <berrange@redhat.com>
Tue, 20 Feb 2018 13:16:28 +0000 (13:16 +0000)
committerDaniel P. Berrangé <berrange@redhat.com>
Thu, 22 Feb 2018 12:21:08 +0000 (12:21 +0000)
Having a daemon/ directory makes little sense from a code structure
point of view, as 90% of the code that is built into libvirtd already
lives in the src/ directory. The virtlockd and virlogd daemons also live
entirely in src/{locking,logging} directories. This moves the source
code for libvirtd into src/remote/, alongside the client code.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
21 files changed:
.gitignore
cfg.mk
daemon/Makefile.am
daemon/libvirtd-config.c [deleted file]
daemon/libvirtd-config.h [deleted file]
daemon/libvirtd.c [deleted file]
daemon/libvirtd.h [deleted file]
daemon/remote.c [deleted file]
daemon/remote.h [deleted file]
daemon/stream.c [deleted file]
daemon/stream.h [deleted file]
po/POTFILES.in
src/Makefile.am
src/remote/remote_daemon.c [new file with mode: 0644]
src/remote/remote_daemon.h [new file with mode: 0644]
src/remote/remote_daemon_config.c [new file with mode: 0644]
src/remote/remote_daemon_config.h [new file with mode: 0644]
src/remote/remote_daemon_dispatch.c [new file with mode: 0644]
src/remote/remote_daemon_dispatch.h [new file with mode: 0644]
src/remote/remote_daemon_stream.c [new file with mode: 0644]
src/remote/remote_daemon_stream.h [new file with mode: 0644]

index 189116a3d01317683b524552c5b65f94125cec7d..0f8446faa0d3b299f20b6f1314d0ad3faa12a520 100644 (file)
@@ -59,9 +59,6 @@
 /configure
 /configure.lineno
 /conftest.*
-/daemon/*_dispatch.h
-/daemon/libvirt_qemud
-/daemon/libvirtd
 /daemon/libvirtd*.logrotate
 /daemon/libvirtd.policy
 /daemon/test_libvirtd.aug
 /src/libvirt_*helper
 /src/libvirt_*probes.h
 /src/libvirt_lxc
+/src/libvirtd
 /src/locking/libxl-lockd.conf
 /src/locking/libxl-sanlock.conf
 /src/locking/lock_daemon_dispatch_stubs.h
 /src/qemu/test_libvirtd_qemu.aug
 /src/remote/*_client_bodies.h
 /src/remote/*_protocol.[ch]
+/src/remote/*_stubs.h
 /src/rpc/virkeepaliveprotocol.[ch]
 /src/rpc/virnetprotocol.[ch]
 /src/test_libvirt*.aug
diff --git a/cfg.mk b/cfg.mk
index ebfe4a339b533310ffa49e2a337f92e18b4c0c72..22de11ce434d8c34516c08dbc0bba030d367af1c 100644 (file)
--- a/cfg.mk
+++ b/cfg.mk
@@ -32,8 +32,7 @@ gnulib_dir = $(srcdir)/.gnulib
 # List of additional files that we want to pick up in our POTFILES.in
 # This is all gnulib files, as well as generated files for RPC code.
 generated_files = \
-  $(srcdir)/daemon/{lxc,qemu,remote}_dispatch.h \
-  $(srcdir)/src/*/{admin_server,log_daemon,lock_daemon}_dispatch_stubs.h \
+  $(srcdir)/src/*/{remote_daemon,admin_server,log_daemon,lock_daemon}_dispatch_*stubs.h \
   $(srcdir)/src/lxc/{lxc_monitor,lxc_controller}_dispatch.h \
   $(srcdir)/src/remote/*_client_bodies.h \
   $(srcdir)/src/*/*_protocol.[ch] \
@@ -1116,15 +1115,15 @@ test-wrap-argv:
 
 # sc_po_check can fail if generated files are not built first
 sc_po_check: \
-               $(srcdir)/daemon/remote_dispatch.h \
-               $(srcdir)/daemon/qemu_dispatch.h \
+               $(srcdir)/src/remote/remote_daemon_dispatch_stubs.h \
+               $(srcdir)/src/remote/remote_daemon_dispatch_qemu_stubs.h \
                $(srcdir)/src/remote/remote_client_bodies.h \
                $(srcdir)/src/admin/admin_server_dispatch_stubs.h \
                $(srcdir)/src/admin/admin_client.h
-$(srcdir)/daemon/remote_dispatch.h: $(srcdir)/src/remote/remote_protocol.x
-       $(MAKE) -C daemon remote_dispatch.h
-$(srcdir)/daemon/qemu_dispatch.h: $(srcdir)/src/remote/qemu_protocol.x
-       $(MAKE) -C daemon qemu_dispatch.h
+$(srcdir)/src/remote/remote_daemon_dispatch_stubs.h: $(srcdir)/src/remote/remote_protocol.x
+       $(MAKE) -C src remote/remote_daemon_dispatch_stubs.h
+$(srcdir)/src/remote/remote_daemon_dispatch_qemu_stubs.h: $(srcdir)/src/remote/qemu_protocol.x
+       $(MAKE) -C src remote/remote_daemon_dispatch_qemu_stubs.h
 $(srcdir)/src/remote/remote_client_bodies.h: $(srcdir)/src/remote/remote_protocol.x
        $(MAKE) -C src remote/remote_client_bodies.h
 $(srcdir)/src/admin/admin_server_dispatch_stubs.h: $(srcdir)/src/admin/admin_protocol.x
@@ -1138,7 +1137,7 @@ exclude_file_name_regexp--sc_avoid_strcase = ^tools/vsh\.h$$
 _src1=libvirt-stream|qemu/qemu_monitor|util/vir(command|file|fdstream)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon|logging/log_daemon
 _test1=shunloadtest|virnettlscontexttest|virnettlssessiontest|vircgroupmock|commandhelper
 exclude_file_name_regexp--sc_avoid_write = \
-  ^(src/($(_src1))|daemon/libvirtd|tools/virsh-console|tests/($(_test1)))\.c$$
+  ^(src/($(_src1))|tools/virsh-console|tests/($(_test1)))\.c$$
 
 exclude_file_name_regexp--sc_bindtextdomain = .*
 
@@ -1159,7 +1158,7 @@ exclude_file_name_regexp--sc_libvirt_unmarked_diagnostics = \
 exclude_file_name_regexp--sc_po_check = ^(docs/|src/rpc/gendispatch\.pl$$)
 
 exclude_file_name_regexp--sc_prohibit_VIR_ERR_NO_MEMORY = \
-  ^(cfg\.mk|include/libvirt/virterror\.h|daemon/dispatch\.c|src/util/virerror\.c|docs/internals/oomtesting\.html\.in)$$
+  ^(cfg\.mk|include/libvirt/virterror\.h|src/remote/remote_daemon_dispatch\.c|src/util/virerror\.c|docs/internals/oomtesting\.html\.in)$$
 
 exclude_file_name_regexp--sc_prohibit_PATH_MAX = \
        ^cfg\.mk$$
@@ -1179,9 +1178,9 @@ exclude_file_name_regexp--sc_prohibit_close = \
 exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \
   (^tests/(qemuhelp|virhostcpu|virpcitest)data/|docs/js/.*\.js|docs/fonts/.*\.woff|\.diff|tests/virconfdata/no-newline\.conf$$)
 
-_src2=src/(util/vircommand|libvirt|lxc/lxc_controller|locking/lock_daemon|logging/log_daemon)
+_src2=src/(util/vircommand|libvirt|lxc/lxc_controller|locking/lock_daemon|logging/log_daemon|remote/remote_daemon)
 exclude_file_name_regexp--sc_prohibit_fork_wrappers = \
-  (^($(_src2)|tests/testutils|daemon/libvirtd)\.c$$)
+  (^($(_src2)|tests/testutils)\.c$$)
 
 exclude_file_name_regexp--sc_prohibit_gethostname = ^src/util/vir(util|log)\.c$$
 
index 658f6c3122e8204b2087bb6e5a838836e08594d1..69c858e4b4a83da2d110882df89d4e4a1c7eab6f 100644 (file)
 ## License along with this library.  If not, see
 ## <http://www.gnu.org/licenses/>.
 
-INCLUDES = \
-       -I$(top_builddir)/gnulib/lib -I$(top_srcdir)/gnulib/lib \
-       -I$(top_srcdir) \
-       -I$(top_builddir)/include -I$(top_srcdir)/include \
-       -I$(top_builddir)/src -I$(top_srcdir)/src \
-       -I$(top_srcdir)/src/util \
-       -I$(top_srcdir)/src/conf \
-       -I$(top_srcdir)/src/rpc \
-       -I$(top_srcdir)/src/remote \
-       -I$(top_srcdir)/src/admin \
-       -I$(top_srcdir)/src/access \
-       $(GETTEXT_CPPFLAGS)
-
 CLEANFILES =
 
-WARN_CFLAGS += $(STRICT_FRAME_LIMIT_CFLAGS)
-
-DAEMON_GENERATED = \
-               remote_dispatch.h \
-               lxc_dispatch.h \
-               qemu_dispatch.h \
-               $(NULL)
-
-DAEMON_SOURCES = \
-               libvirtd.c libvirtd.h \
-               remote.c remote.h \
-               stream.c stream.h \
-               $(DAEMON_GENERATED)
-
-LIBVIRTD_CONF_SOURCES = libvirtd-config.c libvirtd-config.h
-
 PODFILES = \
        libvirtd.pod \
        $(NULL)
@@ -57,9 +28,6 @@ MANINFILES = \
 
 DISTCLEANFILES =
 EXTRA_DIST = \
-       remote_dispatch.h \
-       lxc_dispatch.h \
-       qemu_dispatch.h \
        libvirtd.conf \
        libvirtd.init.in \
        libvirtd.upstart \
@@ -79,59 +47,14 @@ EXTRA_DIST = \
        test_libvirtd.aug.in \
        $(PODFILES) \
        $(MANINFILES) \
-       $(DAEMON_SOURCES) \
-       $(LIBVIRTD_CONF_SOURCES) \
        $(NULL)
 
 BUILT_SOURCES =
 
-REMOTE_PROTOCOL = $(top_srcdir)/src/remote/remote_protocol.x
-LXC_PROTOCOL = $(top_srcdir)/src/remote/lxc_protocol.x
-QEMU_PROTOCOL = $(top_srcdir)/src/remote/qemu_protocol.x
-ADMIN_PROTOCOL = $(top_srcdir)/src/admin/admin_protocol.x
-
-remote_dispatch.h: $(top_srcdir)/src/rpc/gendispatch.pl \
-               $(REMOTE_PROTOCOL) Makefile.am
-       $(AM_V_GEN)$(PERL) -w $(top_srcdir)/src/rpc/gendispatch.pl \
-         --mode=server remote REMOTE $(REMOTE_PROTOCOL) \
-         > $(srcdir)/remote_dispatch.h
-
-lxc_dispatch.h: $(top_srcdir)/src/rpc/gendispatch.pl \
-               $(LXC_PROTOCOL) Makefile.am
-       $(AM_V_GEN)$(PERL) -w $(top_srcdir)/src/rpc/gendispatch.pl \
-         --mode=server lxc LXC $(LXC_PROTOCOL) \
-         > $(srcdir)/lxc_dispatch.h
-
-qemu_dispatch.h: $(top_srcdir)/src/rpc/gendispatch.pl \
-               $(QEMU_PROTOCOL) Makefile.am
-       $(AM_V_GEN)$(PERL) -w $(top_srcdir)/src/rpc/gendispatch.pl \
-         --mode=server qemu QEMU $(QEMU_PROTOCOL) \
-         > $(srcdir)/qemu_dispatch.h
-
 if WITH_LIBVIRTD
 
-# Build a convenience library, for reuse in tests/libvirtdconftest
-noinst_LTLIBRARIES = libvirtd_conf.la
-libvirtd_conf_la_SOURCES = $(LIBVIRTD_CONF_SOURCES)
-libvirtd_conf_la_CFLAGS = \
-       $(LIBXML_CFLAGS) \
-       $(XDR_CFLAGS) \
-       $(WARN_CFLAGS) $(PIE_CFLAGS) \
-       $(COVERAGE_CFLAGS) \
-       $(NULL)
-libvirtd_conf_la_LDFLAGS = \
-       $(RELRO_LDFLAGS) \
-       $(PIE_LDFLAGS) \
-       $(COVERAGE_LDFLAGS) \
-       $(NO_INDIRECT_LDFLAGS) \
-       $(NO_UNDEFINED_LDFLAGS) \
-       $(NULL)
-libvirtd_conf_la_LIBADD = $(LIBXML_LIBS)
-
 man8_MANS = libvirtd.8
 
-sbin_PROGRAMS = libvirtd
-
 confdir = $(sysconfdir)/libvirt/
 conf_DATA = libvirtd.conf
 
@@ -143,44 +66,6 @@ augeastests_DATA = test_libvirtd.aug
 
 CLEANFILES += test_libvirtd.aug
 
-libvirtd_SOURCES = $(DAEMON_SOURCES)
-
-#-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L
-libvirtd_CFLAGS = \
-       $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(SASL_CFLAGS) \
-       $(XDR_CFLAGS) $(DBUS_CFLAGS) $(LIBNL_CFLAGS) \
-       $(WARN_CFLAGS) $(PIE_CFLAGS) \
-       $(COVERAGE_CFLAGS) \
-       -DQEMUD_PID_FILE="\"$(QEMUD_PID_FILE)\""
-
-libvirtd_LDFLAGS = \
-       $(RELRO_LDFLAGS) \
-       $(PIE_LDFLAGS) \
-       $(COVERAGE_LDFLAGS) \
-       $(NO_INDIRECT_LDFLAGS) \
-       $(NO_UNDEFINED_LDFLAGS) \
-       $(NULL)
-
-libvirtd_LDADD = \
-       $(LIBXML_LIBS) \
-       $(GNUTLS_LIBS) \
-       $(SASL_LIBS) \
-       $(DBUS_LIBS) \
-       $(LIBNL_LIBS)
-
-if WITH_DTRACE_PROBES
-libvirtd_LDADD += ../src/libvirt_probes.lo
-endif WITH_DTRACE_PROBES
-
-libvirtd_LDADD += \
-       libvirtd_conf.la \
-       ../src/libvirt_driver_admin.la \
-       ../src/libvirt-lxc.la \
-       ../src/libvirt-qemu.la \
-       $(NULL)
-
-libvirtd_LDADD += ../src/libvirt.la
-
 if WITH_POLKIT
 if WITH_POLKIT0
 policydir = $(datadir)/PolicyKit/policy
@@ -238,9 +123,6 @@ install-data-polkit::
 uninstall-data-polkit::
 endif ! WITH_POLKIT
 
-remote.c: $(DAEMON_GENERATED)
-remote.h: $(DAEMON_GENERATED)
-
 LOGROTATE_CONFS = libvirtd.qemu.logrotate libvirtd.lxc.logrotate \
                  libvirtd.libxl.logrotate libvirtd.uml.logrotate \
                  libvirtd.logrotate
@@ -413,11 +295,6 @@ check-augeas: test_libvirtd.aug
          '$(AUGPARSE)' -I $(srcdir) test_libvirtd.aug; \
        fi
 
-
-# This must be added last, since functions it provides/replaces
-# are used by nearly every other library.
-libvirtd_LDADD += ../gnulib/lib/libgnu.la $(LIBSOCKET)
-
 else ! WITH_LIBVIRTD
 install-data-local: install-data-sasl
 uninstall-local:: uninstall-data-sasl
@@ -461,4 +338,4 @@ endif ! WITH_SASL
 
 CLEANFILES += $(BUILT_SOURCES) $(man8_MANS)
 CLEANFILES += *.cov *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda
-MAINTAINERCLEANFILES = $(MANINFILES) $(DAEMON_GENERATED)
+MAINTAINERCLEANFILES = $(MANINFILES)
diff --git a/daemon/libvirtd-config.c b/daemon/libvirtd-config.c
deleted file mode 100644 (file)
index 19b3d16..0000000
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * libvirtd-config.c: daemon start of day, guest process & i/o management
- *
- * Copyright (C) 2006-2012, 2014, 2015 Red Hat, Inc.
- * Copyright (C) 2006 Daniel P. Berrange
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#include <config.h>
-
-#include "libvirtd-config.h"
-#include "virconf.h"
-#include "viralloc.h"
-#include "virerror.h"
-#include "virlog.h"
-#include "rpc/virnetserver.h"
-#include "configmake.h"
-#include "remote/remote_protocol.h"
-#include "remote/remote_driver.h"
-#include "util/virnetdevopenvswitch.h"
-#include "virstring.h"
-#include "virutil.h"
-
-#define VIR_FROM_THIS VIR_FROM_CONF
-
-VIR_LOG_INIT("daemon.libvirtd-config");
-
-
-static int
-remoteConfigGetAuth(virConfPtr conf,
-                    const char *filename,
-                    const char *key,
-                    int *auth)
-{
-    char *authstr = NULL;
-
-    if (virConfGetValueString(conf, key, &authstr) < 0)
-        return -1;
-
-    if (!authstr)
-        return 0;
-
-    if (STREQ(authstr, "none")) {
-        *auth = VIR_NET_SERVER_SERVICE_AUTH_NONE;
-#if WITH_SASL
-    } else if (STREQ(authstr, "sasl")) {
-        *auth = VIR_NET_SERVER_SERVICE_AUTH_SASL;
-#endif
-    } else if (STREQ(authstr, "polkit")) {
-        *auth = VIR_NET_SERVER_SERVICE_AUTH_POLKIT;
-    } else {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("%s: %s: unsupported auth %s"),
-                       filename, key, authstr);
-        VIR_FREE(authstr);
-        return -1;
-    }
-
-    VIR_FREE(authstr);
-    return 0;
-}
-
-int
-daemonConfigFilePath(bool privileged, char **configfile)
-{
-    if (privileged) {
-        if (VIR_STRDUP(*configfile, SYSCONFDIR "/libvirt/libvirtd.conf") < 0)
-            goto error;
-    } else {
-        char *configdir = NULL;
-
-        if (!(configdir = virGetUserConfigDirectory()))
-            goto error;
-
-        if (virAsprintf(configfile, "%s/libvirtd.conf", configdir) < 0) {
-            VIR_FREE(configdir);
-            goto error;
-        }
-        VIR_FREE(configdir);
-    }
-
-    return 0;
-
- error:
-    return -1;
-}
-
-struct daemonConfig*
-daemonConfigNew(bool privileged ATTRIBUTE_UNUSED)
-{
-    struct daemonConfig *data;
-    char *localhost;
-    int ret;
-
-    if (VIR_ALLOC(data) < 0)
-        return NULL;
-
-    data->listen_tls = 1;
-    data->listen_tcp = 0;
-
-    if (VIR_STRDUP(data->tls_port, LIBVIRTD_TLS_PORT) < 0 ||
-        VIR_STRDUP(data->tcp_port, LIBVIRTD_TCP_PORT) < 0)
-        goto error;
-
-    /* Only default to PolicyKit if running as root */
-#if WITH_POLKIT
-    if (privileged) {
-        data->auth_unix_rw = REMOTE_AUTH_POLKIT;
-        data->auth_unix_ro = REMOTE_AUTH_POLKIT;
-    } else {
-#endif
-        data->auth_unix_rw = REMOTE_AUTH_NONE;
-        data->auth_unix_ro = REMOTE_AUTH_NONE;
-#if WITH_POLKIT
-    }
-#endif
-
-    if (VIR_STRDUP(data->unix_sock_rw_perms,
-                   data->auth_unix_rw == REMOTE_AUTH_POLKIT ? "0777" : "0700") < 0 ||
-        VIR_STRDUP(data->unix_sock_ro_perms, "0777") < 0 ||
-        VIR_STRDUP(data->unix_sock_admin_perms, "0700") < 0)
-        goto error;
-
-#if WITH_SASL
-    data->auth_tcp = REMOTE_AUTH_SASL;
-#else
-    data->auth_tcp = REMOTE_AUTH_NONE;
-#endif
-    data->auth_tls = REMOTE_AUTH_NONE;
-
-    data->mdns_adv = 0;
-
-    data->min_workers = 5;
-    data->max_workers = 20;
-    data->max_clients = 5000;
-    data->max_queued_clients = 1000;
-    data->max_anonymous_clients = 20;
-
-    data->prio_workers = 5;
-
-    data->max_client_requests = 5;
-
-    data->audit_level = 1;
-    data->audit_logging = 0;
-
-    data->keepalive_interval = 5;
-    data->keepalive_count = 5;
-
-    data->admin_min_workers = 5;
-    data->admin_max_workers = 20;
-    data->admin_max_clients = 5000;
-    data->admin_max_queued_clients = 20;
-    data->admin_max_client_requests = 5;
-
-    data->admin_keepalive_interval = 5;
-    data->admin_keepalive_count = 5;
-
-    data->ovs_timeout = VIR_NETDEV_OVS_DEFAULT_TIMEOUT;
-
-    localhost = virGetHostname();
-    if (localhost == NULL) {
-        /* we couldn't resolve the hostname; assume that we are
-         * running in disconnected operation, and report a less
-         * useful Avahi string
-         */
-        ret = VIR_STRDUP(data->mdns_name, "Virtualization Host");
-    } else {
-        char *tmp;
-        /* Extract the host part of the potentially FQDN */
-        if ((tmp = strchr(localhost, '.')))
-            *tmp = '\0';
-        ret = virAsprintf(&data->mdns_name, "Virtualization Host %s",
-                          localhost);
-    }
-    VIR_FREE(localhost);
-    if (ret < 0)
-        goto error;
-
-    return data;
-
- error:
-    daemonConfigFree(data);
-    return NULL;
-}
-
-void
-daemonConfigFree(struct daemonConfig *data)
-{
-    char **tmp;
-
-    if (!data)
-        return;
-
-    VIR_FREE(data->listen_addr);
-    VIR_FREE(data->tls_port);
-    VIR_FREE(data->tcp_port);
-    tmp = data->access_drivers;
-    while (tmp && *tmp) {
-        VIR_FREE(*tmp);
-        tmp++;
-    }
-    VIR_FREE(data->access_drivers);
-
-    VIR_FREE(data->unix_sock_admin_perms);
-    VIR_FREE(data->unix_sock_ro_perms);
-    VIR_FREE(data->unix_sock_rw_perms);
-    VIR_FREE(data->unix_sock_group);
-    VIR_FREE(data->unix_sock_dir);
-    VIR_FREE(data->mdns_name);
-
-    tmp = data->tls_allowed_dn_list;
-    while (tmp && *tmp) {
-        VIR_FREE(*tmp);
-        tmp++;
-    }
-    VIR_FREE(data->tls_allowed_dn_list);
-
-    tmp = data->sasl_allowed_username_list;
-    while (tmp && *tmp) {
-        VIR_FREE(*tmp);
-        tmp++;
-    }
-    VIR_FREE(data->sasl_allowed_username_list);
-    VIR_FREE(data->tls_priority);
-
-    VIR_FREE(data->key_file);
-    VIR_FREE(data->ca_file);
-    VIR_FREE(data->cert_file);
-    VIR_FREE(data->crl_file);
-
-    VIR_FREE(data->host_uuid);
-    VIR_FREE(data->host_uuid_source);
-    VIR_FREE(data->log_filters);
-    VIR_FREE(data->log_outputs);
-
-    VIR_FREE(data);
-}
-
-static int
-daemonConfigLoadOptions(struct daemonConfig *data,
-                        const char *filename,
-                        virConfPtr conf)
-{
-    if (virConfGetValueBool(conf, "listen_tcp", &data->listen_tcp) < 0)
-        goto error;
-    if (virConfGetValueBool(conf, "listen_tls", &data->listen_tls) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "tls_port", &data->tls_port) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "tcp_port", &data->tcp_port) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "listen_addr", &data->listen_addr) < 0)
-        goto error;
-
-    if (remoteConfigGetAuth(conf, filename, "auth_unix_rw", &data->auth_unix_rw) < 0)
-        goto error;
-#if WITH_POLKIT
-    /* Change default perms to be wide-open if PolicyKit is enabled.
-     * Admin can always override in config file
-     */
-    if (data->auth_unix_rw == REMOTE_AUTH_POLKIT) {
-        VIR_FREE(data->unix_sock_rw_perms);
-        if (VIR_STRDUP(data->unix_sock_rw_perms, "0777") < 0)
-            goto error;
-    }
-#endif
-    if (remoteConfigGetAuth(conf, filename, "auth_unix_ro", &data->auth_unix_ro) < 0)
-        goto error;
-    if (remoteConfigGetAuth(conf, filename, "auth_tcp", &data->auth_tcp) < 0)
-        goto error;
-    if (remoteConfigGetAuth(conf, filename, "auth_tls", &data->auth_tls) < 0)
-        goto error;
-
-    if (virConfGetValueStringList(conf, "access_drivers", false,
-                                  &data->access_drivers) < 0)
-        goto error;
-
-    if (virConfGetValueString(conf, "unix_sock_group", &data->unix_sock_group) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "unix_sock_admin_perms", &data->unix_sock_admin_perms) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "unix_sock_ro_perms", &data->unix_sock_ro_perms) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "unix_sock_rw_perms", &data->unix_sock_rw_perms) < 0)
-        goto error;
-
-    if (virConfGetValueString(conf, "unix_sock_dir", &data->unix_sock_dir) < 0)
-        goto error;
-
-    if (virConfGetValueBool(conf, "mdns_adv", &data->mdns_adv) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "mdns_name", &data->mdns_name) < 0)
-        goto error;
-
-    if (virConfGetValueBool(conf, "tls_no_sanity_certificate", &data->tls_no_sanity_certificate) < 0)
-        goto error;
-    if (virConfGetValueBool(conf, "tls_no_verify_certificate", &data->tls_no_verify_certificate) < 0)
-        goto error;
-
-    if (virConfGetValueString(conf, "key_file", &data->key_file) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "cert_file", &data->cert_file) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "ca_file", &data->ca_file) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "crl_file", &data->crl_file) < 0)
-        goto error;
-
-    if (virConfGetValueStringList(conf, "tls_allowed_dn_list", false,
-                                  &data->tls_allowed_dn_list) < 0)
-        goto error;
-
-
-    if (virConfGetValueStringList(conf, "sasl_allowed_username_list", false,
-                                  &data->sasl_allowed_username_list) < 0)
-        goto error;
-
-    if (virConfGetValueString(conf, "tls_priority", &data->tls_priority) < 0)
-        goto error;
-
-    if (virConfGetValueUInt(conf, "min_workers", &data->min_workers) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "max_workers", &data->max_workers) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "max_clients", &data->max_clients) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "max_queued_clients", &data->max_queued_clients) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "max_anonymous_clients", &data->max_anonymous_clients) < 0)
-        goto error;
-
-    if (virConfGetValueUInt(conf, "prio_workers", &data->prio_workers) < 0)
-        goto error;
-
-    if (virConfGetValueUInt(conf, "max_client_requests", &data->max_client_requests) < 0)
-        goto error;
-
-    if (virConfGetValueUInt(conf, "admin_min_workers", &data->admin_min_workers) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "admin_max_workers", &data->admin_max_workers) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "admin_max_clients", &data->admin_max_clients) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "admin_max_queued_clients", &data->admin_max_queued_clients) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "admin_max_client_requests", &data->admin_max_client_requests) < 0)
-        goto error;
-
-    if (virConfGetValueUInt(conf, "audit_level", &data->audit_level) < 0)
-        goto error;
-    if (virConfGetValueBool(conf, "audit_logging", &data->audit_logging) < 0)
-        goto error;
-
-    if (virConfGetValueString(conf, "host_uuid", &data->host_uuid) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "host_uuid_source", &data->host_uuid_source) < 0)
-        goto error;
-
-    if (virConfGetValueUInt(conf, "log_level", &data->log_level) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "log_filters", &data->log_filters) < 0)
-        goto error;
-    if (virConfGetValueString(conf, "log_outputs", &data->log_outputs) < 0)
-        goto error;
-
-    if (virConfGetValueInt(conf, "keepalive_interval", &data->keepalive_interval) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "keepalive_count", &data->keepalive_count) < 0)
-        goto error;
-
-    if (virConfGetValueInt(conf, "admin_keepalive_interval", &data->admin_keepalive_interval) < 0)
-        goto error;
-    if (virConfGetValueUInt(conf, "admin_keepalive_count", &data->admin_keepalive_count) < 0)
-        goto error;
-
-    if (virConfGetValueUInt(conf, "ovs_timeout", &data->ovs_timeout) < 0)
-        goto error;
-
-    return 0;
-
- error:
-    return -1;
-}
-
-
-/* Read the config file if it exists.
- * Only used in the remote case, hence the name.
- */
-int
-daemonConfigLoadFile(struct daemonConfig *data,
-                     const char *filename,
-                     bool allow_missing)
-{
-    virConfPtr conf;
-    int ret;
-
-    if (allow_missing &&
-        access(filename, R_OK) == -1 &&
-        errno == ENOENT)
-        return 0;
-
-    conf = virConfReadFile(filename, 0);
-    if (!conf)
-        return -1;
-
-    ret = daemonConfigLoadOptions(data, filename, conf);
-    virConfFree(conf);
-    return ret;
-}
-
-int daemonConfigLoadData(struct daemonConfig *data,
-                         const char *filename,
-                         const char *filedata)
-{
-    virConfPtr conf;
-    int ret;
-
-    conf = virConfReadString(filedata, 0);
-    if (!conf)
-        return -1;
-
-    ret = daemonConfigLoadOptions(data, filename, conf);
-    virConfFree(conf);
-    return ret;
-}
diff --git a/daemon/libvirtd-config.h b/daemon/libvirtd-config.h
deleted file mode 100644 (file)
index d618c96..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * libvirtd-config.h: daemon start of day, guest process & i/o management
- *
- * Copyright (C) 2006-2012, 2015 Red Hat, Inc.
- * Copyright (C) 2006 Daniel P. Berrange
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#ifndef __LIBVIRTD_CONFIG_H__
-# define __LIBVIRTD_CONFIG_H__
-
-# include "internal.h"
-
-struct daemonConfig {
-    char *host_uuid;
-    char *host_uuid_source;
-
-    bool listen_tls;
-    bool listen_tcp;
-    char *listen_addr;
-    char *tls_port;
-    char *tcp_port;
-
-    char *unix_sock_admin_perms;
-    char *unix_sock_ro_perms;
-    char *unix_sock_rw_perms;
-    char *unix_sock_group;
-    char *unix_sock_dir;
-
-    int auth_unix_rw;
-    int auth_unix_ro;
-    int auth_tcp;
-    int auth_tls;
-
-    char **access_drivers;
-
-    bool mdns_adv;
-    char *mdns_name;
-
-    bool tls_no_verify_certificate;
-    bool tls_no_sanity_certificate;
-    char **tls_allowed_dn_list;
-    char **sasl_allowed_username_list;
-    char *tls_priority;
-
-    char *key_file;
-    char *cert_file;
-    char *ca_file;
-    char *crl_file;
-
-    unsigned int min_workers;
-    unsigned int max_workers;
-    unsigned int max_clients;
-    unsigned int max_queued_clients;
-    unsigned int max_anonymous_clients;
-
-    unsigned int prio_workers;
-
-    unsigned int max_client_requests;
-
-    unsigned int log_level;
-    char *log_filters;
-    char *log_outputs;
-
-    unsigned int audit_level;
-    bool audit_logging;
-
-    int keepalive_interval;
-    unsigned int keepalive_count;
-
-    unsigned int admin_min_workers;
-    unsigned int admin_max_workers;
-    unsigned int admin_max_clients;
-    unsigned int admin_max_queued_clients;
-    unsigned int admin_max_client_requests;
-
-    int admin_keepalive_interval;
-    unsigned int admin_keepalive_count;
-
-    unsigned int ovs_timeout;
-};
-
-
-int daemonConfigFilePath(bool privileged, char **configfile);
-struct daemonConfig* daemonConfigNew(bool privileged);
-void daemonConfigFree(struct daemonConfig *data);
-int daemonConfigLoadFile(struct daemonConfig *data,
-                         const char *filename,
-                         bool allow_missing);
-int daemonConfigLoadData(struct daemonConfig *data,
-                         const char *filename,
-                         const char *filedata);
-
-#endif /* __LIBVIRTD_CONFIG_H__ */
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
deleted file mode 100644 (file)
index c5bb12b..0000000
+++ /dev/null
@@ -1,1554 +0,0 @@
-/*
- * libvirtd.c: daemon start of day, guest process & i/o management
- *
- * Copyright (C) 2006-2015 Red Hat, Inc.
- * Copyright (C) 2006 Daniel P. Berrange
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#include <config.h>
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <grp.h>
-
-#include "libvirt_internal.h"
-#include "virerror.h"
-#include "virfile.h"
-#include "virlog.h"
-#include "virpidfile.h"
-#include "virprocess.h"
-
-#define VIR_FROM_THIS VIR_FROM_QEMU
-
-#include "libvirtd.h"
-#include "libvirtd-config.h"
-
-#include "admin/admin_server_dispatch.h"
-#include "viruuid.h"
-#include "remote_driver.h"
-#include "viralloc.h"
-#include "virconf.h"
-#include "virnetlink.h"
-#include "virnetdaemon.h"
-#include "remote.h"
-#include "virhook.h"
-#include "viraudit.h"
-#include "virstring.h"
-#include "locking/lock_manager.h"
-#include "viraccessmanager.h"
-#include "virutil.h"
-#include "virgettext.h"
-#include "util/virnetdevopenvswitch.h"
-
-#include "driver.h"
-
-#include "configmake.h"
-
-#include "virdbus.h"
-
-VIR_LOG_INIT("daemon.libvirtd");
-
-#if WITH_SASL
-virNetSASLContextPtr saslCtxt = NULL;
-#endif
-virNetServerProgramPtr remoteProgram = NULL;
-virNetServerProgramPtr adminProgram = NULL;
-virNetServerProgramPtr qemuProgram = NULL;
-virNetServerProgramPtr lxcProgram = NULL;
-
-volatile bool driversInitialized = false;
-
-enum {
-    VIR_DAEMON_ERR_NONE = 0,
-    VIR_DAEMON_ERR_PIDFILE,
-    VIR_DAEMON_ERR_RUNDIR,
-    VIR_DAEMON_ERR_INIT,
-    VIR_DAEMON_ERR_SIGNAL,
-    VIR_DAEMON_ERR_PRIVS,
-    VIR_DAEMON_ERR_NETWORK,
-    VIR_DAEMON_ERR_CONFIG,
-    VIR_DAEMON_ERR_HOOKS,
-    VIR_DAEMON_ERR_AUDIT,
-
-    VIR_DAEMON_ERR_LAST
-};
-
-VIR_ENUM_DECL(virDaemonErr)
-VIR_ENUM_IMPL(virDaemonErr, VIR_DAEMON_ERR_LAST,
-              "Initialization successful",
-              "Unable to obtain pidfile",
-              "Unable to create rundir",
-              "Unable to initialize libvirt",
-              "Unable to setup signal handlers",
-              "Unable to drop privileges",
-              "Unable to initialize network sockets",
-              "Unable to load configuration file",
-              "Unable to look for hook scripts",
-              "Unable to initialize audit system")
-
-static int daemonForkIntoBackground(const char *argv0)
-{
-    int statuspipe[2];
-    if (pipe(statuspipe) < 0)
-        return -1;
-
-    pid_t pid = fork();
-    switch (pid) {
-    case 0:
-        {
-            /* intermediate child */
-            int stdinfd = -1;
-            int stdoutfd = -1;
-            int nextpid;
-
-            VIR_FORCE_CLOSE(statuspipe[0]);
-
-            if ((stdinfd = open("/dev/null", O_RDONLY)) <= STDERR_FILENO)
-                goto cleanup;
-            if ((stdoutfd = open("/dev/null", O_WRONLY)) <= STDERR_FILENO)
-                goto cleanup;
-            if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
-                goto cleanup;
-            if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
-                goto cleanup;
-            if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
-                goto cleanup;
-            if (VIR_CLOSE(stdinfd) < 0)
-                goto cleanup;
-            if (VIR_CLOSE(stdoutfd) < 0)
-                goto cleanup;
-
-            if (setsid() < 0)
-                goto cleanup;
-
-            nextpid = fork();
-            switch (nextpid) {
-            case 0: /* grandchild */
-                return statuspipe[1];
-            case -1: /* error */
-                goto cleanup;
-            default: /* intermediate child succeeded */
-                _exit(EXIT_SUCCESS);
-            }
-
-        cleanup:
-            VIR_FORCE_CLOSE(stdoutfd);
-            VIR_FORCE_CLOSE(stdinfd);
-            VIR_FORCE_CLOSE(statuspipe[1]);
-            _exit(EXIT_FAILURE);
-
-        }
-
-    case -1: /* error in parent */
-        goto error;
-
-    default:
-        {
-            /* parent */
-            int ret;
-            char status;
-
-            VIR_FORCE_CLOSE(statuspipe[1]);
-
-            /* We wait to make sure the first child forked successfully */
-            if (virProcessWait(pid, NULL, false) < 0)
-                goto error;
-
-            /* If we get here, then the grandchild was spawned, so we
-             * must exit.  Block until the second child initializes
-             * successfully */
-        again:
-            ret = read(statuspipe[0], &status, 1);
-            if (ret == -1 && errno == EINTR)
-                goto again;
-
-            VIR_FORCE_CLOSE(statuspipe[0]);
-
-            if (ret != 1) {
-                char ebuf[1024];
-
-                fprintf(stderr,
-                        _("%s: error: unable to determine if daemon is "
-                          "running: %s\n"), argv0,
-                        virStrerror(errno, ebuf, sizeof(ebuf)));
-                exit(EXIT_FAILURE);
-            } else if (status != 0) {
-                fprintf(stderr,
-                        _("%s: error: %s. Check /var/log/messages or run "
-                          "without --daemon for more info.\n"), argv0,
-                        virDaemonErrTypeToString(status));
-                exit(EXIT_FAILURE);
-            }
-            _exit(EXIT_SUCCESS);
-        }
-    }
-
- error:
-    VIR_FORCE_CLOSE(statuspipe[0]);
-    VIR_FORCE_CLOSE(statuspipe[1]);
-    return -1;
-}
-
-
-static int
-daemonUnixSocketPaths(struct daemonConfig *config,
-                      bool privileged,
-                      char **sockfile,
-                      char **rosockfile,
-                      char **admsockfile)
-{
-    int ret = -1;
-    char *rundir = NULL;
-
-    if (config->unix_sock_dir) {
-        if (virAsprintf(sockfile, "%s/libvirt-sock", config->unix_sock_dir) < 0)
-            goto cleanup;
-
-        if (privileged) {
-            if (virAsprintf(rosockfile, "%s/libvirt-sock-ro", config->unix_sock_dir) < 0 ||
-                virAsprintf(admsockfile, "%s/libvirt-admin-sock", config->unix_sock_dir) < 0)
-                goto cleanup;
-        }
-    } else {
-        if (privileged) {
-            if (VIR_STRDUP(*sockfile, LOCALSTATEDIR "/run/libvirt/libvirt-sock") < 0 ||
-                VIR_STRDUP(*rosockfile, LOCALSTATEDIR "/run/libvirt/libvirt-sock-ro") < 0 ||
-                VIR_STRDUP(*admsockfile, LOCALSTATEDIR "/run/libvirt/libvirt-admin-sock") < 0)
-                goto cleanup;
-        } else {
-            mode_t old_umask;
-
-            if (!(rundir = virGetUserRuntimeDirectory()))
-                goto cleanup;
-
-            old_umask = umask(077);
-            if (virFileMakePath(rundir) < 0) {
-                umask(old_umask);
-                goto cleanup;
-            }
-            umask(old_umask);
-
-            if (virAsprintf(sockfile, "%s/libvirt-sock", rundir) < 0 ||
-                virAsprintf(admsockfile, "%s/libvirt-admin-sock", rundir) < 0)
-                goto cleanup;
-        }
-    }
-
-    ret = 0;
- cleanup:
-    VIR_FREE(rundir);
-    return ret;
-}
-
-
-static void daemonErrorHandler(void *opaque ATTRIBUTE_UNUSED,
-                               virErrorPtr err ATTRIBUTE_UNUSED)
-{
-    /* Don't do anything, since logging infrastructure already
-     * took care of reporting the error */
-}
-
-static int daemonErrorLogFilter(virErrorPtr err, int priority)
-{
-    /* These error codes don't really reflect real errors. They
-     * are expected events that occur when an app tries to check
-     * whether a particular guest already exists. This filters
-     * them to a lower log level to prevent pollution of syslog
-     */
-    switch (err->code) {
-    case VIR_ERR_NO_DOMAIN:
-    case VIR_ERR_NO_NETWORK:
-    case VIR_ERR_NO_STORAGE_POOL:
-    case VIR_ERR_NO_STORAGE_VOL:
-    case VIR_ERR_NO_NODE_DEVICE:
-    case VIR_ERR_NO_INTERFACE:
-    case VIR_ERR_NO_NWFILTER:
-    case VIR_ERR_NO_SECRET:
-    case VIR_ERR_NO_DOMAIN_SNAPSHOT:
-    case VIR_ERR_OPERATION_INVALID:
-    case VIR_ERR_NO_DOMAIN_METADATA:
-    case VIR_ERR_NO_SERVER:
-    case VIR_ERR_NO_CLIENT:
-        return VIR_LOG_DEBUG;
-    }
-
-    return priority;
-}
-
-
-#define VIR_DAEMON_LOAD_MODULE(func, module) \
-    virDriverLoadModule(module, #func)
-static void daemonInitialize(void)
-{
-    /*
-     * Note that the order is important: the first ones have a higher
-     * priority when calling virStateInitialize. We must register the
-     * network, storage and nodedev drivers before any stateful domain
-     * driver, since their resources must be auto-started before any
-     * domains can be auto-started.
-     */
-    /* We don't care if any of these fail, because the whole point
-     * is to allow users to only install modules they want to use.
-     * If they try to open a connection for a module that
-     * is not loaded they'll get a suitable error at that point
-     */
-#ifdef WITH_NETWORK
-    VIR_DAEMON_LOAD_MODULE(networkRegister, "network");
-#endif
-#ifdef WITH_INTERFACE
-    VIR_DAEMON_LOAD_MODULE(interfaceRegister, "interface");
-#endif
-#ifdef WITH_STORAGE
-    VIR_DAEMON_LOAD_MODULE(storageRegister, "storage");
-#endif
-#ifdef WITH_NODE_DEVICES
-    VIR_DAEMON_LOAD_MODULE(nodedevRegister, "nodedev");
-#endif
-#ifdef WITH_SECRETS
-    VIR_DAEMON_LOAD_MODULE(secretRegister, "secret");
-#endif
-#ifdef WITH_NWFILTER
-    VIR_DAEMON_LOAD_MODULE(nwfilterRegister, "nwfilter");
-#endif
-#ifdef WITH_XEN
-    VIR_DAEMON_LOAD_MODULE(xenRegister, "xen");
-#endif
-#ifdef WITH_LIBXL
-    VIR_DAEMON_LOAD_MODULE(libxlRegister, "libxl");
-#endif
-#ifdef WITH_QEMU
-    VIR_DAEMON_LOAD_MODULE(qemuRegister, "qemu");
-#endif
-#ifdef WITH_LXC
-    VIR_DAEMON_LOAD_MODULE(lxcRegister, "lxc");
-#endif
-#ifdef WITH_UML
-    VIR_DAEMON_LOAD_MODULE(umlRegister, "uml");
-#endif
-#ifdef WITH_VBOX
-    VIR_DAEMON_LOAD_MODULE(vboxRegister, "vbox");
-#endif
-#ifdef WITH_BHYVE
-    VIR_DAEMON_LOAD_MODULE(bhyveRegister, "bhyve");
-#endif
-#ifdef WITH_VZ
-    VIR_DAEMON_LOAD_MODULE(vzRegister, "vz");
-#endif
-}
-#undef VIR_DAEMON_LOAD_MODULE
-
-
-static int ATTRIBUTE_NONNULL(3)
-daemonSetupNetworking(virNetServerPtr srv,
-                      virNetServerPtr srvAdm,
-                      struct daemonConfig *config,
-                      const char *sock_path,
-                      const char *sock_path_ro,
-                      const char *sock_path_adm,
-                      bool ipsock,
-                      bool privileged)
-{
-    virNetServerServicePtr svc = NULL;
-    virNetServerServicePtr svcAdm = NULL;
-    virNetServerServicePtr svcRO = NULL;
-    virNetServerServicePtr svcTCP = NULL;
-#if WITH_GNUTLS
-    virNetServerServicePtr svcTLS = NULL;
-#endif
-    gid_t unix_sock_gid = 0;
-    int unix_sock_ro_mask = 0;
-    int unix_sock_rw_mask = 0;
-    int unix_sock_adm_mask = 0;
-    int ret = -1;
-
-    unsigned int cur_fd = STDERR_FILENO + 1;
-    unsigned int nfds = virGetListenFDs();
-
-    if (config->unix_sock_group) {
-        if (virGetGroupID(config->unix_sock_group, &unix_sock_gid) < 0)
-            return ret;
-    }
-
-    if (nfds > (sock_path_ro ? 2 : 1)) {
-        VIR_ERROR(_("Too many (%u) FDs passed from caller"), nfds);
-        return ret;
-    }
-
-    if (virStrToLong_i(config->unix_sock_ro_perms, NULL, 8, &unix_sock_ro_mask) != 0) {
-        VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_ro_perms);
-        goto cleanup;
-    }
-
-    if (virStrToLong_i(config->unix_sock_admin_perms, NULL, 8, &unix_sock_adm_mask) != 0) {
-        VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_admin_perms);
-        goto cleanup;
-    }
-
-    if (virStrToLong_i(config->unix_sock_rw_perms, NULL, 8, &unix_sock_rw_mask) != 0) {
-        VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_rw_perms);
-        goto cleanup;
-    }
-
-    if (!(svc = virNetServerServiceNewFDOrUNIX(sock_path,
-                                               unix_sock_rw_mask,
-                                               unix_sock_gid,
-                                               config->auth_unix_rw,
-#if WITH_GNUTLS
-                                               NULL,
-#endif
-                                               false,
-                                               config->max_queued_clients,
-                                               config->max_client_requests,
-                                               nfds, &cur_fd)))
-        goto cleanup;
-    if (sock_path_ro) {
-        if (!(svcRO = virNetServerServiceNewFDOrUNIX(sock_path_ro,
-                                                     unix_sock_ro_mask,
-                                                     unix_sock_gid,
-                                                     config->auth_unix_ro,
-#if WITH_GNUTLS
-                                                     NULL,
-#endif
-                                                     true,
-                                                     config->max_queued_clients,
-                                                     config->max_client_requests,
-                                                     nfds, &cur_fd)))
-            goto cleanup;
-    }
-
-    if (virNetServerAddService(srv, svc,
-                               config->mdns_adv && !ipsock ?
-                               "_libvirt._tcp" :
-                               NULL) < 0)
-        goto cleanup;
-
-    if (svcRO &&
-        virNetServerAddService(srv, svcRO, NULL) < 0)
-        goto cleanup;
-
-    if (sock_path_adm) {
-        VIR_DEBUG("Registering unix socket %s", sock_path_adm);
-        if (!(svcAdm = virNetServerServiceNewUNIX(sock_path_adm,
-                                                  unix_sock_adm_mask,
-                                                  unix_sock_gid,
-                                                  REMOTE_AUTH_NONE,
-#if WITH_GNUTLS
-                                                  NULL,
-#endif
-                                                  false,
-                                                  config->admin_max_queued_clients,
-                                                  config->admin_max_client_requests)))
-            goto cleanup;
-
-        if (virNetServerAddService(srvAdm, svcAdm, NULL) < 0)
-            goto cleanup;
-    }
-
-    if (ipsock) {
-        if (config->listen_tcp) {
-            VIR_DEBUG("Registering TCP socket %s:%s",
-                      config->listen_addr, config->tcp_port);
-            if (!(svcTCP = virNetServerServiceNewTCP(config->listen_addr,
-                                                     config->tcp_port,
-                                                     AF_UNSPEC,
-                                                     config->auth_tcp,
-#if WITH_GNUTLS
-                                                     NULL,
-#endif
-                                                     false,
-                                                     config->max_queued_clients,
-                                                     config->max_client_requests)))
-                goto cleanup;
-
-            if (virNetServerAddService(srv, svcTCP,
-                                       config->mdns_adv ? "_libvirt._tcp" : NULL) < 0)
-                goto cleanup;
-        }
-
-#if WITH_GNUTLS
-        if (config->listen_tls) {
-            virNetTLSContextPtr ctxt = NULL;
-
-            if (config->ca_file ||
-                config->cert_file ||
-                config->key_file) {
-                if (!config->ca_file) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                                   _("No CA certificate path set to match server key/cert"));
-                    goto cleanup;
-                }
-                if (!config->cert_file) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                                   _("No server certificate path set to match server key"));
-                    goto cleanup;
-                }
-                if (!config->key_file) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                                   _("No server key path set to match server cert"));
-                    goto cleanup;
-                }
-                VIR_DEBUG("Using CA='%s' cert='%s' key='%s'",
-                          config->ca_file, config->cert_file, config->key_file);
-                if (!(ctxt = virNetTLSContextNewServer(config->ca_file,
-                                                       config->crl_file,
-                                                       config->cert_file,
-                                                       config->key_file,
-                                                       (const char *const*)config->tls_allowed_dn_list,
-                                                       config->tls_priority,
-                                                       config->tls_no_sanity_certificate ? false : true,
-                                                       config->tls_no_verify_certificate ? false : true)))
-                    goto cleanup;
-            } else {
-                if (!(ctxt = virNetTLSContextNewServerPath(NULL,
-                                                           !privileged,
-                                                           (const char *const*)config->tls_allowed_dn_list,
-                                                           config->tls_priority,
-                                                           config->tls_no_sanity_certificate ? false : true,
-                                                           config->tls_no_verify_certificate ? false : true)))
-                    goto cleanup;
-            }
-
-            VIR_DEBUG("Registering TLS socket %s:%s",
-                      config->listen_addr, config->tls_port);
-            if (!(svcTLS =
-                  virNetServerServiceNewTCP(config->listen_addr,
-                                            config->tls_port,
-                                            AF_UNSPEC,
-                                            config->auth_tls,
-                                            ctxt,
-                                            false,
-                                            config->max_queued_clients,
-                                            config->max_client_requests))) {
-                virObjectUnref(ctxt);
-                goto cleanup;
-            }
-            if (virNetServerAddService(srv, svcTLS,
-                                       config->mdns_adv &&
-                                       !config->listen_tcp ? "_libvirt._tcp" : NULL) < 0)
-                goto cleanup;
-
-            virObjectUnref(ctxt);
-        }
-#else
-        (void)privileged;
-        if (config->listen_tls) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("This libvirtd build does not support TLS"));
-            goto cleanup;
-        }
-#endif
-    }
-
-#if WITH_SASL
-    if (config->auth_unix_rw == REMOTE_AUTH_SASL ||
-        (sock_path_ro && config->auth_unix_ro == REMOTE_AUTH_SASL) ||
-# if WITH_GNUTLS
-        (ipsock && config->listen_tls && config->auth_tls == REMOTE_AUTH_SASL) ||
-# endif
-        (ipsock && config->listen_tcp && config->auth_tcp == REMOTE_AUTH_SASL)) {
-        saslCtxt = virNetSASLContextNewServer(
-            (const char *const*)config->sasl_allowed_username_list);
-        if (!saslCtxt)
-            goto cleanup;
-    }
-#endif
-
-    ret = 0;
-
- cleanup:
-#if WITH_GNUTLS
-    virObjectUnref(svcTLS);
-#endif
-    virObjectUnref(svcTCP);
-    virObjectUnref(svcRO);
-    virObjectUnref(svcAdm);
-    virObjectUnref(svc);
-    return ret;
-}
-
-
-/*
- * Set up the openvswitch timeout
- */
-static void
-daemonSetupNetDevOpenvswitch(struct daemonConfig *config)
-{
-    virNetDevOpenvswitchSetTimeout(config->ovs_timeout);
-}
-
-
-/*
- * Set up the logging environment
- * By default if daemonized all errors go to the logfile libvirtd.log,
- * but if verbose or error debugging is asked for then also output
- * informational and debug messages. Default size if 64 kB.
- */
-static int
-daemonSetupLogging(struct daemonConfig *config,
-                   bool privileged,
-                   bool verbose,
-                   bool godaemon)
-{
-    virLogReset();
-
-    /*
-     * Libvirtd's order of precedence is:
-     * cmdline > environment > config
-     *
-     * Given the precedence, we must process the variables in the opposite
-     * order, each one overriding the previous.
-     */
-    if (config->log_level != 0)
-        virLogSetDefaultPriority(config->log_level);
-
-    /* In case the config is empty, both filters and outputs will become empty,
-     * however we can't start with empty outputs, thus we'll need to define and
-     * setup a default one.
-     */
-    ignore_value(virLogSetFilters(config->log_filters));
-    ignore_value(virLogSetOutputs(config->log_outputs));
-
-    /* If there are some environment variables defined, use those instead */
-    virLogSetFromEnv();
-
-    /*
-     * Command line override for --verbose
-     */
-    if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
-        virLogSetDefaultPriority(VIR_LOG_INFO);
-
-    /* Define the default output. This is only applied if there was no setting
-     * from either the config or the environment.
-     */
-    if (virLogSetDefaultOutput("libvirtd.log", godaemon, privileged) < 0)
-        return -1;
-
-    if (virLogGetNbOutputs() == 0)
-        virLogSetOutputs(virLogGetDefaultOutput());
-
-    return 0;
-}
-
-
-static int
-daemonSetupAccessManager(struct daemonConfig *config)
-{
-    virAccessManagerPtr mgr;
-    const char *none[] = { "none", NULL };
-    const char **drv = (const char **)config->access_drivers;
-
-    if (!drv ||
-        !drv[0])
-        drv = none;
-
-    if (!(mgr = virAccessManagerNewStack(drv)))
-        return -1;
-
-    virAccessManagerSetDefault(mgr);
-    virObjectUnref(mgr);
-    return 0;
-}
-
-
-/* Display version information. */
-static void
-daemonVersion(const char *argv0)
-{
-    printf("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
-}
-
-#ifdef __sun
-static int
-daemonSetupPrivs(void)
-{
-    chown("/var/run/libvirt", SYSTEM_UID, SYSTEM_UID);
-
-    if (__init_daemon_priv(PU_RESETGROUPS | PU_CLEARLIMITSET,
-                           SYSTEM_UID, SYSTEM_UID, PRIV_XVM_CONTROL, NULL)) {
-        VIR_ERROR(_("additional privileges are required"));
-        return -1;
-    }
-
-    if (priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_FILE_LINK_ANY, PRIV_PROC_INFO,
-                 PRIV_PROC_SESSION, PRIV_PROC_EXEC, PRIV_PROC_FORK, NULL)) {
-        VIR_ERROR(_("failed to set reduced privileges"));
-        return -1;
-    }
-
-    return 0;
-}
-#else
-# define daemonSetupPrivs() 0
-#endif
-
-
-static void daemonShutdownHandler(virNetDaemonPtr dmn,
-                                  siginfo_t *sig ATTRIBUTE_UNUSED,
-                                  void *opaque ATTRIBUTE_UNUSED)
-{
-    virNetDaemonQuit(dmn);
-}
-
-static void daemonReloadHandler(virNetDaemonPtr dmn ATTRIBUTE_UNUSED,
-                                siginfo_t *sig ATTRIBUTE_UNUSED,
-                                void *opaque ATTRIBUTE_UNUSED)
-{
-    if (!driversInitialized) {
-        VIR_WARN("Drivers are not initialized, reload ignored");
-        return;
-    }
-
-    VIR_INFO("Reloading configuration on SIGHUP");
-    virHookCall(VIR_HOOK_DRIVER_DAEMON, "-",
-                VIR_HOOK_DAEMON_OP_RELOAD, SIGHUP, "SIGHUP", NULL, NULL);
-    if (virStateReload() < 0)
-        VIR_WARN("Error while reloading drivers");
-}
-
-static int daemonSetupSignals(virNetDaemonPtr dmn)
-{
-    if (virNetDaemonAddSignalHandler(dmn, SIGINT, daemonShutdownHandler, NULL) < 0)
-        return -1;
-    if (virNetDaemonAddSignalHandler(dmn, SIGQUIT, daemonShutdownHandler, NULL) < 0)
-        return -1;
-    if (virNetDaemonAddSignalHandler(dmn, SIGTERM, daemonShutdownHandler, NULL) < 0)
-        return -1;
-    if (virNetDaemonAddSignalHandler(dmn, SIGHUP, daemonReloadHandler, NULL) < 0)
-        return -1;
-    return 0;
-}
-
-
-static void daemonInhibitCallback(bool inhibit, void *opaque)
-{
-    virNetDaemonPtr dmn = opaque;
-
-    if (inhibit)
-        virNetDaemonAddShutdownInhibition(dmn);
-    else
-        virNetDaemonRemoveShutdownInhibition(dmn);
-}
-
-
-#ifdef WITH_DBUS
-static DBusConnection *sessionBus;
-static DBusConnection *systemBus;
-
-static void daemonStopWorker(void *opaque)
-{
-    virNetDaemonPtr dmn = opaque;
-
-    VIR_DEBUG("Begin stop dmn=%p", dmn);
-
-    ignore_value(virStateStop());
-
-    VIR_DEBUG("Completed stop dmn=%p", dmn);
-
-    /* Exit libvirtd cleanly */
-    virNetDaemonQuit(dmn);
-}
-
-
-/* We do this in a thread to not block the main loop */
-static void daemonStop(virNetDaemonPtr dmn)
-{
-    virThread thr;
-    virObjectRef(dmn);
-    if (virThreadCreate(&thr, false, daemonStopWorker, dmn) < 0)
-        virObjectUnref(dmn);
-}
-
-
-static DBusHandlerResult
-handleSessionMessageFunc(DBusConnection *connection ATTRIBUTE_UNUSED,
-                         DBusMessage *message,
-                         void *opaque)
-{
-    virNetDaemonPtr dmn = opaque;
-
-    VIR_DEBUG("dmn=%p", dmn);
-
-    if (dbus_message_is_signal(message,
-                               DBUS_INTERFACE_LOCAL,
-                               "Disconnected"))
-        daemonStop(dmn);
-
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-
-static DBusHandlerResult
-handleSystemMessageFunc(DBusConnection *connection ATTRIBUTE_UNUSED,
-                        DBusMessage *message,
-                        void *opaque)
-{
-    virNetDaemonPtr dmn = opaque;
-
-    VIR_DEBUG("dmn=%p", dmn);
-
-    if (dbus_message_is_signal(message,
-                               "org.freedesktop.login1.Manager",
-                               "PrepareForShutdown"))
-        daemonStop(dmn);
-
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-#endif
-
-
-static void daemonRunStateInit(void *opaque)
-{
-    virNetDaemonPtr dmn = opaque;
-    virIdentityPtr sysident = virIdentityGetSystem();
-
-    virIdentitySetCurrent(sysident);
-
-    /* Since driver initialization can take time inhibit daemon shutdown until
-       we're done so clients get a chance to connect */
-    daemonInhibitCallback(true, dmn);
-
-    /* Start the stateful HV drivers
-     * This is deliberately done after telling the parent process
-     * we're ready, since it can take a long time and this will
-     * seriously delay OS bootup process */
-    if (virStateInitialize(virNetDaemonIsPrivileged(dmn),
-                           daemonInhibitCallback,
-                           dmn) < 0) {
-        VIR_ERROR(_("Driver state initialization failed"));
-        /* Ensure the main event loop quits */
-        kill(getpid(), SIGTERM);
-        goto cleanup;
-    }
-
-    driversInitialized = true;
-
-#ifdef WITH_DBUS
-    /* Tie the non-privileged libvirtd to the session/shutdown lifecycle */
-    if (!virNetDaemonIsPrivileged(dmn)) {
-
-        sessionBus = virDBusGetSessionBus();
-        if (sessionBus != NULL)
-            dbus_connection_add_filter(sessionBus,
-                                       handleSessionMessageFunc, dmn, NULL);
-
-        systemBus = virDBusGetSystemBus();
-        if (systemBus != NULL) {
-            dbus_connection_add_filter(systemBus,
-                                       handleSystemMessageFunc, dmn, NULL);
-            dbus_bus_add_match(systemBus,
-                               "type='signal',sender='org.freedesktop.login1', interface='org.freedesktop.login1.Manager'",
-                               NULL);
-        }
-    }
-#endif
-    /* Only now accept clients from network */
-    virNetDaemonUpdateServices(dmn, true);
- cleanup:
-    daemonInhibitCallback(false, dmn);
-    virObjectUnref(dmn);
-    virObjectUnref(sysident);
-    virIdentitySetCurrent(NULL);
-}
-
-static int daemonStateInit(virNetDaemonPtr dmn)
-{
-    virThread thr;
-    virObjectRef(dmn);
-    if (virThreadCreate(&thr, false, daemonRunStateInit, dmn) < 0) {
-        virObjectUnref(dmn);
-        return -1;
-    }
-    return 0;
-}
-
-static int migrateProfile(void)
-{
-    char *old_base = NULL;
-    char *updated = NULL;
-    char *home = NULL;
-    char *xdg_dir = NULL;
-    char *config_dir = NULL;
-    const char *config_home;
-    int ret = -1;
-    mode_t old_umask;
-
-    VIR_DEBUG("Checking if user profile needs migrating");
-
-    if (!(home = virGetUserDirectory()))
-        goto cleanup;
-
-    if (virAsprintf(&old_base, "%s/.libvirt", home) < 0)
-        goto cleanup;
-
-    /* if the new directory is there or the old one is not: do nothing */
-    if (!(config_dir = virGetUserConfigDirectory()))
-        goto cleanup;
-
-    if (!virFileIsDir(old_base) || virFileExists(config_dir)) {
-        VIR_DEBUG("No old profile in '%s' / "
-                  "new profile directory already present '%s'",
-                  old_base, config_dir);
-        ret = 0;
-        goto cleanup;
-    }
-
-    /* test if we already attempted to migrate first */
-    if (virAsprintf(&updated, "%s/DEPRECATED-DIRECTORY", old_base) < 0)
-        goto cleanup;
-    if (virFileExists(updated))
-        goto cleanup;
-
-    config_home = virGetEnvBlockSUID("XDG_CONFIG_HOME");
-    if (config_home && config_home[0] != '\0') {
-        if (VIR_STRDUP(xdg_dir, config_home) < 0)
-            goto cleanup;
-    } else {
-        if (virAsprintf(&xdg_dir, "%s/.config", home) < 0)
-            goto cleanup;
-    }
-
-    old_umask = umask(077);
-    if (virFileMakePath(xdg_dir) < 0) {
-        umask(old_umask);
-        goto cleanup;
-    }
-    umask(old_umask);
-
-    if (rename(old_base, config_dir) < 0) {
-        int fd = creat(updated, 0600);
-        VIR_FORCE_CLOSE(fd);
-        VIR_ERROR(_("Unable to migrate %s to %s"), old_base, config_dir);
-        goto cleanup;
-    }
-
-    VIR_DEBUG("Profile migrated from %s to %s", old_base, config_dir);
-    ret = 0;
-
- cleanup:
-    VIR_FREE(home);
-    VIR_FREE(old_base);
-    VIR_FREE(xdg_dir);
-    VIR_FREE(config_dir);
-    VIR_FREE(updated);
-
-    return ret;
-}
-
-static int
-daemonSetupHostUUID(const struct daemonConfig *config)
-{
-    static const char *machine_id = "/etc/machine-id";
-    char buf[VIR_UUID_STRING_BUFLEN];
-    const char *uuid;
-
-    if (config->host_uuid) {
-        uuid = config->host_uuid;
-    } else if (!config->host_uuid_source ||
-               STREQ(config->host_uuid_source, "smbios")) {
-        /* smbios UUID is fetched on demand in virGetHostUUID */
-        return 0;
-    } else if (STREQ(config->host_uuid_source, "machine-id")) {
-        if (virFileReadBufQuiet(machine_id, buf, sizeof(buf)) < 0) {
-            VIR_ERROR(_("Can't read %s"), machine_id);
-            return -1;
-        }
-
-        uuid = buf;
-    } else {
-        VIR_ERROR(_("invalid UUID source: %s"), config->host_uuid_source);
-        return -1;
-    }
-
-    if (virSetHostUUIDStr(uuid)) {
-        VIR_ERROR(_("invalid host UUID: %s"), uuid);
-        return -1;
-    }
-
-    return 0;
-}
-
-/* Print command-line usage. */
-static void
-daemonUsage(const char *argv0, bool privileged)
-{
-    fprintf(stderr,
-            _("\n"
-              "Usage:\n"
-              "  %s [options]\n"
-              "\n"
-              "Options:\n"
-              "  -h | --help            Display program help:\n"
-              "  -v | --verbose         Verbose messages.\n"
-              "  -d | --daemon          Run as a daemon & write PID file.\n"
-              "  -l | --listen          Listen for TCP/IP connections.\n"
-              "  -t | --timeout <secs>  Exit after timeout period.\n"
-              "  -f | --config <file>   Configuration file.\n"
-              "  -V | --version         Display version information.\n"
-              "  -p | --pid-file <file> Change name of PID file.\n"
-              "\n"
-              "libvirt management daemon:\n"),
-            argv0);
-
-    if (privileged) {
-        fprintf(stderr,
-                _("\n"
-                  "  Default paths:\n"
-                  "\n"
-                  "    Configuration file (unless overridden by -f):\n"
-                  "      %s\n"
-                  "\n"
-                  "    Sockets:\n"
-                  "      %s\n"
-                  "      %s\n"
-                  "\n"
-                  "    TLS:\n"
-                  "      CA certificate:     %s\n"
-                  "      Server certificate: %s\n"
-                  "      Server private key: %s\n"
-                  "\n"
-                  "    PID file (unless overridden by -p):\n"
-                  "      %s/run/libvirtd.pid\n"
-                  "\n"),
-                LIBVIRTD_CONFIGURATION_FILE,
-                LIBVIRTD_PRIV_UNIX_SOCKET,
-                LIBVIRTD_PRIV_UNIX_SOCKET_RO,
-                LIBVIRT_CACERT,
-                LIBVIRT_SERVERCERT,
-                LIBVIRT_SERVERKEY,
-                LOCALSTATEDIR);
-    } else {
-        fprintf(stderr, "%s",
-                _("\n"
-                  "  Default paths:\n"
-                  "\n"
-                  "    Configuration file (unless overridden by -f):\n"
-                  "      $XDG_CONFIG_HOME/libvirt/libvirtd.conf\n"
-                  "\n"
-                  "    Sockets:\n"
-                  "      $XDG_RUNTIME_DIR/libvirt/libvirt-sock\n"
-                  "\n"
-                  "    TLS:\n"
-                  "      CA certificate:     $HOME/.pki/libvirt/cacert.pem\n"
-                  "      Server certificate: $HOME/.pki/libvirt/servercert.pem\n"
-                  "      Server private key: $HOME/.pki/libvirt/serverkey.pem\n"
-                  "\n"
-                  "    PID file:\n"
-                  "      $XDG_RUNTIME_DIR/libvirt/libvirtd.pid\n"
-                  "\n"));
-    }
-}
-
-int main(int argc, char **argv) {
-    virNetDaemonPtr dmn = NULL;
-    virNetServerPtr srv = NULL;
-    virNetServerPtr srvAdm = NULL;
-    char *remote_config_file = NULL;
-    int statuswrite = -1;
-    int ret = 1;
-    int pid_file_fd = -1;
-    char *pid_file = NULL;
-    char *sock_file = NULL;
-    char *sock_file_ro = NULL;
-    char *sock_file_adm = NULL;
-    int timeout = -1;        /* -t: Shutdown timeout */
-    int verbose = 0;
-    int godaemon = 0;
-    int ipsock = 0;
-    struct daemonConfig *config;
-    bool privileged = geteuid() == 0 ? true : false;
-    bool implicit_conf = false;
-    char *run_dir = NULL;
-    mode_t old_umask;
-
-    struct option opts[] = {
-        { "verbose", no_argument, &verbose, 'v'},
-        { "daemon", no_argument, &godaemon, 'd'},
-        { "listen", no_argument, &ipsock, 'l'},
-        { "config", required_argument, NULL, 'f'},
-        { "timeout", required_argument, NULL, 't'},
-        { "pid-file", required_argument, NULL, 'p'},
-        { "version", no_argument, NULL, 'V' },
-        { "help", no_argument, NULL, 'h' },
-        {0, 0, 0, 0}
-    };
-
-    if (virGettextInitialize() < 0 ||
-        virInitialize() < 0) {
-        fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
-        exit(EXIT_FAILURE);
-    }
-
-    virUpdateSelfLastChanged(argv[0]);
-
-    virFileActivateDirOverride(argv[0]);
-
-    while (1) {
-        int optidx = 0;
-        int c;
-        char *tmp;
-
-        c = getopt_long(argc, argv, "ldf:p:t:vVh", opts, &optidx);
-
-        if (c == -1)
-            break;
-
-        switch (c) {
-        case 0:
-            /* Got one of the flags */
-            break;
-        case 'v':
-            verbose = 1;
-            break;
-        case 'd':
-            godaemon = 1;
-            break;
-        case 'l':
-            ipsock = 1;
-            break;
-
-        case 't':
-            if (virStrToLong_i(optarg, &tmp, 10, &timeout) != 0
-                || timeout <= 0
-                /* Ensure that we can multiply by 1000 without overflowing.  */
-                || timeout > INT_MAX / 1000) {
-                VIR_ERROR(_("Invalid value for timeout"));
-                exit(EXIT_FAILURE);
-            }
-            break;
-
-        case 'p':
-            VIR_FREE(pid_file);
-            if (VIR_STRDUP_QUIET(pid_file, optarg) < 0) {
-                VIR_ERROR(_("Can't allocate memory"));
-                exit(EXIT_FAILURE);
-            }
-            break;
-
-        case 'f':
-            VIR_FREE(remote_config_file);
-            if (VIR_STRDUP_QUIET(remote_config_file, optarg) < 0) {
-                VIR_ERROR(_("Can't allocate memory"));
-                exit(EXIT_FAILURE);
-            }
-            break;
-
-        case 'V':
-            daemonVersion(argv[0]);
-            exit(EXIT_SUCCESS);
-
-        case 'h':
-            daemonUsage(argv[0], privileged);
-            exit(EXIT_SUCCESS);
-
-        case '?':
-        default:
-            daemonUsage(argv[0], privileged);
-            exit(EXIT_FAILURE);
-        }
-    }
-
-    if (optind != argc) {
-        fprintf(stderr, "%s: unexpected, non-option, command line arguments\n",
-                argv[0]);
-        exit(EXIT_FAILURE);
-    }
-
-    if (!(config = daemonConfigNew(privileged))) {
-        VIR_ERROR(_("Can't create initial configuration"));
-        exit(EXIT_FAILURE);
-    }
-
-    /* No explicit config, so try and find a default one */
-    if (remote_config_file == NULL) {
-        implicit_conf = true;
-        if (daemonConfigFilePath(privileged,
-                                 &remote_config_file) < 0) {
-            VIR_ERROR(_("Can't determine config path"));
-            exit(EXIT_FAILURE);
-        }
-    }
-
-    /* Read the config file if it exists*/
-    if (remote_config_file &&
-        daemonConfigLoadFile(config, remote_config_file, implicit_conf) < 0) {
-        VIR_ERROR(_("Can't load config file: %s: %s"),
-                  virGetLastErrorMessage(), remote_config_file);
-        exit(EXIT_FAILURE);
-    }
-
-    if (!privileged &&
-        migrateProfile() < 0) {
-        VIR_ERROR(_("Exiting due to failure to migrate profile"));
-        exit(EXIT_FAILURE);
-    }
-
-    if (daemonSetupHostUUID(config) < 0) {
-        VIR_ERROR(_("Can't setup host uuid"));
-        exit(EXIT_FAILURE);
-    }
-
-    if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0) {
-        VIR_ERROR(_("Can't initialize logging"));
-        exit(EXIT_FAILURE);
-    }
-
-    daemonSetupNetDevOpenvswitch(config);
-
-    if (daemonSetupAccessManager(config) < 0) {
-        VIR_ERROR(_("Can't initialize access manager"));
-        exit(EXIT_FAILURE);
-    }
-
-    if (!pid_file &&
-        virPidFileConstructPath(privileged,
-                                LOCALSTATEDIR,
-                                "libvirtd",
-                                &pid_file) < 0) {
-        VIR_ERROR(_("Can't determine pid file path."));
-        exit(EXIT_FAILURE);
-    }
-    VIR_DEBUG("Decided on pid file path '%s'", NULLSTR(pid_file));
-
-    if (daemonUnixSocketPaths(config,
-                              privileged,
-                              &sock_file,
-                              &sock_file_ro,
-                              &sock_file_adm) < 0) {
-        VIR_ERROR(_("Can't determine socket paths"));
-        exit(EXIT_FAILURE);
-    }
-    VIR_DEBUG("Decided on socket paths '%s', '%s' and '%s'",
-              sock_file,
-              NULLSTR(sock_file_ro),
-              NULLSTR(sock_file_adm));
-
-    if (godaemon) {
-        char ebuf[1024];
-
-        if (chdir("/") < 0) {
-            VIR_ERROR(_("cannot change to root directory: %s"),
-                      virStrerror(errno, ebuf, sizeof(ebuf)));
-            goto cleanup;
-        }
-
-        if ((statuswrite = daemonForkIntoBackground(argv[0])) < 0) {
-            VIR_ERROR(_("Failed to fork as daemon: %s"),
-                      virStrerror(errno, ebuf, sizeof(ebuf)));
-            goto cleanup;
-        }
-    }
-
-    /* Try to claim the pidfile, exiting if we can't */
-    if ((pid_file_fd = virPidFileAcquirePath(pid_file, false, getpid())) < 0) {
-        ret = VIR_DAEMON_ERR_PIDFILE;
-        goto cleanup;
-    }
-
-    /* Ensure the rundir exists (on tmpfs on some systems) */
-    if (privileged) {
-        if (VIR_STRDUP_QUIET(run_dir, LOCALSTATEDIR "/run/libvirt") < 0) {
-            VIR_ERROR(_("Can't allocate memory"));
-            goto cleanup;
-        }
-    } else {
-        run_dir = virGetUserRuntimeDirectory();
-
-        if (!run_dir) {
-            VIR_ERROR(_("Can't determine user directory"));
-            goto cleanup;
-        }
-    }
-    if (privileged)
-        old_umask = umask(022);
-    else
-        old_umask = umask(077);
-    VIR_DEBUG("Ensuring run dir '%s' exists", run_dir);
-    if (virFileMakePath(run_dir) < 0) {
-        char ebuf[1024];
-        VIR_ERROR(_("unable to create rundir %s: %s"), run_dir,
-                  virStrerror(errno, ebuf, sizeof(ebuf)));
-        ret = VIR_DAEMON_ERR_RUNDIR;
-        goto cleanup;
-    }
-    umask(old_umask);
-
-    if (virNetlinkStartup() < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (!(dmn = virNetDaemonNew())) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (!(srv = virNetServerNew("libvirtd", 1,
-                                config->min_workers,
-                                config->max_workers,
-                                config->prio_workers,
-                                config->max_clients,
-                                config->max_anonymous_clients,
-                                config->keepalive_interval,
-                                config->keepalive_count,
-                                config->mdns_adv ? config->mdns_name : NULL,
-                                remoteClientNew,
-                                NULL,
-                                remoteClientFree,
-                                NULL))) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (virNetDaemonAddServer(dmn, srv) < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    /* Beyond this point, nothing should rely on using
-     * getuid/geteuid() == 0, for privilege level checks.
-     */
-    VIR_DEBUG("Dropping privileges (if required)");
-    if (daemonSetupPrivs() < 0) {
-        ret = VIR_DAEMON_ERR_PRIVS;
-        goto cleanup;
-    }
-
-    daemonInitialize();
-
-    remoteProcs[REMOTE_PROC_AUTH_LIST].needAuth = false;
-    remoteProcs[REMOTE_PROC_AUTH_SASL_INIT].needAuth = false;
-    remoteProcs[REMOTE_PROC_AUTH_SASL_STEP].needAuth = false;
-    remoteProcs[REMOTE_PROC_AUTH_SASL_START].needAuth = false;
-    remoteProcs[REMOTE_PROC_AUTH_POLKIT].needAuth = false;
-    if (!(remoteProgram = virNetServerProgramNew(REMOTE_PROGRAM,
-                                                 REMOTE_PROTOCOL_VERSION,
-                                                 remoteProcs,
-                                                 remoteNProcs))) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-    if (virNetServerAddProgram(srv, remoteProgram) < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (!(lxcProgram = virNetServerProgramNew(LXC_PROGRAM,
-                                              LXC_PROTOCOL_VERSION,
-                                              lxcProcs,
-                                              lxcNProcs))) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-    if (virNetServerAddProgram(srv, lxcProgram) < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (!(qemuProgram = virNetServerProgramNew(QEMU_PROGRAM,
-                                               QEMU_PROTOCOL_VERSION,
-                                               qemuProcs,
-                                               qemuNProcs))) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-    if (virNetServerAddProgram(srv, qemuProgram) < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (!(srvAdm = virNetServerNew("admin", 1,
-                                   config->admin_min_workers,
-                                   config->admin_max_workers,
-                                   0,
-                                   config->admin_max_clients,
-                                   0,
-                                   config->admin_keepalive_interval,
-                                   config->admin_keepalive_count,
-                                   NULL,
-                                   remoteAdmClientNew,
-                                   NULL,
-                                   remoteAdmClientFree,
-                                   dmn))) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (virNetDaemonAddServer(dmn, srvAdm) < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (!(adminProgram = virNetServerProgramNew(ADMIN_PROGRAM,
-                                                ADMIN_PROTOCOL_VERSION,
-                                                adminProcs,
-                                                adminNProcs))) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-    if (virNetServerAddProgram(srvAdm, adminProgram) < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-    if (timeout != -1) {
-        VIR_DEBUG("Registering shutdown timeout %d", timeout);
-        virNetDaemonAutoShutdown(dmn, timeout);
-    }
-
-    if ((daemonSetupSignals(dmn)) < 0) {
-        ret = VIR_DAEMON_ERR_SIGNAL;
-        goto cleanup;
-    }
-
-    if (config->audit_level) {
-        VIR_DEBUG("Attempting to configure auditing subsystem");
-        if (virAuditOpen(config->audit_level) < 0) {
-            if (config->audit_level > 1) {
-                ret = VIR_DAEMON_ERR_AUDIT;
-                goto cleanup;
-            }
-            VIR_DEBUG("Proceeding without auditing");
-        }
-    }
-    virAuditLog(config->audit_logging > 0);
-
-    /* setup the hooks if any */
-    if (virHookInitialize() < 0) {
-        ret = VIR_DAEMON_ERR_HOOKS;
-        goto cleanup;
-    }
-
-    /* Disable error func, now logging is setup */
-    virSetErrorFunc(NULL, daemonErrorHandler);
-    virSetErrorLogPriorityFunc(daemonErrorLogFilter);
-
-    /*
-     * Call the daemon startup hook
-     * TODO: should we abort the daemon startup if the script returned
-     *       an error ?
-     */
-    virHookCall(VIR_HOOK_DRIVER_DAEMON, "-", VIR_HOOK_DAEMON_OP_START,
-                0, "start", NULL, NULL);
-
-    if (daemonSetupNetworking(srv, srvAdm,
-                              config,
-                              sock_file,
-                              sock_file_ro,
-                              sock_file_adm,
-                              ipsock, privileged) < 0) {
-        ret = VIR_DAEMON_ERR_NETWORK;
-        goto cleanup;
-    }
-
-    /* Tell parent of daemon that basic initialization is complete
-     * In particular we're ready to accept net connections & have
-     * written the pidfile
-     */
-    if (statuswrite != -1) {
-        char status = 0;
-        while (write(statuswrite, &status, 1) == -1 &&
-               errno == EINTR)
-            ;
-        VIR_FORCE_CLOSE(statuswrite);
-    }
-
-    /* Initialize drivers & then start accepting new clients from network */
-    if (daemonStateInit(dmn) < 0) {
-        ret = VIR_DAEMON_ERR_INIT;
-        goto cleanup;
-    }
-
-#if defined(__linux__) && defined(NETLINK_ROUTE)
-    /* Register the netlink event service for NETLINK_ROUTE */
-    if (virNetlinkEventServiceStart(NETLINK_ROUTE, 0) < 0) {
-        ret = VIR_DAEMON_ERR_NETWORK;
-        goto cleanup;
-    }
-#endif
-
-#if defined(__linux__) && defined(NETLINK_KOBJECT_UEVENT)
-    /* Register the netlink event service for NETLINK_KOBJECT_UEVENT */
-    if (virNetlinkEventServiceStart(NETLINK_KOBJECT_UEVENT, 1) < 0) {
-        ret = VIR_DAEMON_ERR_NETWORK;
-        goto cleanup;
-    }
-#endif
-
-    /* Run event loop. */
-    virNetDaemonRun(dmn);
-
-    ret = 0;
-
-    virHookCall(VIR_HOOK_DRIVER_DAEMON, "-", VIR_HOOK_DAEMON_OP_SHUTDOWN,
-                0, "shutdown", NULL, NULL);
-
- cleanup:
-    /* Keep cleanup order in inverse order of startup */
-    virNetDaemonClose(dmn);
-
-    virNetlinkEventServiceStopAll();
-
-    if (driversInitialized) {
-        /* NB: Possible issue with timing window between driversInitialized
-         * setting if virNetlinkEventServerStart fails */
-        driversInitialized = false;
-        virStateCleanup();
-    }
-
-    virObjectUnref(adminProgram);
-    virObjectUnref(srvAdm);
-    virObjectUnref(qemuProgram);
-    virObjectUnref(lxcProgram);
-    virObjectUnref(remoteProgram);
-    virObjectUnref(srv);
-    virObjectUnref(dmn);
-
-    virNetlinkShutdown();
-
-    if (pid_file_fd != -1)
-        virPidFileReleasePath(pid_file, pid_file_fd);
-
-    VIR_FREE(run_dir);
-
-    if (statuswrite != -1) {
-        if (ret != 0) {
-            /* Tell parent of daemon what failed */
-            char status = ret;
-            while (write(statuswrite, &status, 1) == -1 &&
-                   errno == EINTR)
-                ;
-        }
-        VIR_FORCE_CLOSE(statuswrite);
-    }
-
-    VIR_FREE(sock_file);
-    VIR_FREE(sock_file_ro);
-    VIR_FREE(sock_file_adm);
-
-    VIR_FREE(pid_file);
-
-    VIR_FREE(remote_config_file);
-    daemonConfigFree(config);
-
-    return ret;
-}
diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
deleted file mode 100644 (file)
index 082c4bc..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * libvirtd.h: daemon data structure definitions
- *
- * Copyright (C) 2006-2015 Red Hat, Inc.
- * Copyright (C) 2006 Daniel P. Berrange
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-
-#ifndef LIBVIRTD_H__
-# define LIBVIRTD_H__
-
-# define VIR_ENUM_SENTINELS
-
-# include <rpc/types.h>
-# include <rpc/xdr.h>
-# include "remote_protocol.h"
-# include "lxc_protocol.h"
-# include "qemu_protocol.h"
-# include "virthread.h"
-
-# if WITH_SASL
-#  include "virnetsaslcontext.h"
-# endif
-# include "virnetserverprogram.h"
-
-typedef struct daemonClientStream daemonClientStream;
-typedef daemonClientStream *daemonClientStreamPtr;
-typedef struct daemonClientPrivate daemonClientPrivate;
-typedef daemonClientPrivate *daemonClientPrivatePtr;
-typedef struct daemonClientEventCallback daemonClientEventCallback;
-typedef daemonClientEventCallback *daemonClientEventCallbackPtr;
-
-/* Stores the per-client connection state */
-struct daemonClientPrivate {
-    /* Hold while accessing any data except conn */
-    virMutex lock;
-
-    daemonClientEventCallbackPtr *domainEventCallbacks;
-    size_t ndomainEventCallbacks;
-    daemonClientEventCallbackPtr *networkEventCallbacks;
-    size_t nnetworkEventCallbacks;
-    daemonClientEventCallbackPtr *qemuEventCallbacks;
-    size_t nqemuEventCallbacks;
-    daemonClientEventCallbackPtr *storageEventCallbacks;
-    size_t nstorageEventCallbacks;
-    daemonClientEventCallbackPtr *nodeDeviceEventCallbacks;
-    size_t nnodeDeviceEventCallbacks;
-    daemonClientEventCallbackPtr *secretEventCallbacks;
-    size_t nsecretEventCallbacks;
-    bool closeRegistered;
-
-# if WITH_SASL
-    virNetSASLSessionPtr sasl;
-# endif
-
-    /* This is only valid if a remote open call has been made on this
-     * connection, otherwise it will be NULL.  Also if remote close is
-     * called, it will be set back to NULL if that succeeds.
-     */
-    virConnectPtr conn;
-
-    daemonClientStreamPtr streams;
-};
-
-
-# if WITH_SASL
-extern virNetSASLContextPtr saslCtxt;
-# endif
-extern virNetServerProgramPtr remoteProgram;
-extern virNetServerProgramPtr qemuProgram;
-
-#endif
diff --git a/daemon/remote.c b/daemon/remote.c
deleted file mode 100644 (file)
index 6de4bd0..0000000
+++ /dev/null
@@ -1,7074 +0,0 @@
-/*
- * remote.c: handlers for RPC method calls
- *
- * Copyright (C) 2007-2015 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, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Richard W.M. Jones <rjones@redhat.com>
- */
-
-#include <config.h>
-
-#include "virerror.h"
-
-#include "remote.h"
-#include "libvirtd.h"
-#include "libvirt_internal.h"
-#include "datatypes.h"
-#include "viralloc.h"
-#include "virlog.h"
-#include "stream.h"
-#include "viruuid.h"
-#include "vircommand.h"
-#include "intprops.h"
-#include "virnetserverservice.h"
-#include "virnetserver.h"
-#include "virfile.h"
-#include "virtypedparam.h"
-#include "virdbus.h"
-#include "virprocess.h"
-#include "remote_protocol.h"
-#include "qemu_protocol.h"
-#include "lxc_protocol.h"
-#include "virstring.h"
-#include "object_event.h"
-#include "domain_conf.h"
-#include "network_conf.h"
-#include "virprobe.h"
-#include "viraccessapicheck.h"
-#include "viraccessapicheckqemu.h"
-#include "virpolkit.h"
-#include "virthreadjob.h"
-
-#define VIR_FROM_THIS VIR_FROM_RPC
-
-VIR_LOG_INIT("daemon.remote");
-
-#if SIZEOF_LONG < 8
-# define HYPER_TO_TYPE(_type, _to, _from) \
-    do { \
-        if ((_from) != (_type)(_from)) { \
-            virReportError(VIR_ERR_OVERFLOW, \
-                           _("conversion from hyper to %s overflowed"), \
-                           #_type); \
-            goto cleanup; \
-        } \
-        (_to) = (_from); \
-    } while (0)
-
-# define HYPER_TO_LONG(_to, _from) HYPER_TO_TYPE(long, _to, _from)
-# define HYPER_TO_ULONG(_to, _from) HYPER_TO_TYPE(unsigned long, _to, _from)
-#else
-# define HYPER_TO_LONG(_to, _from) (_to) = (_from)
-# define HYPER_TO_ULONG(_to, _from) (_to) = (_from)
-#endif
-
-struct daemonClientEventCallback {
-    virNetServerClientPtr client;
-    int eventID;
-    int callbackID;
-    bool legacy;
-};
-
-static virDomainPtr get_nonnull_domain(virConnectPtr conn, remote_nonnull_domain domain);
-static virNetworkPtr get_nonnull_network(virConnectPtr conn, remote_nonnull_network network);
-static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface);
-static virStoragePoolPtr get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool);
-static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol);
-static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret);
-static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter);
-static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot);
-static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev);
-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);
-static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src);
-static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src);
-static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src);
-static void make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src);
-static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src);
-static void make_nonnull_nwfilter(remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src);
-static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src);
-
-static int
-remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors,
-                                int nerrors,
-                                remote_domain_disk_error **ret_errors_val,
-                                u_int *ret_errors_len);
-
-#include "remote_dispatch.h"
-#include "qemu_dispatch.h"
-#include "lxc_dispatch.h"
-
-
-/* Prototypes */
-static void
-remoteDispatchObjectEventSend(virNetServerClientPtr client,
-                              virNetServerProgramPtr program,
-                              int procnr,
-                              xdrproc_t proc,
-                              void *data);
-
-static void
-remoteEventCallbackFree(void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    if (!callback)
-        return;
-    virObjectUnref(callback->client);
-    VIR_FREE(callback);
-}
-
-
-static bool
-remoteRelayDomainEventCheckACL(virNetServerClientPtr client,
-                               virConnectPtr conn, virDomainPtr dom)
-{
-    virDomainDef def;
-    virIdentityPtr identity = NULL;
-    bool ret = false;
-
-    /* For now, we just create a virDomainDef with enough contents to
-     * satisfy what viraccessdriverpolkit.c references.  This is a bit
-     * fragile, but I don't know of anything better.  */
-    memset(&def, 0, sizeof(def));
-    def.name = dom->name;
-    memcpy(def.uuid, dom->uuid, VIR_UUID_BUFLEN);
-
-    if (!(identity = virNetServerClientGetIdentity(client)))
-        goto cleanup;
-    if (virIdentitySetCurrent(identity) < 0)
-        goto cleanup;
-    ret = virConnectDomainEventRegisterAnyCheckACL(conn, &def);
-
- cleanup:
-    ignore_value(virIdentitySetCurrent(NULL));
-    virObjectUnref(identity);
-    return ret;
-}
-
-
-static bool
-remoteRelayNetworkEventCheckACL(virNetServerClientPtr client,
-                                virConnectPtr conn, virNetworkPtr net)
-{
-    virNetworkDef def;
-    virIdentityPtr identity = NULL;
-    bool ret = false;
-
-    /* For now, we just create a virNetworkDef with enough contents to
-     * satisfy what viraccessdriverpolkit.c references.  This is a bit
-     * fragile, but I don't know of anything better.  */
-    def.name = net->name;
-    memcpy(def.uuid, net->uuid, VIR_UUID_BUFLEN);
-
-    if (!(identity = virNetServerClientGetIdentity(client)))
-        goto cleanup;
-    if (virIdentitySetCurrent(identity) < 0)
-        goto cleanup;
-    ret = virConnectNetworkEventRegisterAnyCheckACL(conn, &def);
-
- cleanup:
-    ignore_value(virIdentitySetCurrent(NULL));
-    virObjectUnref(identity);
-    return ret;
-}
-
-static bool
-remoteRelayStoragePoolEventCheckACL(virNetServerClientPtr client,
-                                    virConnectPtr conn,
-                                    virStoragePoolPtr pool)
-{
-    virStoragePoolDef def;
-    virIdentityPtr identity = NULL;
-    bool ret = false;
-
-    /* For now, we just create a virStoragePoolDef with enough contents to
-     * satisfy what viraccessdriverpolkit.c references.  This is a bit
-     * fragile, but I don't know of anything better.  */
-    def.name = pool->name;
-    memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN);
-
-    if (!(identity = virNetServerClientGetIdentity(client)))
-        goto cleanup;
-    if (virIdentitySetCurrent(identity) < 0)
-        goto cleanup;
-    ret = virConnectStoragePoolEventRegisterAnyCheckACL(conn, &def);
-
- cleanup:
-    ignore_value(virIdentitySetCurrent(NULL));
-    virObjectUnref(identity);
-    return ret;
-}
-
-static bool
-remoteRelayNodeDeviceEventCheckACL(virNetServerClientPtr client,
-                                   virConnectPtr conn,
-                                   virNodeDevicePtr dev)
-{
-    virNodeDeviceDef def;
-    virIdentityPtr identity = NULL;
-    bool ret = false;
-
-    /* For now, we just create a virNodeDeviceDef with enough contents to
-     * satisfy what viraccessdriverpolkit.c references.  This is a bit
-     * fragile, but I don't know of anything better.  */
-    def.name = dev->name;
-
-    if (!(identity = virNetServerClientGetIdentity(client)))
-        goto cleanup;
-    if (virIdentitySetCurrent(identity) < 0)
-        goto cleanup;
-    ret = virConnectNodeDeviceEventRegisterAnyCheckACL(conn, &def);
-
- cleanup:
-    ignore_value(virIdentitySetCurrent(NULL));
-    virObjectUnref(identity);
-    return ret;
-}
-
-static bool
-remoteRelaySecretEventCheckACL(virNetServerClientPtr client,
-                               virConnectPtr conn,
-                               virSecretPtr secret)
-{
-    virSecretDef def;
-    virIdentityPtr identity = NULL;
-    bool ret = false;
-
-    /* For now, we just create a virSecretDef with enough contents to
-     * satisfy what viraccessdriverpolkit.c references.  This is a bit
-     * fragile, but I don't know of anything better.  */
-    memcpy(def.uuid, secret->uuid, VIR_UUID_BUFLEN);
-    def.usage_type = secret->usageType;
-    def.usage_id = secret->usageID;
-
-    if (!(identity = virNetServerClientGetIdentity(client)))
-        goto cleanup;
-    if (virIdentitySetCurrent(identity) < 0)
-        goto cleanup;
-    ret = virConnectSecretEventRegisterAnyCheckACL(conn, &def);
-
- cleanup:
-    ignore_value(virIdentitySetCurrent(NULL));
-    virObjectUnref(identity);
-    return ret;
-}
-
-static bool
-remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client,
-                                          virConnectPtr conn, virDomainPtr dom)
-{
-    virDomainDef def;
-    virIdentityPtr identity = NULL;
-    bool ret = false;
-
-    /* For now, we just create a virDomainDef with enough contents to
-     * satisfy what viraccessdriverpolkit.c references.  This is a bit
-     * fragile, but I don't know of anything better.  */
-    def.name = dom->name;
-    memcpy(def.uuid, dom->uuid, VIR_UUID_BUFLEN);
-
-    if (!(identity = virNetServerClientGetIdentity(client)))
-        goto cleanup;
-    if (virIdentitySetCurrent(identity) < 0)
-        goto cleanup;
-    ret = virConnectDomainQemuMonitorEventRegisterCheckACL(conn, &def);
-
- cleanup:
-    ignore_value(virIdentitySetCurrent(NULL));
-    virObjectUnref(identity);
-    return ret;
-}
-
-
-static int
-remoteRelayDomainEventLifecycle(virConnectPtr conn,
-                                virDomainPtr dom,
-                                int event,
-                                int detail,
-                                void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_lifecycle_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain lifecycle event %d %d, callback %d legacy %d",
-              event, detail, callback->callbackID, callback->legacy);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-    data.event = event;
-    data.detail = detail;
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE,
-                                      (xdrproc_t)xdr_remote_domain_event_lifecycle_msg,
-                                      &data);
-    } else {
-        remote_domain_event_callback_lifecycle_msg msg = { callback->callbackID,
-                                                           data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_LIFECYCLE,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_lifecycle_msg,
-                                      &msg);
-    }
-
-    return 0;
-}
-
-static int
-remoteRelayDomainEventReboot(virConnectPtr conn,
-                             virDomainPtr dom,
-                             void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_reboot_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain reboot event %s %d, callback %d legacy %d",
-              dom->name, dom->id, callback->callbackID, callback->legacy);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_REBOOT,
-                                      (xdrproc_t)xdr_remote_domain_event_reboot_msg, &data);
-    } else {
-        remote_domain_event_callback_reboot_msg msg = { callback->callbackID,
-                                                        data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_REBOOT,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_reboot_msg, &msg);
-    }
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventRTCChange(virConnectPtr conn,
-                                virDomainPtr dom,
-                                long long offset,
-                                void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_rtc_change_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain rtc change event %s %d %lld, callback %d legacy %d",
-              dom->name, dom->id, offset,
-              callback->callbackID, callback->legacy);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-    data.offset = offset;
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_rtc_change_msg, &data);
-    } else {
-        remote_domain_event_callback_rtc_change_msg msg = { callback->callbackID,
-                                                            data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_RTC_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_rtc_change_msg, &msg);
-    }
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventWatchdog(virConnectPtr conn,
-                               virDomainPtr dom,
-                               int action,
-                               void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_watchdog_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain watchdog event %s %d %d, callback %d",
-              dom->name, dom->id, action, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-    data.action = action;
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_WATCHDOG,
-                                      (xdrproc_t)xdr_remote_domain_event_watchdog_msg, &data);
-    } else {
-        remote_domain_event_callback_watchdog_msg msg = { callback->callbackID,
-                                                          data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_WATCHDOG,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_watchdog_msg, &msg);
-    }
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventIOError(virConnectPtr conn,
-                              virDomainPtr dom,
-                              const char *srcPath,
-                              const char *devAlias,
-                              int action,
-                              void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_io_error_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain io error %s %d %s %s %d, callback %d",
-              dom->name, dom->id, srcPath, devAlias, action,
-              callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    if (VIR_STRDUP(data.srcPath, srcPath) < 0 ||
-        VIR_STRDUP(data.devAlias, devAlias) < 0)
-        goto error;
-    make_nonnull_domain(&data.dom, dom);
-    data.action = action;
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_IO_ERROR,
-                                      (xdrproc_t)xdr_remote_domain_event_io_error_msg, &data);
-    } else {
-        remote_domain_event_callback_io_error_msg msg = { callback->callbackID,
-                                                          data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_IO_ERROR,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_io_error_msg, &msg);
-    }
-
-    return 0;
- error:
-    VIR_FREE(data.srcPath);
-    VIR_FREE(data.devAlias);
-    return -1;
-}
-
-
-static int
-remoteRelayDomainEventIOErrorReason(virConnectPtr conn,
-                                    virDomainPtr dom,
-                                    const char *srcPath,
-                                    const char *devAlias,
-                                    int action,
-                                    const char *reason,
-                                    void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_io_error_reason_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain io error %s %d %s %s %d %s, callback %d",
-              dom->name, dom->id, srcPath, devAlias, action, reason,
-              callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    if (VIR_STRDUP(data.srcPath, srcPath) < 0 ||
-        VIR_STRDUP(data.devAlias, devAlias) < 0 ||
-        VIR_STRDUP(data.reason, reason) < 0)
-        goto error;
-    data.action = action;
-
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON,
-                                      (xdrproc_t)xdr_remote_domain_event_io_error_reason_msg, &data);
-    } else {
-        remote_domain_event_callback_io_error_reason_msg msg = { callback->callbackID,
-                                                                 data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_IO_ERROR_REASON,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_io_error_reason_msg, &msg);
-    }
-
-    return 0;
-
- error:
-    VIR_FREE(data.srcPath);
-    VIR_FREE(data.devAlias);
-    VIR_FREE(data.reason);
-    return -1;
-}
-
-
-static int
-remoteRelayDomainEventGraphics(virConnectPtr conn,
-                               virDomainPtr dom,
-                               int phase,
-                               virDomainEventGraphicsAddressPtr local,
-                               virDomainEventGraphicsAddressPtr remote,
-                               const char *authScheme,
-                               virDomainEventGraphicsSubjectPtr subject,
-                               void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_graphics_msg data;
-    size_t i;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain graphics event %s %d %d - %d %s %s  - %d %s %s - %s, callback %d",
-              dom->name, dom->id, phase,
-              local->family, local->service, local->node,
-              remote->family, remote->service, remote->node,
-              authScheme, callback->callbackID);
-
-    VIR_DEBUG("Subject %d", subject->nidentity);
-    for (i = 0; i < subject->nidentity; i++)
-        VIR_DEBUG("  %s=%s", subject->identities[i].type, subject->identities[i].name);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.phase = phase;
-    data.local.family = local->family;
-    data.remote.family = remote->family;
-    if (VIR_STRDUP(data.authScheme, authScheme) < 0 ||
-        VIR_STRDUP(data.local.node, local->node) < 0 ||
-        VIR_STRDUP(data.local.service, local->service) < 0 ||
-        VIR_STRDUP(data.remote.node, remote->node) < 0 ||
-        VIR_STRDUP(data.remote.service, remote->service) < 0)
-        goto error;
-
-    data.subject.subject_len = subject->nidentity;
-    if (VIR_ALLOC_N(data.subject.subject_val, data.subject.subject_len) < 0)
-        goto error;
-
-    for (i = 0; i < data.subject.subject_len; i++) {
-        if (VIR_STRDUP(data.subject.subject_val[i].type, subject->identities[i].type) < 0 ||
-            VIR_STRDUP(data.subject.subject_val[i].name, subject->identities[i].name) < 0)
-            goto error;
-    }
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_GRAPHICS,
-                                      (xdrproc_t)xdr_remote_domain_event_graphics_msg, &data);
-    } else {
-        remote_domain_event_callback_graphics_msg msg = { callback->callbackID,
-                                                          data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_GRAPHICS,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_graphics_msg, &msg);
-    }
-
-    return 0;
-
- error:
-    VIR_FREE(data.authScheme);
-    VIR_FREE(data.local.node);
-    VIR_FREE(data.local.service);
-    VIR_FREE(data.remote.node);
-    VIR_FREE(data.remote.service);
-    if (data.subject.subject_val != NULL) {
-        for (i = 0; i < data.subject.subject_len; i++) {
-            VIR_FREE(data.subject.subject_val[i].type);
-            VIR_FREE(data.subject.subject_val[i].name);
-        }
-        VIR_FREE(data.subject.subject_val);
-    }
-    return -1;
-}
-
-static int
-remoteRelayDomainEventBlockJob(virConnectPtr conn,
-                               virDomainPtr dom,
-                               const char *path,
-                               int type,
-                               int status,
-                               void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_block_job_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain block job event %s %d %s %i, %i, callback %d",
-              dom->name, dom->id, path, type, status, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    if (VIR_STRDUP(data.path, path) < 0)
-        return -1;
-    data.type = type;
-    data.status = status;
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB,
-                                      (xdrproc_t)xdr_remote_domain_event_block_job_msg, &data);
-    } else {
-        remote_domain_event_callback_block_job_msg msg = { callback->callbackID,
-                                                           data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_BLOCK_JOB,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_block_job_msg, &msg);
-    }
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventControlError(virConnectPtr conn,
-                                   virDomainPtr dom,
-                                   void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_control_error_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain control error %s %d, callback %d",
-              dom->name, dom->id, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR,
-                                      (xdrproc_t)xdr_remote_domain_event_control_error_msg, &data);
-    } else {
-        remote_domain_event_callback_control_error_msg msg = { callback->callbackID,
-                                                               data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CONTROL_ERROR,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_control_error_msg, &msg);
-    }
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventDiskChange(virConnectPtr conn,
-                                 virDomainPtr dom,
-                                 const char *oldSrcPath,
-                                 const char *newSrcPath,
-                                 const char *devAlias,
-                                 int reason,
-                                 void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_disk_change_msg data;
-    char **oldSrcPath_p = NULL, **newSrcPath_p = NULL;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain %s %d disk change %s %s %s %d, callback %d",
-              dom->name, dom->id, oldSrcPath, newSrcPath, devAlias, reason,
-              callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    if (oldSrcPath &&
-        ((VIR_ALLOC(oldSrcPath_p) < 0) ||
-         VIR_STRDUP(*oldSrcPath_p, oldSrcPath) < 0))
-        goto error;
-
-    if (newSrcPath &&
-        ((VIR_ALLOC(newSrcPath_p) < 0) ||
-         VIR_STRDUP(*newSrcPath_p, newSrcPath) < 0))
-        goto error;
-
-    data.oldSrcPath = oldSrcPath_p;
-    data.newSrcPath = newSrcPath_p;
-    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
-        goto error;
-    data.reason = reason;
-
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_DISK_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_disk_change_msg, &data);
-    } else {
-        remote_domain_event_callback_disk_change_msg msg = { callback->callbackID,
-                                                             data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DISK_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_disk_change_msg, &msg);
-    }
-
-    return 0;
-
- error:
-    VIR_FREE(oldSrcPath_p);
-    VIR_FREE(newSrcPath_p);
-    return -1;
-}
-
-
-static int
-remoteRelayDomainEventTrayChange(virConnectPtr conn,
-                                 virDomainPtr dom,
-                                 const char *devAlias,
-                                 int reason,
-                                 void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_tray_change_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain %s %d tray change devAlias: %s reason: %d, callback %d",
-              dom->name, dom->id, devAlias, reason, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-
-    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
-        return -1;
-    data.reason = reason;
-
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_TRAY_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_tray_change_msg, &data);
-    } else {
-        remote_domain_event_callback_tray_change_msg msg = { callback->callbackID,
-                                                             data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_TRAY_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_tray_change_msg, &msg);
-    }
-
-    return 0;
-}
-
-static int
-remoteRelayDomainEventPMWakeup(virConnectPtr conn,
-                               virDomainPtr dom,
-                               int reason,
-                               void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_pmwakeup_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain %s %d system pmwakeup, callback %d",
-              dom->name, dom->id, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_PMWAKEUP,
-                                      (xdrproc_t)xdr_remote_domain_event_pmwakeup_msg, &data);
-    } else {
-        remote_domain_event_callback_pmwakeup_msg msg = { callback->callbackID,
-                                                          reason, data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_PMWAKEUP,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_pmwakeup_msg, &msg);
-    }
-
-    return 0;
-}
-
-static int
-remoteRelayDomainEventPMSuspend(virConnectPtr conn,
-                                virDomainPtr dom,
-                                int reason,
-                                void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_pmsuspend_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain %s %d system pmsuspend, callback %d",
-              dom->name, dom->id, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND,
-                                      (xdrproc_t)xdr_remote_domain_event_pmsuspend_msg, &data);
-    } else {
-        remote_domain_event_callback_pmsuspend_msg msg = { callback->callbackID,
-                                                           reason, data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_PMSUSPEND,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_pmsuspend_msg, &msg);
-    }
-
-    return 0;
-}
-
-static int
-remoteRelayDomainEventBalloonChange(virConnectPtr conn,
-                                    virDomainPtr dom,
-                                    unsigned long long actual,
-                                    void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_balloon_change_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain balloon change event %s %d %lld, callback %d",
-              dom->name, dom->id, actual, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-    data.actual = actual;
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_balloon_change_msg, &data);
-    } else {
-        remote_domain_event_callback_balloon_change_msg msg = { callback->callbackID,
-                                                                data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_BALLOON_CHANGE,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_balloon_change_msg, &msg);
-    }
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventPMSuspendDisk(virConnectPtr conn,
-                                    virDomainPtr dom,
-                                    int reason,
-                                    void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_pmsuspend_disk_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain %s %d system pmsuspend-disk, callback %d",
-              dom->name, dom->id, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND_DISK,
-                                      (xdrproc_t)xdr_remote_domain_event_pmsuspend_disk_msg, &data);
-    } else {
-        remote_domain_event_callback_pmsuspend_disk_msg msg = { callback->callbackID,
-                                                                reason, data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_PMSUSPEND_DISK,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_pmsuspend_disk_msg, &msg);
-    }
-
-    return 0;
-}
-
-static int
-remoteRelayDomainEventDeviceRemoved(virConnectPtr conn,
-                                    virDomainPtr dom,
-                                    const char *devAlias,
-                                    void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_device_removed_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain device removed event %s %d %s, callback %d",
-              dom->name, dom->id, devAlias, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-
-    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
-        return -1;
-
-    make_nonnull_domain(&data.dom, dom);
-
-    if (callback->legacy) {
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_DEVICE_REMOVED,
-                                      (xdrproc_t)xdr_remote_domain_event_device_removed_msg,
-                                      &data);
-    } else {
-        remote_domain_event_callback_device_removed_msg msg = { callback->callbackID,
-                                                                data };
-
-        remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVED,
-                                      (xdrproc_t)xdr_remote_domain_event_callback_device_removed_msg,
-                                      &msg);
-    }
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventBlockJob2(virConnectPtr conn,
-                                virDomainPtr dom,
-                                const char *dst,
-                                int type,
-                                int status,
-                                void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_block_job_2_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain block job 2 event %s %d %s %i, %i, callback %d",
-              dom->name, dom->id, dst, type, status, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.callbackID = callback->callbackID;
-    if (VIR_STRDUP(data.dst, dst) < 0)
-        return -1;
-    data.type = type;
-    data.status = status;
-    make_nonnull_domain(&data.dom, dom);
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_2,
-                                  (xdrproc_t)xdr_remote_domain_event_block_job_2_msg, &data);
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventTunable(virConnectPtr conn,
-                              virDomainPtr dom,
-                              virTypedParameterPtr params,
-                              int nparams,
-                              void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_callback_tunable_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain tunable event %s %d, callback %d, params %p %d",
-              dom->name, dom->id, callback->callbackID, params, nparams);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.callbackID = callback->callbackID;
-    make_nonnull_domain(&data.dom, dom);
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &data.params.params_val,
-                                &data.params.params_len,
-                                VIR_TYPED_PARAM_STRING_OKAY) < 0) {
-        VIR_FREE(data.dom.name);
-        return -1;
-    }
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_TUNABLE,
-                                  (xdrproc_t)xdr_remote_domain_event_callback_tunable_msg,
-                                  &data);
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventAgentLifecycle(virConnectPtr conn,
-                                     virDomainPtr dom,
-                                     int state,
-                                     int reason,
-                                     void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_callback_agent_lifecycle_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain agent lifecycle event %s %d, callback %d, "
-              " state %d, reason %d",
-              dom->name, dom->id, callback->callbackID, state, reason);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.callbackID = callback->callbackID;
-    make_nonnull_domain(&data.dom, dom);
-
-    data.state = state;
-    data.reason = reason;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE,
-                                  (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_msg,
-                                  &data);
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventDeviceAdded(virConnectPtr conn,
-                                  virDomainPtr dom,
-                                  const char *devAlias,
-                                  void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_callback_device_added_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain device added event %s %d %s, callback %d",
-              dom->name, dom->id, devAlias, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-
-    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
-        return -1;
-
-    make_nonnull_domain(&data.dom, dom);
-    data.callbackID = callback->callbackID;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_ADDED,
-                                  (xdrproc_t)xdr_remote_domain_event_callback_device_added_msg,
-                                  &data);
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventMigrationIteration(virConnectPtr conn,
-                                         virDomainPtr dom,
-                                         int iteration,
-                                         void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_callback_migration_iteration_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain migration pass event %s %d, "
-              "callback %d, iteration %d",
-              dom->name, dom->id, callback->callbackID, iteration);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.callbackID = callback->callbackID;
-    make_nonnull_domain(&data.dom, dom);
-
-    data.iteration = iteration;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_MIGRATION_ITERATION,
-                                  (xdrproc_t)xdr_remote_domain_event_callback_migration_iteration_msg,
-                                  &data);
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventJobCompleted(virConnectPtr conn,
-                                   virDomainPtr dom,
-                                   virTypedParameterPtr params,
-                                   int nparams,
-                                   void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_callback_job_completed_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain migration completed event %s %d, "
-              "callback %d, params %p %d",
-              dom->name, dom->id, callback->callbackID, params, nparams);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.callbackID = callback->callbackID;
-    make_nonnull_domain(&data.dom, dom);
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &data.params.params_val,
-                                &data.params.params_len,
-                                VIR_TYPED_PARAM_STRING_OKAY) < 0) {
-        VIR_FREE(data.dom.name);
-        return -1;
-    }
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_JOB_COMPLETED,
-                                  (xdrproc_t)xdr_remote_domain_event_callback_job_completed_msg,
-                                  &data);
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventDeviceRemovalFailed(virConnectPtr conn,
-                                          virDomainPtr dom,
-                                          const char *devAlias,
-                                          void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_callback_device_removal_failed_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain device removal failed event %s %d %s, callback %d",
-              dom->name, dom->id, devAlias, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-
-    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
-        return -1;
-
-    make_nonnull_domain(&data.dom, dom);
-    data.callbackID = callback->callbackID;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED,
-                                  (xdrproc_t)xdr_remote_domain_event_callback_device_removal_failed_msg,
-                                  &data);
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventMetadataChange(virConnectPtr conn,
-                                     virDomainPtr dom,
-                                     int type,
-                                     const char *nsuri,
-                                     void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_callback_metadata_change_msg data;
-    char **nsurip;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain metadata change %s %d %d %s, callback %d",
-              dom->name, dom->id, type, NULLSTR(nsuri), callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-
-    data.type = type;
-    if (nsuri) {
-        if (VIR_ALLOC(nsurip) < 0)
-            return -1;
-        if (VIR_STRDUP(*nsurip, nsuri) < 0) {
-            VIR_FREE(nsurip);
-            return -1;
-        }
-        data.nsuri = nsurip;
-    }
-
-    make_nonnull_domain(&data.dom, dom);
-    data.callbackID = callback->callbackID;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE,
-                                  (xdrproc_t)xdr_remote_domain_event_callback_metadata_change_msg,
-                                  &data);
-
-    return 0;
-}
-
-
-static int
-remoteRelayDomainEventBlockThreshold(virConnectPtr conn,
-                                     virDomainPtr dom,
-                                     const char *dev,
-                                     const char *path,
-                                     unsigned long long threshold,
-                                     unsigned long long excess,
-                                     void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_domain_event_block_threshold_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
-        return -1;
-
-    VIR_DEBUG("Relaying domain block threshold event %s %d %s %s %llu %llu, callback %d",
-              dom->name, dom->id, dev, NULLSTR(path), threshold, excess, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.callbackID = callback->callbackID;
-    if (VIR_STRDUP(data.dev, dev) < 0)
-        goto error;
-    if (path) {
-        if (VIR_ALLOC(data.path) < 0)
-            goto error;
-        if (VIR_STRDUP(*(data.path), path) < 0)
-            goto error;
-    }
-    data.threshold = threshold;
-    data.excess = excess;
-    make_nonnull_domain(&data.dom, dom);
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD,
-                                  (xdrproc_t)xdr_remote_domain_event_block_threshold_msg, &data);
-
-    return 0;
- error:
-    VIR_FREE(data.dev);
-    return -1;
-}
-
-
-static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventWatchdog),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskChange),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTrayChange),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMWakeup),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspend),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBalloonChange),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspendDisk),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemoved),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob2),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTunable),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventAgentLifecycle),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceAdded),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMigrationIteration),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange),
-    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold),
-};
-
-verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
-
-static int
-remoteRelayNetworkEventLifecycle(virConnectPtr conn,
-                                 virNetworkPtr net,
-                                 int event,
-                                 int detail,
-                                 void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_network_event_lifecycle_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayNetworkEventCheckACL(callback->client, conn, net))
-        return -1;
-
-    VIR_DEBUG("Relaying network lifecycle event %d, detail %d, callback %d",
-              event, detail, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_network(&data.net, net);
-    data.callbackID = callback->callbackID;
-    data.event = event;
-    data.detail = detail;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_NETWORK_EVENT_LIFECYCLE,
-                                  (xdrproc_t)xdr_remote_network_event_lifecycle_msg, &data);
-
-    return 0;
-}
-
-static virConnectNetworkEventGenericCallback networkEventCallbacks[] = {
-    VIR_NETWORK_EVENT_CALLBACK(remoteRelayNetworkEventLifecycle),
-};
-
-verify(ARRAY_CARDINALITY(networkEventCallbacks) == VIR_NETWORK_EVENT_ID_LAST);
-
-static int
-remoteRelayStoragePoolEventLifecycle(virConnectPtr conn,
-                                     virStoragePoolPtr pool,
-                                     int event,
-                                     int detail,
-                                     void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_storage_pool_event_lifecycle_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayStoragePoolEventCheckACL(callback->client, conn, pool))
-        return -1;
-
-    VIR_DEBUG("Relaying storage pool lifecycle event %d, detail %d, callback %d",
-              event, detail, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_storage_pool(&data.pool, pool);
-    data.callbackID = callback->callbackID;
-    data.event = event;
-    data.detail = detail;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE,
-                                  (xdrproc_t)xdr_remote_storage_pool_event_lifecycle_msg,
-                                  &data);
-
-    return 0;
-}
-
-static int
-remoteRelayStoragePoolEventRefresh(virConnectPtr conn,
-                                   virStoragePoolPtr pool,
-                                   void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_storage_pool_event_refresh_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayStoragePoolEventCheckACL(callback->client, conn, pool))
-        return -1;
-
-    VIR_DEBUG("Relaying storage pool refresh event callback %d",
-              callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_storage_pool(&data.pool, pool);
-    data.callbackID = callback->callbackID;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH,
-                                  (xdrproc_t)xdr_remote_storage_pool_event_refresh_msg,
-                                  &data);
-
-    return 0;
-}
-
-static virConnectStoragePoolEventGenericCallback storageEventCallbacks[] = {
-    VIR_STORAGE_POOL_EVENT_CALLBACK(remoteRelayStoragePoolEventLifecycle),
-    VIR_STORAGE_POOL_EVENT_CALLBACK(remoteRelayStoragePoolEventRefresh),
-};
-
-verify(ARRAY_CARDINALITY(storageEventCallbacks) == VIR_STORAGE_POOL_EVENT_ID_LAST);
-
-static int
-remoteRelayNodeDeviceEventLifecycle(virConnectPtr conn,
-                                    virNodeDevicePtr dev,
-                                    int event,
-                                    int detail,
-                                    void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_node_device_event_lifecycle_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayNodeDeviceEventCheckACL(callback->client, conn, dev))
-        return -1;
-
-    VIR_DEBUG("Relaying node device lifecycle event %d, detail %d, callback %d",
-              event, detail, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_node_device(&data.dev, dev);
-    data.callbackID = callback->callbackID;
-    data.event = event;
-    data.detail = detail;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE,
-                                  (xdrproc_t)xdr_remote_node_device_event_lifecycle_msg,
-                                  &data);
-
-    return 0;
-}
-
-static int
-remoteRelayNodeDeviceEventUpdate(virConnectPtr conn,
-                                 virNodeDevicePtr dev,
-                                 void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_node_device_event_update_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayNodeDeviceEventCheckACL(callback->client, conn, dev))
-        return -1;
-
-    VIR_DEBUG("Relaying node device update event callback %d",
-              callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_node_device(&data.dev, dev);
-    data.callbackID = callback->callbackID;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE,
-                                  (xdrproc_t)xdr_remote_node_device_event_update_msg,
-                                  &data);
-
-    return 0;
-}
-
-static virConnectNodeDeviceEventGenericCallback nodeDeviceEventCallbacks[] = {
-    VIR_NODE_DEVICE_EVENT_CALLBACK(remoteRelayNodeDeviceEventLifecycle),
-    VIR_NODE_DEVICE_EVENT_CALLBACK(remoteRelayNodeDeviceEventUpdate),
-};
-
-verify(ARRAY_CARDINALITY(nodeDeviceEventCallbacks) == VIR_NODE_DEVICE_EVENT_ID_LAST);
-
-static int
-remoteRelaySecretEventLifecycle(virConnectPtr conn,
-                                virSecretPtr secret,
-                                int event,
-                                int detail,
-                                void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_secret_event_lifecycle_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelaySecretEventCheckACL(callback->client, conn, secret))
-        return -1;
-
-    VIR_DEBUG("Relaying node secretice lifecycle event %d, detail %d, callback %d",
-              event, detail, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_secret(&data.secret, secret);
-    data.callbackID = callback->callbackID;
-    data.event = event;
-    data.detail = detail;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_SECRET_EVENT_LIFECYCLE,
-                                  (xdrproc_t)xdr_remote_secret_event_lifecycle_msg,
-                                  &data);
-
-    return 0;
-}
-
-static int
-remoteRelaySecretEventValueChanged(virConnectPtr conn,
-                                   virSecretPtr secret,
-                                   void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    remote_secret_event_value_changed_msg data;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelaySecretEventCheckACL(callback->client, conn, secret))
-        return -1;
-
-    VIR_DEBUG("Relaying node secret value changed callback %d",
-              callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    make_nonnull_secret(&data.secret, secret);
-    data.callbackID = callback->callbackID;
-
-    remoteDispatchObjectEventSend(callback->client, remoteProgram,
-                                  REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED,
-                                  (xdrproc_t)xdr_remote_secret_event_value_changed_msg,
-                                  &data);
-
-    return 0;
-}
-
-static virConnectSecretEventGenericCallback secretEventCallbacks[] = {
-    VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventLifecycle),
-    VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventValueChanged),
-};
-
-verify(ARRAY_CARDINALITY(secretEventCallbacks) == VIR_SECRET_EVENT_ID_LAST);
-
-static void
-remoteRelayDomainQemuMonitorEvent(virConnectPtr conn,
-                                  virDomainPtr dom,
-                                  const char *event,
-                                  long long seconds,
-                                  unsigned int micros,
-                                  const char *details,
-                                  void *opaque)
-{
-    daemonClientEventCallbackPtr callback = opaque;
-    qemu_domain_monitor_event_msg data;
-    char **details_p = NULL;
-
-    if (callback->callbackID < 0 ||
-        !remoteRelayDomainQemuMonitorEventCheckACL(callback->client, conn,
-                                                   dom))
-        return;
-
-    VIR_DEBUG("Relaying qemu monitor event %s %s, callback %d",
-              event, details, callback->callbackID);
-
-    /* build return data */
-    memset(&data, 0, sizeof(data));
-    data.callbackID = callback->callbackID;
-    if (VIR_STRDUP(data.event, event) < 0)
-        goto error;
-    data.seconds = seconds;
-    data.micros = micros;
-    if (details &&
-        ((VIR_ALLOC(details_p) < 0) ||
-         VIR_STRDUP(*details_p, details) < 0))
-        goto error;
-    data.details = details_p;
-    make_nonnull_domain(&data.dom, dom);
-
-    remoteDispatchObjectEventSend(callback->client, qemuProgram,
-                                  QEMU_PROC_DOMAIN_MONITOR_EVENT,
-                                  (xdrproc_t)xdr_qemu_domain_monitor_event_msg,
-                                  &data);
-    return;
-
- error:
-    VIR_FREE(data.event);
-    VIR_FREE(details_p);
-}
-
-static
-void remoteRelayConnectionClosedEvent(virConnectPtr conn ATTRIBUTE_UNUSED, int reason, void *opaque)
-{
-    virNetServerClientPtr client = opaque;
-
-    VIR_DEBUG("Relaying connection closed event, reason %d", reason);
-
-    remote_connect_event_connection_closed_msg msg = { reason };
-    remoteDispatchObjectEventSend(client, remoteProgram,
-                                  REMOTE_PROC_CONNECT_EVENT_CONNECTION_CLOSED,
-                                  (xdrproc_t)xdr_remote_connect_event_connection_closed_msg,
-                                  &msg);
-}
-
-#define DEREG_CB(conn, eventCallbacks, neventCallbacks, deregFcn, name) \
-    do { \
-        size_t i; \
-        for (i = 0; i < neventCallbacks; i++) { \
-            int callbackID = eventCallbacks[i]->callbackID; \
-            if (callbackID < 0) { \
-                VIR_WARN("unexpected incomplete %s callback %zu", name, i); \
-                continue; \
-            } \
-            VIR_DEBUG("Deregistering remote %s event relay %d", \
-                      name, callbackID); \
-            eventCallbacks[i]->callbackID = -1; \
-            if (deregFcn(conn, callbackID) < 0) \
-                VIR_WARN("unexpected %s event deregister failure", name); \
-        } \
-        VIR_FREE(eventCallbacks); \
-        neventCallbacks = 0; \
-    } while (0);
-
-
-static void
-remoteClientFreePrivateCallbacks(struct daemonClientPrivate *priv)
-{
-    virIdentityPtr sysident = virIdentityGetSystem();
-    virIdentitySetCurrent(sysident);
-
-    DEREG_CB(priv->conn, priv->domainEventCallbacks,
-             priv->ndomainEventCallbacks,
-             virConnectDomainEventDeregisterAny, "domain");
-    DEREG_CB(priv->conn, priv->networkEventCallbacks,
-             priv->nnetworkEventCallbacks,
-             virConnectNetworkEventDeregisterAny, "network");
-    DEREG_CB(priv->conn, priv->storageEventCallbacks,
-             priv->nstorageEventCallbacks,
-             virConnectStoragePoolEventDeregisterAny, "storage");
-    DEREG_CB(priv->conn, priv->nodeDeviceEventCallbacks,
-             priv->nnodeDeviceEventCallbacks,
-             virConnectNodeDeviceEventDeregisterAny, "node device");
-    DEREG_CB(priv->conn, priv->secretEventCallbacks,
-             priv->nsecretEventCallbacks,
-             virConnectSecretEventDeregisterAny, "secret");
-    DEREG_CB(priv->conn, priv->qemuEventCallbacks,
-             priv->nqemuEventCallbacks,
-             virConnectDomainQemuMonitorEventDeregister, "qemu monitor");
-
-    if (priv->closeRegistered) {
-        if (virConnectUnregisterCloseCallback(priv->conn,
-                                              remoteRelayConnectionClosedEvent) < 0)
-            VIR_WARN("unexpected close callback event deregister failure");
-    }
-
-    virIdentitySetCurrent(NULL);
-    virObjectUnref(sysident);
-}
-#undef DEREG_CB
-
-
-/*
- * You must hold lock for at least the client
- * We don't free stuff here, merely disconnect the client's
- * network socket & resources.
- * We keep the libvirt connection open until any async
- * jobs have finished, then clean it up elsewhere
- */
-void remoteClientFree(void *data)
-{
-    struct daemonClientPrivate *priv = data;
-
-    if (priv->conn)
-        virConnectClose(priv->conn);
-
-    VIR_FREE(priv);
-}
-
-
-static void remoteClientCloseFunc(virNetServerClientPtr client)
-{
-    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
-
-    daemonRemoveAllClientStreams(priv->streams);
-
-    /* Deregister event delivery callback */
-    if (priv->conn)
-        remoteClientFreePrivateCallbacks(priv);
-}
-
-
-void *remoteClientNew(virNetServerClientPtr client,
-                      void *opaque ATTRIBUTE_UNUSED)
-{
-    struct daemonClientPrivate *priv;
-
-    if (VIR_ALLOC(priv) < 0)
-        return NULL;
-
-    if (virMutexInit(&priv->lock) < 0) {
-        VIR_FREE(priv);
-        virReportSystemError(errno, "%s", _("unable to init mutex"));
-        return NULL;
-    }
-
-    virNetServerClientSetCloseHook(client, remoteClientCloseFunc);
-    return priv;
-}
-
-/*----- Functions. -----*/
-
-static int
-remoteDispatchConnectOpen(virNetServerPtr server ATTRIBUTE_UNUSED,
-                          virNetServerClientPtr client,
-                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                          virNetMessageErrorPtr rerr,
-                          struct remote_connect_open_args *args)
-{
-    const char *name;
-    unsigned int flags;
-    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
-    int rv = -1;
-
-    VIR_DEBUG("priv=%p conn=%p", priv, priv->conn);
-    virMutexLock(&priv->lock);
-    /* Already opened? */
-    if (priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection already open"));
-        goto cleanup;
-    }
-
-    name = args->name ? *args->name : NULL;
-
-    /* If this connection arrived on a readonly socket, force
-     * the connection to be readonly.
-     */
-    flags = args->flags;
-    if (virNetServerClientGetReadonly(client))
-        flags |= VIR_CONNECT_RO;
-
-    priv->conn =
-        flags & VIR_CONNECT_RO
-        ? virConnectOpenReadOnly(name)
-        : virConnectOpen(name);
-
-    if (priv->conn == NULL)
-        goto cleanup;
-
-    /* force update the @readonly attribute which was inherited from the
-     * virNetServerService object - this is important for sockets that are RW
-     * by default, but do accept RO flags, e.g. TCP
-     */
-    virNetServerClientSetReadonly(client, (flags & VIR_CONNECT_RO));
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectClose(virNetServerPtr server ATTRIBUTE_UNUSED,
-                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                           virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED)
-{
-    virNetServerClientDelayedClose(client);
-    return 0;
-}
-
-
-static int
-remoteDispatchDomainGetSchedulerType(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                     virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                     virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                     virNetMessageErrorPtr rerr,
-                                     remote_domain_get_scheduler_type_args *args,
-                                     remote_domain_get_scheduler_type_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    char *type;
-    int nparams;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (!(type = virDomainGetSchedulerType(dom, &nparams)))
-        goto cleanup;
-
-    ret->type = type;
-    ret->nparams = nparams;
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetSchedulerParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                           virNetMessageErrorPtr rerr,
-                                           remote_domain_get_scheduler_parameters_args *args,
-                                           remote_domain_get_scheduler_parameters_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetSchedulerParameters(dom, params, &nparams) < 0)
-        goto cleanup;
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                0) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetSchedulerParametersFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                virNetMessageErrorPtr rerr,
-                                                remote_domain_get_scheduler_parameters_flags_args *args,
-                                                remote_domain_get_scheduler_parameters_flags_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetSchedulerParametersFlags(dom, params, &nparams,
-                                             args->flags) < 0)
-        goto cleanup;
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                args->flags) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                virNetMessageErrorPtr rerr,
-                                remote_domain_memory_stats_args *args,
-                                remote_domain_memory_stats_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virDomainMemoryStatPtr stats = NULL;
-    int nr_stats;
-    size_t i;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    /* Allocate stats array for making dispatch call */
-    if (VIR_ALLOC_N(stats, args->maxStats) < 0)
-        goto cleanup;
-
-    nr_stats = virDomainMemoryStats(dom, stats, args->maxStats, args->flags);
-    if (nr_stats < 0)
-        goto cleanup;
-
-    /* Allocate return buffer */
-    if (VIR_ALLOC_N(ret->stats.stats_val, args->maxStats) < 0)
-        goto cleanup;
-
-    /* Copy the stats into the xdr return structure */
-    for (i = 0; i < nr_stats; i++) {
-        ret->stats.stats_val[i].tag = stats[i].tag;
-        ret->stats.stats_val[i].val = stats[i].val;
-    }
-    ret->stats.stats_len = nr_stats;
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    VIR_FREE(stats);
-    return rv;
-}
-
-static int
-remoteDispatchDomainBlockPeek(virNetServerPtr server ATTRIBUTE_UNUSED,
-                              virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                              virNetMessageErrorPtr rerr,
-                              remote_domain_block_peek_args *args,
-                              remote_domain_block_peek_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    char *path;
-    unsigned long long offset;
-    size_t size;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-    path = args->path;
-    offset = args->offset;
-    size = args->size;
-    flags = args->flags;
-
-    if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("size > maximum buffer size"));
-        goto cleanup;
-    }
-
-    ret->buffer.buffer_len = size;
-    if (VIR_ALLOC_N(ret->buffer.buffer_val, size) < 0)
-        goto cleanup;
-
-    if (virDomainBlockPeek(dom, path, offset, size,
-                           ret->buffer.buffer_val, flags) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(ret->buffer.buffer_val);
-    }
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainBlockStatsFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                    virNetMessageErrorPtr rerr,
-                                    remote_domain_block_stats_flags_args *args,
-                                    remote_domain_block_stats_flags_ret *ret)
-{
-    virTypedParameterPtr params = NULL;
-    virDomainPtr dom = NULL;
-    const char *path = args->path;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_DOMAIN_BLOCK_STATS_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (virDomainBlockStatsFlags(dom, path, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    /* Serialize the block stats. */
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                args->flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainMemoryPeek(virNetServerPtr server ATTRIBUTE_UNUSED,
-                               virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                               virNetMessageErrorPtr rerr,
-                               remote_domain_memory_peek_args *args,
-                               remote_domain_memory_peek_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    unsigned long long offset;
-    size_t size;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-    offset = args->offset;
-    size = args->size;
-    flags = args->flags;
-
-    if (size > REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("size > maximum buffer size"));
-        goto cleanup;
-    }
-
-    ret->buffer.buffer_len = size;
-    if (VIR_ALLOC_N(ret->buffer.buffer_val, size) < 0)
-        goto cleanup;
-
-    if (virDomainMemoryPeek(dom, offset, size,
-                            ret->buffer.buffer_val, flags) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(ret->buffer.buffer_val);
-    }
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetSecurityLabel(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                     virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                     virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                     virNetMessageErrorPtr rerr,
-                                     remote_domain_get_security_label_args *args,
-                                     remote_domain_get_security_label_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virSecurityLabelPtr seclabel = NULL;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (VIR_ALLOC(seclabel) < 0)
-        goto cleanup;
-
-    if (virDomainGetSecurityLabel(dom, seclabel) < 0)
-        goto cleanup;
-
-    ret->label.label_len = strlen(seclabel->label) + 1;
-    if (VIR_ALLOC_N(ret->label.label_val, ret->label.label_len) < 0)
-        goto cleanup;
-    strcpy(ret->label.label_val, seclabel->label);
-    ret->enforcing = seclabel->enforcing;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    VIR_FREE(seclabel);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetSecurityLabelList(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                         virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                         virNetMessageErrorPtr rerr,
-                                         remote_domain_get_security_label_list_args *args,
-                                         remote_domain_get_security_label_list_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virSecurityLabelPtr seclabels = NULL;
-    int len, rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if ((len = virDomainGetSecurityLabelList(dom, &seclabels)) < 0) {
-        ret->ret = len;
-        ret->labels.labels_len = 0;
-        ret->labels.labels_val = NULL;
-        goto done;
-    }
-
-    if (VIR_ALLOC_N(ret->labels.labels_val, len) < 0)
-        goto cleanup;
-
-    for (i = 0; i < len; i++) {
-        size_t label_len = strlen(seclabels[i].label) + 1;
-        remote_domain_get_security_label_ret *cur = &ret->labels.labels_val[i];
-        if (VIR_ALLOC_N(cur->label.label_val, label_len) < 0)
-            goto cleanup;
-        if (virStrcpy(cur->label.label_val, seclabels[i].label, label_len) == NULL) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("failed to copy security label"));
-            goto cleanup;
-        }
-        cur->label.label_len = label_len;
-        cur->enforcing = seclabels[i].enforcing;
-    }
-    ret->labels.labels_len = ret->ret = len;
-
- done:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    VIR_FREE(seclabels);
-    return rv;
-}
-
-static int
-remoteDispatchNodeGetSecurityModel(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                   virNetMessageErrorPtr rerr,
-                                   remote_node_get_security_model_ret *ret)
-{
-    virSecurityModel secmodel;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    memset(&secmodel, 0, sizeof(secmodel));
-    if (virNodeGetSecurityModel(priv->conn, &secmodel) < 0)
-        goto cleanup;
-
-    ret->model.model_len = strlen(secmodel.model) + 1;
-    if (VIR_ALLOC_N(ret->model.model_val, ret->model.model_len) < 0)
-        goto cleanup;
-    strcpy(ret->model.model_val, secmodel.model);
-
-    ret->doi.doi_len = strlen(secmodel.doi) + 1;
-    if (VIR_ALLOC_N(ret->doi.doi_val, ret->doi.doi_len) < 0)
-        goto cleanup;
-    strcpy(ret->doi.doi_val, secmodel.doi);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetVcpuPinInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                   virNetMessageErrorPtr rerr,
-                                   remote_domain_get_vcpu_pin_info_args *args,
-                                   remote_domain_get_vcpu_pin_info_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    unsigned char *cpumaps = NULL;
-    int num;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (args->ncpumaps > REMOTE_VCPUINFO_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("ncpumaps > REMOTE_VCPUINFO_MAX"));
-        goto cleanup;
-    }
-
-    if (INT_MULTIPLY_OVERFLOW(args->ncpumaps, args->maplen) ||
-        args->ncpumaps * args->maplen > REMOTE_CPUMAPS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo * maplen > REMOTE_CPUMAPS_MAX"));
-        goto cleanup;
-    }
-
-    /* Allocate buffers to take the results. */
-    if (args->maplen > 0 &&
-        VIR_ALLOC_N(cpumaps, args->ncpumaps * args->maplen) < 0)
-        goto cleanup;
-
-    if ((num = virDomainGetVcpuPinInfo(dom,
-                                       args->ncpumaps,
-                                       cpumaps,
-                                       args->maplen,
-                                       args->flags)) < 0)
-        goto cleanup;
-
-    ret->num = num;
-    /* Don't need to allocate/copy the cpumaps if we make the reasonable
-     * assumption that unsigned char and char are the same size.
-     * Note that remoteDispatchClientRequest will free.
-     */
-    ret->cpumaps.cpumaps_len = args->ncpumaps * args->maplen;
-    ret->cpumaps.cpumaps_val = (char *) cpumaps;
-    cpumaps = NULL;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    VIR_FREE(cpumaps);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainPinEmulator(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                virNetServerClientPtr client,
-                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                virNetMessageErrorPtr rerr,
-                                remote_domain_pin_emulator_args *args)
-{
-    int rv = -1;
-    virDomainPtr dom = NULL;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainPinEmulator(dom,
-                             (unsigned char *) args->cpumap.cpumap_val,
-                             args->cpumap.cpumap_len,
-                             args->flags) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainGetEmulatorPinInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                       virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                       virNetMessageErrorPtr rerr,
-                                       remote_domain_get_emulator_pin_info_args *args,
-                                       remote_domain_get_emulator_pin_info_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    unsigned char *cpumaps = NULL;
-    int r;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    /* Allocate buffers to take the results */
-    if (args->maplen > 0 &&
-        VIR_ALLOC_N(cpumaps, args->maplen) < 0)
-        goto cleanup;
-
-    if ((r = virDomainGetEmulatorPinInfo(dom,
-                                         cpumaps,
-                                         args->maplen,
-                                         args->flags)) < 0)
-        goto cleanup;
-
-    ret->ret = r;
-    ret->cpumaps.cpumaps_len = args->maplen;
-    ret->cpumaps.cpumaps_val = (char *) cpumaps;
-    cpumaps = NULL;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    VIR_FREE(cpumaps);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetVcpus(virNetServerPtr server ATTRIBUTE_UNUSED,
-                             virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                             virNetMessageErrorPtr rerr,
-                             remote_domain_get_vcpus_args *args,
-                             remote_domain_get_vcpus_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virVcpuInfoPtr info = NULL;
-    unsigned char *cpumaps = NULL;
-    int info_len;
-    size_t i;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (args->maxinfo > REMOTE_VCPUINFO_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo > REMOTE_VCPUINFO_MAX"));
-        goto cleanup;
-    }
-
-    if (INT_MULTIPLY_OVERFLOW(args->maxinfo, args->maplen) ||
-        args->maxinfo * args->maplen > REMOTE_CPUMAPS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo * maplen > REMOTE_CPUMAPS_MAX"));
-        goto cleanup;
-    }
-
-    /* Allocate buffers to take the results. */
-    if (VIR_ALLOC_N(info, args->maxinfo) < 0)
-        goto cleanup;
-    if (args->maplen > 0 &&
-        VIR_ALLOC_N(cpumaps, args->maxinfo * args->maplen) < 0)
-        goto cleanup;
-
-    if ((info_len = virDomainGetVcpus(dom,
-                                      info, args->maxinfo,
-                                      cpumaps, args->maplen)) < 0)
-        goto cleanup;
-
-    /* Allocate the return buffer for info. */
-    ret->info.info_len = info_len;
-    if (VIR_ALLOC_N(ret->info.info_val, info_len) < 0)
-        goto cleanup;
-
-    for (i = 0; i < info_len; ++i) {
-        ret->info.info_val[i].number = info[i].number;
-        ret->info.info_val[i].state = info[i].state;
-        ret->info.info_val[i].cpu_time = info[i].cpuTime;
-        ret->info.info_val[i].cpu = info[i].cpu;
-    }
-
-    /* Don't need to allocate/copy the cpumaps if we make the reasonable
-     * assumption that unsigned char and char are the same size.
-     * Note that remoteDispatchClientRequest will free.
-     */
-    ret->cpumaps.cpumaps_len = args->maxinfo * args->maplen;
-    ret->cpumaps.cpumaps_val = (char *) cpumaps;
-    cpumaps = NULL;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(ret->info.info_val);
-    }
-    VIR_FREE(cpumaps);
-    VIR_FREE(info);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetIOThreadInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                    virNetServerClientPtr client,
-                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                    virNetMessageErrorPtr rerr,
-                                    remote_domain_get_iothread_info_args *args,
-                                    remote_domain_get_iothread_info_ret *ret)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
-    virDomainIOThreadInfoPtr *info = NULL;
-    virDomainPtr dom = NULL;
-    remote_domain_iothread_info *dst;
-    int ninfo = 0;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if ((ninfo = virDomainGetIOThreadInfo(dom, &info, args->flags)) < 0)
-        goto cleanup;
-
-    if (ninfo > REMOTE_IOTHREAD_INFO_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many IOThreads in info: %d for limit %d"),
-                       ninfo, REMOTE_IOTHREAD_INFO_MAX);
-        goto cleanup;
-    }
-
-    if (ninfo) {
-        if (VIR_ALLOC_N(ret->info.info_val, ninfo) < 0)
-            goto cleanup;
-
-        ret->info.info_len = ninfo;
-
-        for (i = 0; i < ninfo; i++) {
-            dst = &ret->info.info_val[i];
-            dst->iothread_id = info[i]->iothread_id;
-
-            /* No need to allocate/copy the cpumap if we make the reasonable
-             * assumption that unsigned char and char are the same size.
-             */
-            dst->cpumap.cpumap_len = info[i]->cpumaplen;
-            dst->cpumap.cpumap_val = (char *)info[i]->cpumap;
-            info[i]->cpumap = NULL;
-        }
-    } else {
-        ret->info.info_len = 0;
-        ret->info.info_val = NULL;
-    }
-
-    ret->ret = ninfo;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    if (ninfo >= 0)
-        for (i = 0; i < ninfo; i++)
-            virDomainIOThreadInfoFree(info[i]);
-    VIR_FREE(info);
-
-    return rv;
-}
-
-static int
-remoteDispatchDomainMigratePrepare(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                   virNetMessageErrorPtr rerr,
-                                   remote_domain_migrate_prepare_args *args,
-                                   remote_domain_migrate_prepare_ret *ret)
-{
-    char *cookie = NULL;
-    int cookielen = 0;
-    char *uri_in;
-    char **uri_out;
-    char *dname;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
-    dname = args->dname == NULL ? NULL : *args->dname;
-
-    /* Wacky world of XDR ... */
-    if (VIR_ALLOC(uri_out) < 0)
-        goto cleanup;
-
-    if (virDomainMigratePrepare(priv->conn, &cookie, &cookielen,
-                                uri_in, uri_out,
-                                args->flags, dname, args->resource) < 0)
-        goto cleanup;
-
-    /* remoteDispatchClientRequest will free cookie, uri_out and
-     * the string if there is one.
-     */
-    ret->cookie.cookie_len = cookielen;
-    ret->cookie.cookie_val = cookie;
-    if (*uri_out == NULL) {
-        ret->uri_out = NULL;
-    } else {
-        ret->uri_out = uri_out;
-        uri_out = NULL;
-    }
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    VIR_FREE(uri_out);
-    return rv;
-}
-
-static int
-remoteDispatchDomainMigratePrepare2(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                    virNetMessageErrorPtr rerr,
-                                    remote_domain_migrate_prepare2_args *args,
-                                    remote_domain_migrate_prepare2_ret *ret)
-{
-    char *cookie = NULL;
-    int cookielen = 0;
-    char *uri_in;
-    char **uri_out;
-    char *dname;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
-    dname = args->dname == NULL ? NULL : *args->dname;
-
-    /* Wacky world of XDR ... */
-    if (VIR_ALLOC(uri_out) < 0)
-        goto cleanup;
-
-    if (virDomainMigratePrepare2(priv->conn, &cookie, &cookielen,
-                                 uri_in, uri_out,
-                                 args->flags, dname, args->resource,
-                                 args->dom_xml) < 0)
-        goto cleanup;
-
-    /* remoteDispatchClientRequest will free cookie, uri_out and
-     * the string if there is one.
-     */
-    ret->cookie.cookie_len = cookielen;
-    ret->cookie.cookie_val = cookie;
-    ret->uri_out = *uri_out == NULL ? NULL : uri_out;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(uri_out);
-    }
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                        virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                        virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                        virNetMessageErrorPtr rerr,
-                                        remote_domain_get_memory_parameters_args *args,
-                                        remote_domain_get_memory_parameters_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetMemoryParameters(dom, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                args->flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetNumaParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                      virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                      virNetMessageErrorPtr rerr,
-                                      remote_domain_get_numa_parameters_args *args,
-                                      remote_domain_get_numa_parameters_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_DOMAIN_NUMA_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetNumaParameters(dom, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetBlkioParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                       virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                       virNetMessageErrorPtr rerr,
-                                       remote_domain_get_blkio_parameters_args *args,
-                                       remote_domain_get_blkio_parameters_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_DOMAIN_BLKIO_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetBlkioParameters(dom, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                args->flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchNodeGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
-                              virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                              virNetMessageErrorPtr rerr,
-                              remote_node_get_cpu_stats_args *args,
-                              remote_node_get_cpu_stats_ret *ret)
-{
-    virNodeCPUStatsPtr params = NULL;
-    size_t i;
-    int cpuNum = args->cpuNum;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_NODE_CPU_STATS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (virNodeGetCPUStats(priv->conn, cpuNum, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of stats
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    /* Serialise the memory parameters. */
-    ret->params.params_len = nparams;
-    if (VIR_ALLOC_N(ret->params.params_val, nparams) < 0)
-        goto cleanup;
-
-    for (i = 0; i < nparams; ++i) {
-        /* remoteDispatchClientRequest will free this: */
-        if (VIR_STRDUP(ret->params.params_val[i].field, params[i].field) < 0)
-            goto cleanup;
-
-        ret->params.params_val[i].value = params[i].value;
-    }
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        if (ret->params.params_val) {
-            for (i = 0; i < nparams; i++)
-                VIR_FREE(ret->params.params_val[i].field);
-            VIR_FREE(ret->params.params_val);
-        }
-    }
-    VIR_FREE(params);
-    return rv;
-}
-
-static int
-remoteDispatchNodeGetMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                 virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                 virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                 virNetMessageErrorPtr rerr,
-                                 remote_node_get_memory_stats_args *args,
-                                 remote_node_get_memory_stats_ret *ret)
-{
-    virNodeMemoryStatsPtr params = NULL;
-    size_t i;
-    int cellNum = args->cellNum;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_NODE_MEMORY_STATS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (virNodeGetMemoryStats(priv->conn, cellNum, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    /* Serialise the memory parameters. */
-    ret->params.params_len = nparams;
-    if (VIR_ALLOC_N(ret->params.params_val, nparams) < 0)
-        goto cleanup;
-
-    for (i = 0; i < nparams; ++i) {
-        /* remoteDispatchClientRequest will free this: */
-        if (VIR_STRDUP(ret->params.params_val[i].field, params[i].field) < 0)
-            goto cleanup;
-
-        ret->params.params_val[i].value = params[i].value;
-    }
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        if (ret->params.params_val) {
-            for (i = 0; i < nparams; i++)
-                VIR_FREE(ret->params.params_val[i].field);
-            VIR_FREE(ret->params.params_val);
-        }
-    }
-    VIR_FREE(params);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetPerfEvents(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                  virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                  virNetMessageErrorPtr rerr,
-                                  remote_domain_get_perf_events_args *args,
-                                  remote_domain_get_perf_events_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetPerfEvents(dom, &params, &nparams, args->flags) < 0)
-        goto cleanup;
-
-    if (nparams > REMOTE_DOMAIN_PERF_EVENTS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                0) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetBlockJobInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                    virNetMessageErrorPtr rerr,
-                                    remote_domain_get_block_job_info_args *args,
-                                    remote_domain_get_block_job_info_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virDomainBlockJobInfo tmp;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    rv = virDomainGetBlockJobInfo(dom, args->path, &tmp, args->flags);
-    if (rv <= 0)
-        goto cleanup;
-
-    ret->type = tmp.type;
-    ret->bandwidth = tmp.bandwidth;
-    ret->cur = tmp.cur;
-    ret->end = tmp.end;
-    ret->found = 1;
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetBlockIoTune(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                   virNetMessagePtr hdr ATTRIBUTE_UNUSED,
-                                   virNetMessageErrorPtr rerr,
-                                   remote_domain_get_block_io_tune_args *args,
-                                   remote_domain_get_block_io_tune_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    int rv = -1;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->nparams > REMOTE_DOMAIN_BLOCK_IO_TUNE_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetBlockIoTune(dom, args->disk ? *args->disk : NULL,
-                                params, &nparams, args->flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    /* Serialize the block I/O tuning parameters. */
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                args->flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-/*-------------------------------------------------------------*/
-
-static int
-remoteDispatchAuthList(virNetServerPtr server,
-                       virNetServerClientPtr client,
-                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                       virNetMessageErrorPtr rerr,
-                       remote_auth_list_ret *ret)
-{
-    int rv = -1;
-    int auth = virNetServerClientGetAuth(client);
-    uid_t callerUid;
-    gid_t callerGid;
-    pid_t callerPid;
-    unsigned long long timestamp;
-
-    /* If the client is root then we want to bypass the
-     * policykit auth to avoid root being denied if
-     * some piece of polkit isn't present/running
-     */
-    if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
-        if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
-                                              &callerPid, &timestamp) < 0) {
-            /* Don't do anything on error - it'll be validated at next
-             * phase of auth anyway */
-            virResetLastError();
-        } else if (callerUid == 0) {
-            char *ident;
-            if (virAsprintf(&ident, "pid:%lld,uid:%d",
-                            (long long) callerPid, (int) callerUid) < 0)
-                goto cleanup;
-            VIR_INFO("Bypass polkit auth for privileged client %s", ident);
-            virNetServerSetClientAuthenticated(server, client);
-            auth = VIR_NET_SERVER_SERVICE_AUTH_NONE;
-            VIR_FREE(ident);
-        }
-    }
-
-    ret->types.types_len = 1;
-    if (VIR_ALLOC_N(ret->types.types_val, ret->types.types_len) < 0)
-        goto cleanup;
-
-    switch ((virNetServerServiceAuthMethods) auth) {
-    case VIR_NET_SERVER_SERVICE_AUTH_NONE:
-        ret->types.types_val[0] = REMOTE_AUTH_NONE;
-        break;
-    case VIR_NET_SERVER_SERVICE_AUTH_POLKIT:
-        ret->types.types_val[0] = REMOTE_AUTH_POLKIT;
-        break;
-    case VIR_NET_SERVER_SERVICE_AUTH_SASL:
-        ret->types.types_val[0] = REMOTE_AUTH_SASL;
-        break;
-    }
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    return rv;
-}
-
-
-#ifdef WITH_SASL
-/*
- * Initializes the SASL session in prepare for authentication
- * and gives the client a list of allowed mechanisms to choose
- */
-static int
-remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED,
-                           virNetServerClientPtr client,
-                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                           virNetMessageErrorPtr rerr,
-                           remote_auth_sasl_init_ret *ret)
-{
-    virNetSASLSessionPtr sasl = NULL;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    virMutexLock(&priv->lock);
-
-    VIR_DEBUG("Initialize SASL auth %d", virNetServerClientGetFD(client));
-    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
-        priv->sasl != NULL) {
-        VIR_ERROR(_("client tried invalid SASL init request"));
-        goto authfail;
-    }
-
-    sasl = virNetSASLSessionNewServer(saslCtxt,
-                                      "libvirt",
-                                      virNetServerClientLocalAddrStringSASL(client),
-                                      virNetServerClientRemoteAddrStringSASL(client));
-    if (!sasl)
-        goto authfail;
-
-# if WITH_GNUTLS
-    /* Inform SASL that we've got an external SSF layer from TLS */
-    if (virNetServerClientHasTLSSession(client)) {
-        int ssf;
-
-        if ((ssf = virNetServerClientGetTLSKeySize(client)) < 0)
-            goto authfail;
-
-        ssf *= 8; /* key size is bytes, sasl wants bits */
-
-        VIR_DEBUG("Setting external SSF %d", ssf);
-        if (virNetSASLSessionExtKeySize(sasl, ssf) < 0)
-            goto authfail;
-    }
-# endif
-
-    if (virNetServerClientIsSecure(client))
-        /* If we've got TLS or UNIX domain sock, we don't care about SSF */
-        virNetSASLSessionSecProps(sasl, 0, 0, true);
-    else
-        /* Plain TCP, better get an SSF layer */
-        virNetSASLSessionSecProps(sasl,
-                                  56,  /* Good enough to require kerberos */
-                                  100000,  /* Arbitrary big number */
-                                  false); /* No anonymous */
-
-    if (!(ret->mechlist = virNetSASLSessionListMechanisms(sasl)))
-        goto authfail;
-    VIR_DEBUG("Available mechanisms for client: '%s'", ret->mechlist);
-
-    priv->sasl = sasl;
-    virMutexUnlock(&priv->lock);
-    return 0;
-
- authfail:
-    virResetLastError();
-    virReportError(VIR_ERR_AUTH_FAILED, "%s",
-                   _("authentication failed"));
-    virNetMessageSaveError(rerr);
-    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
-          "client=%p auth=%d",
-          client, REMOTE_AUTH_SASL);
-    virObjectUnref(sasl);
-    virMutexUnlock(&priv->lock);
-    return -1;
-}
-
-/*
- * Returns 0 if ok, -1 on error, -2 if rejected
- */
-static int
-remoteSASLFinish(virNetServerPtr server,
-                 virNetServerClientPtr client)
-{
-    virIdentityPtr clnt_identity = NULL;
-    const char *identity;
-    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
-    int ssf;
-
-    /* TLS or UNIX domain sockets trivially OK */
-    if (!virNetServerClientIsSecure(client)) {
-        if ((ssf = virNetSASLSessionGetKeySize(priv->sasl)) < 0)
-            goto error;
-
-        VIR_DEBUG("negotiated an SSF of %d", ssf);
-        if (ssf < 56) { /* 56 is good for Kerberos */
-            VIR_ERROR(_("negotiated SSF %d was not strong enough"), ssf);
-            return -2;
-        }
-    }
-
-    if (!(identity = virNetSASLSessionGetIdentity(priv->sasl)))
-        return -2;
-
-    if (!virNetSASLContextCheckIdentity(saslCtxt, identity))
-        return -2;
-
-    if (!(clnt_identity = virNetServerClientGetIdentity(client)))
-        goto error;
-
-    virNetServerSetClientAuthenticated(server, client);
-    virNetServerClientSetSASLSession(client, priv->sasl);
-    virIdentitySetSASLUserName(clnt_identity, identity);
-
-    VIR_DEBUG("Authentication successful %d", virNetServerClientGetFD(client));
-
-    PROBE(RPC_SERVER_CLIENT_AUTH_ALLOW,
-          "client=%p auth=%d identity=%s",
-          client, REMOTE_AUTH_SASL, identity);
-
-    virObjectUnref(clnt_identity);
-    virObjectUnref(priv->sasl);
-    priv->sasl = NULL;
-
-    return 0;
-
- error:
-    return -1;
-}
-
-/*
- * This starts the SASL authentication negotiation.
- */
-static int
-remoteDispatchAuthSaslStart(virNetServerPtr server,
-                            virNetServerClientPtr client,
-                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                            virNetMessageErrorPtr rerr,
-                            remote_auth_sasl_start_args *args,
-                            remote_auth_sasl_start_ret *ret)
-{
-    const char *serverout;
-    size_t serveroutlen;
-    int err;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    const char *identity;
-
-    virMutexLock(&priv->lock);
-
-    VIR_DEBUG("Start SASL auth %d", virNetServerClientGetFD(client));
-    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
-        priv->sasl == NULL) {
-        VIR_ERROR(_("client tried invalid SASL start request"));
-        goto authfail;
-    }
-
-    VIR_DEBUG("Using SASL mechanism %s. Data %d bytes, nil: %d",
-              args->mech, args->data.data_len, args->nil);
-    err = virNetSASLSessionServerStart(priv->sasl,
-                                       args->mech,
-                                       /* NB, distinction of NULL vs "" is *critical* in SASL */
-                                       args->nil ? NULL : args->data.data_val,
-                                       args->data.data_len,
-                                       &serverout,
-                                       &serveroutlen);
-    if (err != VIR_NET_SASL_COMPLETE &&
-        err != VIR_NET_SASL_CONTINUE)
-        goto authfail;
-
-    if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
-        VIR_ERROR(_("sasl start reply data too long %d"), (int)serveroutlen);
-        goto authfail;
-    }
-
-    /* NB, distinction of NULL vs "" is *critical* in SASL */
-    if (serverout) {
-        if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0)
-            goto authfail;
-        memcpy(ret->data.data_val, serverout, serveroutlen);
-    } else {
-        ret->data.data_val = NULL;
-    }
-    ret->nil = serverout ? 0 : 1;
-    ret->data.data_len = serveroutlen;
-
-    VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil);
-    if (err == VIR_NET_SASL_CONTINUE) {
-        ret->complete = 0;
-    } else {
-        /* Check username whitelist ACL */
-        if ((err = remoteSASLFinish(server, client)) < 0) {
-            if (err == -2)
-                goto authdeny;
-            else
-                goto authfail;
-        }
-
-        ret->complete = 1;
-    }
-
-    virMutexUnlock(&priv->lock);
-    return 0;
-
- authfail:
-    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
-          "client=%p auth=%d",
-          client, REMOTE_AUTH_SASL);
-    goto error;
-
- authdeny:
-    identity = virNetSASLSessionGetIdentity(priv->sasl);
-    PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
-          "client=%p auth=%d identity=%s",
-          client, REMOTE_AUTH_SASL, identity);
-    goto error;
-
- error:
-    virObjectUnref(priv->sasl);
-    priv->sasl = NULL;
-    virResetLastError();
-    virReportError(VIR_ERR_AUTH_FAILED, "%s",
-                   _("authentication failed"));
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return -1;
-}
-
-
-static int
-remoteDispatchAuthSaslStep(virNetServerPtr server,
-                           virNetServerClientPtr client,
-                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                           virNetMessageErrorPtr rerr,
-                           remote_auth_sasl_step_args *args,
-                           remote_auth_sasl_step_ret *ret)
-{
-    const char *serverout;
-    size_t serveroutlen;
-    int err;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    const char *identity;
-
-    virMutexLock(&priv->lock);
-
-    VIR_DEBUG("Step SASL auth %d", virNetServerClientGetFD(client));
-    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
-        priv->sasl == NULL) {
-        VIR_ERROR(_("client tried invalid SASL start request"));
-        goto authfail;
-    }
-
-    VIR_DEBUG("Step using SASL Data %d bytes, nil: %d",
-              args->data.data_len, args->nil);
-    err = virNetSASLSessionServerStep(priv->sasl,
-                                      /* NB, distinction of NULL vs "" is *critical* in SASL */
-                                      args->nil ? NULL : args->data.data_val,
-                                      args->data.data_len,
-                                      &serverout,
-                                      &serveroutlen);
-    if (err != VIR_NET_SASL_COMPLETE &&
-        err != VIR_NET_SASL_CONTINUE)
-        goto authfail;
-
-    if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
-        VIR_ERROR(_("sasl step reply data too long %d"),
-                  (int)serveroutlen);
-        goto authfail;
-    }
-
-    /* NB, distinction of NULL vs "" is *critical* in SASL */
-    if (serverout) {
-        if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0)
-            goto authfail;
-        memcpy(ret->data.data_val, serverout, serveroutlen);
-    } else {
-        ret->data.data_val = NULL;
-    }
-    ret->nil = serverout ? 0 : 1;
-    ret->data.data_len = serveroutlen;
-
-    VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil);
-    if (err == VIR_NET_SASL_CONTINUE) {
-        ret->complete = 0;
-    } else {
-        /* Check username whitelist ACL */
-        if ((err = remoteSASLFinish(server, client)) < 0) {
-            if (err == -2)
-                goto authdeny;
-            else
-                goto authfail;
-        }
-
-        ret->complete = 1;
-    }
-
-    virMutexUnlock(&priv->lock);
-    return 0;
-
- authfail:
-    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
-          "client=%p auth=%d",
-          client, REMOTE_AUTH_SASL);
-    goto error;
-
- authdeny:
-    identity = virNetSASLSessionGetIdentity(priv->sasl);
-    PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
-          "client=%p auth=%d identity=%s",
-          client, REMOTE_AUTH_SASL, identity);
-    goto error;
-
- error:
-    virObjectUnref(priv->sasl);
-    priv->sasl = NULL;
-    virResetLastError();
-    virReportError(VIR_ERR_AUTH_FAILED, "%s",
-                   _("authentication failed"));
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return -1;
-}
-#else
-static int
-remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED,
-                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                           virNetMessageErrorPtr rerr,
-                           remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED)
-{
-    VIR_WARN("Client tried unsupported SASL auth");
-    virReportError(VIR_ERR_AUTH_FAILED, "%s",
-                   _("authentication failed"));
-    virNetMessageSaveError(rerr);
-    return -1;
-}
-static int
-remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED,
-                            virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                            virNetMessageErrorPtr rerr,
-                            remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED,
-                            remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED)
-{
-    VIR_WARN("Client tried unsupported SASL auth");
-    virReportError(VIR_ERR_AUTH_FAILED, "%s",
-                   _("authentication failed"));
-    virNetMessageSaveError(rerr);
-    return -1;
-}
-static int
-remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED,
-                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                           virNetMessageErrorPtr rerr,
-                           remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED,
-                           remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED)
-{
-    VIR_WARN("Client tried unsupported SASL auth");
-    virReportError(VIR_ERR_AUTH_FAILED, "%s",
-                   _("authentication failed"));
-    virNetMessageSaveError(rerr);
-    return -1;
-}
-#endif
-
-
-
-static int
-remoteDispatchAuthPolkit(virNetServerPtr server,
-                         virNetServerClientPtr client,
-                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                         virNetMessageErrorPtr rerr,
-                         remote_auth_polkit_ret *ret)
-{
-    pid_t callerPid = -1;
-    gid_t callerGid = -1;
-    uid_t callerUid = -1;
-    unsigned long long timestamp;
-    const char *action;
-    char *ident = NULL;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    int rv;
-
-    virMutexLock(&priv->lock);
-    action = virNetServerClientGetReadonly(client) ?
-        "org.libvirt.unix.monitor" :
-        "org.libvirt.unix.manage";
-
-    VIR_DEBUG("Start PolicyKit auth %d", virNetServerClientGetFD(client));
-    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
-        VIR_ERROR(_("client tried invalid PolicyKit init request"));
-        goto authfail;
-    }
-
-    if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
-                                          &callerPid, &timestamp) < 0) {
-        goto authfail;
-    }
-
-    if (timestamp == 0) {
-        VIR_WARN("Failing polkit auth due to missing client (pid=%lld) start time",
-                 (long long)callerPid);
-        goto authfail;
-    }
-
-    VIR_INFO("Checking PID %lld running as %d",
-             (long long) callerPid, callerUid);
-
-    rv = virPolkitCheckAuth(action,
-                            callerPid,
-                            timestamp,
-                            callerUid,
-                            NULL,
-                            true);
-    if (rv == -1)
-        goto authfail;
-    else if (rv == -2)
-        goto authdeny;
-
-    PROBE(RPC_SERVER_CLIENT_AUTH_ALLOW,
-          "client=%p auth=%d identity=%s",
-          client, REMOTE_AUTH_POLKIT, ident);
-    VIR_INFO("Policy allowed action %s from pid %lld, uid %d",
-             action, (long long) callerPid, callerUid);
-    ret->complete = 1;
-
-    virNetServerSetClientAuthenticated(server, client);
-    virMutexUnlock(&priv->lock);
-
-    return 0;
-
- error:
-    virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return -1;
-
- authfail:
-    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
-          "client=%p auth=%d",
-          client, REMOTE_AUTH_POLKIT);
-    goto error;
-
- authdeny:
-    PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
-          "client=%p auth=%d identity=%s",
-          client, REMOTE_AUTH_POLKIT, ident);
-    goto error;
-}
-
-
-static int
-remoteDispatchNodeDeviceGetParent(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                  virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                  virNetMessageErrorPtr rerr,
-                                  remote_node_device_get_parent_args *args,
-                                  remote_node_device_get_parent_ret *ret)
-{
-    virNodeDevicePtr dev = NULL;
-    const char *parent = NULL;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dev = virNodeDeviceLookupByName(priv->conn, args->name)))
-        goto cleanup;
-
-    parent = virNodeDeviceGetParent(dev);
-
-    if (parent == NULL) {
-        ret->parent = NULL;
-    } else {
-        /* remoteDispatchClientRequest will free this. */
-        char **parent_p;
-        if (VIR_ALLOC(parent_p) < 0)
-            goto cleanup;
-        if (VIR_STRDUP(*parent_p, parent) < 0) {
-            VIR_FREE(parent_p);
-            goto cleanup;
-        }
-        ret->parent = parent_p;
-    }
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dev);
-    return rv;
-}
-
-static int
-remoteDispatchConnectRegisterCloseCallback(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                           virNetServerClientPtr client,
-                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                           virNetMessageErrorPtr rerr)
-{
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    virMutexLock(&priv->lock);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (virConnectRegisterCloseCallback(priv->conn,
-                                        remoteRelayConnectionClosedEvent,
-                                        client, NULL) < 0)
-        goto cleanup;
-
-    priv->closeRegistered = true;
-    rv = 0;
-
- cleanup:
-    virMutexUnlock(&priv->lock);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    return rv;
-}
-
-static int
-remoteDispatchConnectUnregisterCloseCallback(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                             virNetServerClientPtr client,
-                                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                             virNetMessageErrorPtr rerr)
-{
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    virMutexLock(&priv->lock);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (virConnectUnregisterCloseCallback(priv->conn,
-                                          remoteRelayConnectionClosedEvent) < 0)
-        goto cleanup;
-
-    priv->closeRegistered = false;
-    rv = 0;
-
- cleanup:
-    virMutexUnlock(&priv->lock);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    return rv;
-}
-
-static int
-remoteDispatchConnectDomainEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                         virNetServerClientPtr client,
-                                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                         virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                         remote_connect_domain_event_register_ret *ret ATTRIBUTE_UNUSED)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; or you can use VIR_APPEND_ELEMENT_COPY to avoid
-     * clearing 'callback' and having to juggle the pointer
-     * between 'ref' and 'callback'.
-     */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->eventID = VIR_DOMAIN_EVENT_ID_LIFECYCLE;
-    callback->callbackID = -1;
-    callback->legacy = true;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->domainEventCallbacks,
-                           priv->ndomainEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
-                                                       NULL,
-                                                       VIR_DOMAIN_EVENT_ID_LIFECYCLE,
-                                                       VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
-                                                       ref,
-                                                       remoteEventCallbackFree)) < 0) {
-        VIR_SHRINK_N(priv->domainEventCallbacks,
-                     priv->ndomainEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchConnectDomainEventDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                           virNetServerClientPtr client,
-                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                           virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                           remote_connect_domain_event_deregister_ret *ret ATTRIBUTE_UNUSED)
-{
-    int callbackID = -1;
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    for (i = 0; i < priv->ndomainEventCallbacks; i++) {
-        if (priv->domainEventCallbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE) {
-            callbackID = priv->domainEventCallbacks[i]->callbackID;
-            break;
-        }
-    }
-
-    if (callbackID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("domain event %d not registered"),
-                       VIR_DOMAIN_EVENT_ID_LIFECYCLE);
-        goto cleanup;
-    }
-
-    if (virConnectDomainEventDeregisterAny(priv->conn, callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->domainEventCallbacks, i,
-                       priv->ndomainEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static void
-remoteDispatchObjectEventSend(virNetServerClientPtr client,
-                              virNetServerProgramPtr program,
-                              int procnr,
-                              xdrproc_t proc,
-                              void *data)
-{
-    virNetMessagePtr msg;
-
-    if (!(msg = virNetMessageNew(false)))
-        goto cleanup;
-
-    msg->header.prog = virNetServerProgramGetID(program);
-    msg->header.vers = virNetServerProgramGetVersion(program);
-    msg->header.proc = procnr;
-    msg->header.type = VIR_NET_MESSAGE;
-    msg->header.serial = 1;
-    msg->header.status = VIR_NET_OK;
-
-    if (virNetMessageEncodeHeader(msg) < 0)
-        goto cleanup;
-
-    if (virNetMessageEncodePayload(msg, proc, data) < 0)
-        goto cleanup;
-
-    VIR_DEBUG("Queue event %d %zu", procnr, msg->bufferLength);
-    virNetServerClientSendMessage(client, msg);
-
-    xdr_free(proc, data);
-    return;
-
- cleanup:
-    virNetMessageFree(msg);
-    xdr_free(proc, data);
-}
-
-static int
-remoteDispatchSecretGetValue(virNetServerPtr server ATTRIBUTE_UNUSED,
-                             virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                             virNetMessageErrorPtr rerr,
-                             remote_secret_get_value_args *args,
-                             remote_secret_get_value_ret *ret)
-{
-    virSecretPtr secret = NULL;
-    size_t value_size;
-    unsigned char *value;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(secret = get_nonnull_secret(priv->conn, args->secret)))
-        goto cleanup;
-
-    if (!(value = virSecretGetValue(secret, &value_size, args->flags)))
-        goto cleanup;
-
-    ret->value.value_len = value_size;
-    ret->value.value_val = (char *)value;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(secret);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetState(virNetServerPtr server ATTRIBUTE_UNUSED,
-                             virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                             virNetMessageErrorPtr rerr,
-                             remote_domain_get_state_args *args,
-                             remote_domain_get_state_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetState(dom, &ret->state, &ret->reason, args->flags) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-/* Due to back-compat reasons, two RPC calls map to the same libvirt
- * API of virConnectDomainEventRegisterAny.  A client should only use
- * the new call if they have probed
- * VIR_DRV_SUPPORTS_FEATURE(VIR_DRV_FEATURE_REMOTE_EVENT_CALLBACK),
- * and must not mix the two styles.  */
-static int
-remoteDispatchConnectDomainEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                            virNetServerClientPtr client,
-                                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                            virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                            remote_connect_domain_event_register_any_args *args)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    /* We intentionally do not use VIR_DOMAIN_EVENT_ID_LAST here; any
-     * new domain events added after this point should only use the
-     * modern callback style of RPC.  */
-    if (args->eventID > VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED ||
-        args->eventID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"),
-                       args->eventID);
-        goto cleanup;
-    }
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
-     * success, we use 'ref' to save a copy of the pointer.  */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->eventID = args->eventID;
-    callback->callbackID = -1;
-    callback->legacy = true;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->domainEventCallbacks,
-                           priv->ndomainEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
-                                                       NULL,
-                                                       args->eventID,
-                                                       domainEventCallbacks[args->eventID],
-                                                       ref,
-                                                       remoteEventCallbackFree)) < 0) {
-        VIR_SHRINK_N(priv->domainEventCallbacks,
-                     priv->ndomainEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectDomainEventCallbackRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                    virNetServerClientPtr client,
-                                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                    virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                                    remote_connect_domain_event_callback_register_any_args *args,
-                                                    remote_connect_domain_event_callback_register_any_ret *ret)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    virDomainPtr dom = NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    if (args->dom &&
-        !(dom = get_nonnull_domain(priv->conn, *args->dom)))
-        goto cleanup;
-
-    if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST || args->eventID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"),
-                       args->eventID);
-        goto cleanup;
-    }
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
-     * success, we use 'ref' to save a copy of the pointer.  */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->eventID = args->eventID;
-    callback->callbackID = -1;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->domainEventCallbacks,
-                           priv->ndomainEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
-                                                       dom,
-                                                       args->eventID,
-                                                       domainEventCallbacks[args->eventID],
-                                                       ref,
-                                                       remoteEventCallbackFree)) < 0) {
-        VIR_SHRINK_N(priv->domainEventCallbacks,
-                     priv->ndomainEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-    ret->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectDomainEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                              virNetServerClientPtr client,
-                                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                              virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                              remote_connect_domain_event_deregister_any_args *args)
-{
-    int callbackID = -1;
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    /* We intentionally do not use VIR_DOMAIN_EVENT_ID_LAST here; any
-     * new domain events added after this point should only use the
-     * modern callback style of RPC.  */
-    if (args->eventID > VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED ||
-        args->eventID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"),
-                       args->eventID);
-        goto cleanup;
-    }
-
-    for (i = 0; i < priv->ndomainEventCallbacks; i++) {
-        if (priv->domainEventCallbacks[i]->eventID == args->eventID) {
-            callbackID = priv->domainEventCallbacks[i]->callbackID;
-            break;
-        }
-    }
-    if (callbackID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("domain event %d not registered"), args->eventID);
-        goto cleanup;
-    }
-
-    if (virConnectDomainEventDeregisterAny(priv->conn, callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->domainEventCallbacks, i,
-                       priv->ndomainEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectDomainEventCallbackDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                      virNetServerClientPtr client,
-                                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                      virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                                      remote_connect_domain_event_callback_deregister_any_args *args)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    for (i = 0; i < priv->ndomainEventCallbacks; i++) {
-        if (priv->domainEventCallbacks[i]->callbackID == args->callbackID)
-            break;
-    }
-    if (i == priv->ndomainEventCallbacks) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("domain event callback %d not registered"),
-                       args->callbackID);
-        goto cleanup;
-    }
-
-    if (virConnectDomainEventDeregisterAny(priv->conn, args->callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->domainEventCallbacks, i,
-                       priv->ndomainEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-
-static int
-qemuDispatchDomainMonitorCommand(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                 virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                 virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                 virNetMessageErrorPtr rerr,
-                                 qemu_domain_monitor_command_args *args,
-                                 qemu_domain_monitor_command_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainQemuMonitorCommand(dom, args->cmd, &ret->result,
-                                    args->flags) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigrateBegin3(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                  virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                  virNetMessageErrorPtr rerr,
-                                  remote_domain_migrate_begin3_args *args,
-                                  remote_domain_migrate_begin3_ret *ret)
-{
-    char *xml = NULL;
-    virDomainPtr dom = NULL;
-    char *dname;
-    char *xmlin;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    xmlin = args->xmlin == NULL ? NULL : *args->xmlin;
-    dname = args->dname == NULL ? NULL : *args->dname;
-
-    if (!(xml = virDomainMigrateBegin3(dom, xmlin,
-                                       &cookieout, &cookieoutlen,
-                                       args->flags, dname, args->resource)))
-        goto cleanup;
-
-    /* remoteDispatchClientRequest will free cookie and
-     * the xml string if there is one.
-     */
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-    ret->xml = xml;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigratePrepare3(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                    virNetMessageErrorPtr rerr,
-                                    remote_domain_migrate_prepare3_args *args,
-                                    remote_domain_migrate_prepare3_ret *ret)
-{
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    char *uri_in;
-    char **uri_out;
-    char *dname;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
-    dname = args->dname == NULL ? NULL : *args->dname;
-
-    /* Wacky world of XDR ... */
-    if (VIR_ALLOC(uri_out) < 0)
-        goto cleanup;
-
-    if (virDomainMigratePrepare3(priv->conn,
-                                 args->cookie_in.cookie_in_val,
-                                 args->cookie_in.cookie_in_len,
-                                 &cookieout, &cookieoutlen,
-                                 uri_in, uri_out,
-                                 args->flags, dname, args->resource,
-                                 args->dom_xml) < 0)
-        goto cleanup;
-
-    /* remoteDispatchClientRequest will free cookie, uri_out and
-     * the string if there is one.
-     */
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-    ret->uri_out = *uri_out == NULL ? NULL : uri_out;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(uri_out);
-    }
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigratePerform3(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                    virNetMessageErrorPtr rerr,
-                                    remote_domain_migrate_perform3_args *args,
-                                    remote_domain_migrate_perform3_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    char *xmlin;
-    char *dname;
-    char *uri;
-    char *dconnuri;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    xmlin = args->xmlin == NULL ? NULL : *args->xmlin;
-    dname = args->dname == NULL ? NULL : *args->dname;
-    uri = args->uri == NULL ? NULL : *args->uri;
-    dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
-
-    if (virDomainMigratePerform3(dom, xmlin,
-                                 args->cookie_in.cookie_in_val,
-                                 args->cookie_in.cookie_in_len,
-                                 &cookieout, &cookieoutlen,
-                                 dconnuri, uri,
-                                 args->flags, dname, args->resource) < 0)
-        goto cleanup;
-
-    /* remoteDispatchClientRequest will free cookie
-     */
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigrateFinish3(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                   virNetMessageErrorPtr rerr,
-                                   remote_domain_migrate_finish3_args *args,
-                                   remote_domain_migrate_finish3_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    char *uri;
-    char *dconnuri;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    uri = args->uri == NULL ? NULL : *args->uri;
-    dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
-
-    if (!(dom = virDomainMigrateFinish3(priv->conn, args->dname,
-                                        args->cookie_in.cookie_in_val,
-                                        args->cookie_in.cookie_in_len,
-                                        &cookieout, &cookieoutlen,
-                                        dconnuri, uri,
-                                        args->flags,
-                                        args->cancelled)))
-        goto cleanup;
-
-    make_nonnull_domain(&ret->dom, dom);
-
-    /* remoteDispatchClientRequest will free cookie
-     */
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(cookieout);
-    }
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigrateConfirm3(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                    virNetMessageErrorPtr rerr,
-                                    remote_domain_migrate_confirm3_args *args)
-{
-    virDomainPtr dom = NULL;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainMigrateConfirm3(dom,
-                                 args->cookie_in.cookie_in_val,
-                                 args->cookie_in.cookie_in_len,
-                                 args->flags, args->cancelled) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int remoteDispatchConnectSupportsFeature(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                virNetServerClientPtr client,
-                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                virNetMessageErrorPtr rerr,
-                                                remote_connect_supports_feature_args *args,
-                                                remote_connect_supports_feature_ret *ret)
-{
-    int rv = -1;
-    int supported;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    /* This feature is checked before opening the connection, thus we must
-     * check it first.
-     */
-    if (args->feature == VIR_DRV_FEATURE_PROGRAM_KEEPALIVE) {
-        if (virNetServerClientStartKeepAlive(client) < 0)
-            goto cleanup;
-        supported = 1;
-        goto done;
-    }
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    switch (args->feature) {
-    case VIR_DRV_FEATURE_FD_PASSING:
-    case VIR_DRV_FEATURE_REMOTE_EVENT_CALLBACK:
-    case VIR_DRV_FEATURE_REMOTE_CLOSE_CALLBACK:
-        supported = 1;
-        break;
-
-    default:
-        if ((supported = virConnectSupportsFeature(priv->conn, args->feature)) < 0)
-            goto cleanup;
-        break;
-    }
-
- done:
-    ret->supported = supported;
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainOpenGraphics(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                 virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                 virNetMessagePtr msg,
-                                 virNetMessageErrorPtr rerr,
-                                 remote_domain_open_graphics_args *args)
-{
-    virDomainPtr dom = NULL;
-    int rv = -1;
-    int fd = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if ((fd = virNetMessageDupFD(msg, 0)) < 0)
-        goto cleanup;
-
-    if (virDomainOpenGraphics(dom,
-                              args->idx,
-                              fd,
-                              args->flags) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    VIR_FORCE_CLOSE(fd);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainOpenGraphicsFd(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                   virNetMessagePtr msg,
-                                   virNetMessageErrorPtr rerr,
-                                   remote_domain_open_graphics_fd_args *args)
-{
-    virDomainPtr dom = NULL;
-    int rv = -1;
-    int fd = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if ((fd = virDomainOpenGraphicsFD(dom,
-                                      args->idx,
-                                      args->flags)) < 0)
-        goto cleanup;
-
-    if (virNetMessageAddFD(msg, fd) < 0)
-        goto cleanup;
-
-    /* return 1 here to let virNetServerProgramDispatchCall know
-     * we are passing a FD */
-    rv = 1;
-
- cleanup:
-    VIR_FORCE_CLOSE(fd);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainGetInterfaceParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                           virNetMessageErrorPtr rerr,
-                                           remote_domain_get_interface_parameters_args *args,
-                                           remote_domain_get_interface_parameters_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    const char *device = args->device;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                virNetMessagePtr hdr ATTRIBUTE_UNUSED,
-                                virNetMessageErrorPtr rerr,
-                                remote_domain_get_cpu_stats_args *args,
-                                remote_domain_get_cpu_stats_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    struct daemonClientPrivate *priv;
-    virTypedParameterPtr params = NULL;
-    int rv = -1;
-    int percpu_len = 0;
-
-    priv = virNetServerClientGetPrivateData(client);
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->nparams > REMOTE_NODE_CPU_STATS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->ncpus > REMOTE_DOMAIN_GET_CPU_STATS_NCPUS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("ncpus too large"));
-        goto cleanup;
-    }
-
-    if (args->nparams > 0 &&
-        VIR_ALLOC_N(params, args->ncpus * args->nparams) < 0)
-        goto cleanup;
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    percpu_len = virDomainGetCPUStats(dom, params, args->nparams,
-                                      args->start_cpu, args->ncpus,
-                                      args->flags);
-    if (percpu_len < 0)
-        goto cleanup;
-    /* If nparams == 0, the function returns a single value */
-    if (args->nparams == 0)
-        goto success;
-
-    if (virTypedParamsSerialize(params, args->nparams * args->ncpus,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                args->flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-    ret->nparams = percpu_len;
-    if (args->nparams && !(args->flags & VIR_TYPED_PARAM_STRING_OKAY)) {
-        size_t i;
-
-        for (i = 0; i < percpu_len; i++) {
-            if (params[i].type == VIR_TYPED_PARAM_STRING)
-                ret->nparams--;
-        }
-    }
-
- cleanup:
-    if (rv < 0)
-         virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, args->ncpus * args->nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetDiskErrors(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                  virNetServerClientPtr client,
-                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                  virNetMessageErrorPtr rerr,
-                                  remote_domain_get_disk_errors_args *args,
-                                  remote_domain_get_disk_errors_ret *ret)
-{
-    int rv = -1;
-    virDomainPtr dom = NULL;
-    virDomainDiskErrorPtr errors = NULL;
-    int len = 0;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (args->maxerrors > REMOTE_DOMAIN_DISK_ERRORS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("maxerrors too large"));
-        goto cleanup;
-    }
-
-    if (args->maxerrors &&
-        VIR_ALLOC_N(errors, args->maxerrors) < 0)
-        goto cleanup;
-
-    if ((len = virDomainGetDiskErrors(dom, errors,
-                                      args->maxerrors,
-                                      args->flags)) < 0)
-        goto cleanup;
-
-    ret->nerrors = len;
-    if (errors &&
-        remoteSerializeDomainDiskErrors(errors, len,
-                                        &ret->errors.errors_val,
-                                        &ret->errors.errors_len) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    if (errors && len > 0) {
-        size_t i;
-        for (i = 0; i < len; i++)
-            VIR_FREE(errors[i].disk);
-    }
-    VIR_FREE(errors);
-    return rv;
-}
-
-
-static int
-remoteDispatchNodeGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                      virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                      virNetMessageErrorPtr rerr,
-                                      remote_node_get_memory_parameters_args *args,
-                                      remote_node_get_memory_parameters_ret *ret)
-{
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    unsigned int flags;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    if (args->nparams > REMOTE_NODE_MEMORY_PARAMETERS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
-        goto cleanup;
-    }
-    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
-        goto cleanup;
-    nparams = args->nparams;
-
-    if (virNodeGetMemoryParameters(priv->conn, params, &nparams, flags) < 0)
-        goto cleanup;
-
-    /* In this case, we need to send back the number of parameters
-     * supported
-     */
-    if (args->nparams == 0) {
-        ret->nparams = nparams;
-        goto success;
-    }
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                args->flags) < 0)
-        goto cleanup;
-
- success:
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    return rv;
-}
-
-static int
-remoteDispatchNodeGetCPUMap(virNetServerPtr server ATTRIBUTE_UNUSED,
-                            virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                            virNetMessageErrorPtr rerr,
-                            remote_node_get_cpu_map_args *args,
-                            remote_node_get_cpu_map_ret *ret)
-{
-    unsigned char *cpumap = NULL;
-    unsigned int online = 0;
-    unsigned int flags;
-    int cpunum;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    flags = args->flags;
-
-    cpunum = virNodeGetCPUMap(priv->conn, args->need_map ? &cpumap : NULL,
-                              args->need_online ? &online : NULL, flags);
-    if (cpunum < 0)
-        goto cleanup;
-
-    /* 'serialize' return cpumap */
-    if (args->need_map) {
-        ret->cpumap.cpumap_len = VIR_CPU_MAPLEN(cpunum);
-        ret->cpumap.cpumap_val = (char *) cpumap;
-        cpumap = NULL;
-    }
-
-    ret->online = online;
-    ret->ret = cpunum;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    VIR_FREE(cpumap);
-    return rv;
-}
-
-static int
-lxcDispatchDomainOpenNamespace(virNetServerPtr server ATTRIBUTE_UNUSED,
-                               virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                               virNetMessageErrorPtr rerr,
-                               lxc_domain_open_namespace_args *args)
-{
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    int *fdlist = NULL;
-    int ret;
-    virDomainPtr dom = NULL;
-    size_t i;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    ret = virDomainLxcOpenNamespace(dom,
-                                    &fdlist,
-                                    args->flags);
-    if (ret < 0)
-        goto cleanup;
-
-    /* We shouldn't have received any from the client,
-     * but in case they're playing games with us, prevent
-     * a resource leak
-     */
-    for (i = 0; i < msg->nfds; i++)
-        VIR_FORCE_CLOSE(msg->fds[i]);
-    VIR_FREE(msg->fds);
-    msg->nfds = 0;
-
-    msg->fds = fdlist;
-    msg->nfds = ret;
-
-    rv = 1;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetJobStats(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                virNetServerClientPtr client,
-                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                virNetMessageErrorPtr rerr,
-                                remote_domain_get_job_stats_args *args,
-                                remote_domain_get_job_stats_ret *ret)
-{
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetJobStats(dom, &ret->type, &params,
-                             &nparams, args->flags) < 0)
-        goto cleanup;
-
-    if (nparams > REMOTE_DOMAIN_JOB_STATS_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many job stats '%d' for limit '%d'"),
-                       nparams, REMOTE_DOMAIN_JOB_STATS_MAX);
-        goto cleanup;
-    }
-
-    if (virTypedParamsSerialize(params, nparams,
-                                (virTypedParameterRemotePtr *) &ret->params.params_val,
-                                &ret->params.params_len,
-                                0) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virTypedParamsFree(params, nparams);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainMigrateBegin3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                        virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                        virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                        virNetMessageErrorPtr rerr,
-                                        remote_domain_migrate_begin3_params_args *args,
-                                        remote_domain_migrate_begin3_params_ret *ret)
-{
-    char *xml = NULL;
-    virDomainPtr dom = NULL;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many migration parameters '%d' for limit '%d'"),
-                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
-                                  args->params.params_len,
-                                  0, &params, &nparams) < 0)
-        goto cleanup;
-
-    if (!(xml = virDomainMigrateBegin3Params(dom, params, nparams,
-                                             &cookieout, &cookieoutlen,
-                                             args->flags)))
-        goto cleanup;
-
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-    ret->xml = xml;
-
-    rv = 0;
-
- cleanup:
-    virTypedParamsFree(params, nparams);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-static int
-remoteDispatchDomainMigratePrepare3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                          virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                          virNetMessageErrorPtr rerr,
-                                          remote_domain_migrate_prepare3_params_args *args,
-                                          remote_domain_migrate_prepare3_params_ret *ret)
-{
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    char **uri_out;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many migration parameters '%d' for limit '%d'"),
-                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
-        goto cleanup;
-    }
-
-    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
-                                  args->params.params_len,
-                                  0, &params, &nparams) < 0)
-        goto cleanup;
-
-    /* Wacky world of XDR ... */
-    if (VIR_ALLOC(uri_out) < 0)
-        goto cleanup;
-
-    if (virDomainMigratePrepare3Params(priv->conn, params, nparams,
-                                       args->cookie_in.cookie_in_val,
-                                       args->cookie_in.cookie_in_len,
-                                       &cookieout, &cookieoutlen,
-                                       uri_out, args->flags) < 0)
-        goto cleanup;
-
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-    ret->uri_out = !*uri_out ? NULL : uri_out;
-
-    rv = 0;
-
- cleanup:
-    virTypedParamsFree(params, nparams);
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(uri_out);
-    }
-    return rv;
-}
-
-static int
-remoteDispatchDomainMigratePrepareTunnel3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                virNetServerClientPtr client,
-                                                virNetMessagePtr msg,
-                                                virNetMessageErrorPtr rerr,
-                                                remote_domain_migrate_prepare_tunnel3_params_args *args,
-                                                remote_domain_migrate_prepare_tunnel3_params_ret *ret)
-{
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    virStreamPtr st = NULL;
-    daemonClientStreamPtr stream = NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many migration parameters '%d' for limit '%d'"),
-                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
-        goto cleanup;
-    }
-
-    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
-                                  args->params.params_len,
-                                  0, &params, &nparams) < 0)
-        goto cleanup;
-
-    if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)) ||
-        !(stream = daemonCreateClientStream(client, st, remoteProgram,
-                                            &msg->header, false)))
-        goto cleanup;
-
-    if (virDomainMigratePrepareTunnel3Params(priv->conn, st, params, nparams,
-                                             args->cookie_in.cookie_in_val,
-                                             args->cookie_in.cookie_in_len,
-                                             &cookieout, &cookieoutlen,
-                                             args->flags) < 0)
-        goto cleanup;
-
-    if (daemonAddClientStream(client, stream, false) < 0)
-        goto cleanup;
-
-    ret->cookie_out.cookie_out_val = cookieout;
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    rv = 0;
-
- cleanup:
-    virTypedParamsFree(params, nparams);
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(cookieout);
-        if (stream) {
-            virStreamAbort(st);
-            daemonFreeClientStream(client, stream);
-        } else {
-            virObjectUnref(st);
-        }
-    }
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigratePerform3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                          virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                          virNetMessageErrorPtr rerr,
-                                          remote_domain_migrate_perform3_params_args *args,
-                                          remote_domain_migrate_perform3_params_ret *ret)
-{
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    virDomainPtr dom = NULL;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    char *dconnuri;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many migration parameters '%d' for limit '%d'"),
-                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
-                                  args->params.params_len,
-                                  0, &params, &nparams) < 0)
-        goto cleanup;
-
-    dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
-
-    if (virDomainMigratePerform3Params(dom, dconnuri, params, nparams,
-                                       args->cookie_in.cookie_in_val,
-                                       args->cookie_in.cookie_in_len,
-                                       &cookieout, &cookieoutlen,
-                                       args->flags) < 0)
-        goto cleanup;
-
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-
-    rv = 0;
-
- cleanup:
-    virTypedParamsFree(params, nparams);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigrateFinish3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                         virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                         virNetMessageErrorPtr rerr,
-                                         remote_domain_migrate_finish3_params_args *args,
-                                         remote_domain_migrate_finish3_params_ret *ret)
-{
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    virDomainPtr dom = NULL;
-    char *cookieout = NULL;
-    int cookieoutlen = 0;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many migration parameters '%d' for limit '%d'"),
-                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
-        goto cleanup;
-    }
-
-    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
-                                  args->params.params_len,
-                                  0, &params, &nparams) < 0)
-        goto cleanup;
-
-    dom = virDomainMigrateFinish3Params(priv->conn, params, nparams,
-                                        args->cookie_in.cookie_in_val,
-                                        args->cookie_in.cookie_in_len,
-                                        &cookieout, &cookieoutlen,
-                                        args->flags, args->cancelled);
-    if (!dom)
-        goto cleanup;
-
-    make_nonnull_domain(&ret->dom, dom);
-
-    ret->cookie_out.cookie_out_len = cookieoutlen;
-    ret->cookie_out.cookie_out_val = cookieout;
-
-    rv = 0;
-
- cleanup:
-    virTypedParamsFree(params, nparams);
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(cookieout);
-    }
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainMigrateConfirm3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                          virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                          virNetMessageErrorPtr rerr,
-                                          remote_domain_migrate_confirm3_params_args *args)
-{
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    virDomainPtr dom = NULL;
-    int rv = -1;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many migration parameters '%d' for limit '%d'"),
-                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
-                                  args->params.params_len,
-                                  0, &params, &nparams) < 0)
-        goto cleanup;
-
-    if (virDomainMigrateConfirm3Params(dom, params, nparams,
-                                       args->cookie_in.cookie_in_val,
-                                       args->cookie_in.cookie_in_len,
-                                       args->flags, args->cancelled) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    virTypedParamsFree(params, nparams);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectGetCPUModelNames(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                      virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                      virNetMessageErrorPtr rerr,
-                                      remote_connect_get_cpu_model_names_args *args,
-                                      remote_connect_get_cpu_model_names_ret *ret)
-{
-    int len, rv = -1;
-    char **models = NULL;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    len = virConnectGetCPUModelNames(priv->conn, args->arch,
-                                     args->need_results ? &models : NULL,
-                                     args->flags);
-    if (len < 0)
-        goto cleanup;
-
-    if (len > REMOTE_CONNECT_CPU_MODELS_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many CPU models '%d' for limit '%d'"),
-                       len, REMOTE_CONNECT_CPU_MODELS_MAX);
-        goto cleanup;
-    }
-
-    if (len && models) {
-        ret->models.models_val = models;
-        ret->models.models_len = len;
-        models = NULL;
-    } else {
-        ret->models.models_val = NULL;
-        ret->models.models_len = 0;
-    }
-
-    ret->ret = len;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virStringListFree(models);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainCreateXMLWithFiles(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                       virNetServerClientPtr client,
-                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                       virNetMessageErrorPtr rerr,
-                                       remote_domain_create_xml_with_files_args *args,
-                                       remote_domain_create_xml_with_files_ret *ret)
-{
-    int rv = -1;
-    virDomainPtr dom = NULL;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    int *files = NULL;
-    unsigned int nfiles = 0;
-    size_t i;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (VIR_ALLOC_N(files, msg->nfds) < 0)
-        goto cleanup;
-    for (i = 0; i < msg->nfds; i++) {
-        if ((files[i] = virNetMessageDupFD(msg, i)) < 0)
-            goto cleanup;
-        nfiles++;
-    }
-
-    if ((dom = virDomainCreateXMLWithFiles(priv->conn, args->xml_desc,
-                                           nfiles, files,
-                                           args->flags)) == NULL)
-        goto cleanup;
-
-    make_nonnull_domain(&ret->dom, dom);
-    rv = 0;
-
- cleanup:
-    for (i = 0; i < nfiles; i++)
-        VIR_FORCE_CLOSE(files[i]);
-    VIR_FREE(files);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int remoteDispatchDomainCreateWithFiles(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                               virNetServerClientPtr client,
-                                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                               virNetMessageErrorPtr rerr,
-                                               remote_domain_create_with_files_args *args,
-                                               remote_domain_create_with_files_ret *ret)
-{
-    int rv = -1;
-    virDomainPtr dom = NULL;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    int *files = NULL;
-    unsigned int nfiles = 0;
-    size_t i;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (VIR_ALLOC_N(files, msg->nfds) < 0)
-        goto cleanup;
-    for (i = 0; i < msg->nfds; i++) {
-        if ((files[i] = virNetMessageDupFD(msg, i)) < 0)
-            goto cleanup;
-        nfiles++;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainCreateWithFiles(dom,
-                                 nfiles, files,
-                                 args->flags) < 0)
-        goto cleanup;
-
-    make_nonnull_domain(&ret->dom, dom);
-    rv = 0;
-
- cleanup:
-    for (i = 0; i < nfiles; i++)
-        VIR_FORCE_CLOSE(files[i]);
-    VIR_FREE(files);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                             virNetServerClientPtr client,
-                                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                             virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                             remote_connect_network_event_register_any_args *args,
-                                             remote_connect_network_event_register_any_ret *ret)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    virNetworkPtr net = NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    if (args->net &&
-        !(net = get_nonnull_network(priv->conn, *args->net)))
-        goto cleanup;
-
-    if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST || args->eventID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unsupported network event ID %d"), args->eventID);
-        goto cleanup;
-    }
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
-     * success, we use 'ref' to save a copy of the pointer.  */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->eventID = args->eventID;
-    callback->callbackID = -1;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->networkEventCallbacks,
-                           priv->nnetworkEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectNetworkEventRegisterAny(priv->conn,
-                                                        net,
-                                                        args->eventID,
-                                                        networkEventCallbacks[args->eventID],
-                                                        ref,
-                                                        remoteEventCallbackFree)) < 0) {
-        VIR_SHRINK_N(priv->networkEventCallbacks,
-                     priv->nnetworkEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-    ret->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(net);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                               virNetServerClientPtr client,
-                                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                               virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                               remote_connect_network_event_deregister_any_args *args)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    for (i = 0; i < priv->nnetworkEventCallbacks; i++) {
-        if (priv->networkEventCallbacks[i]->callbackID == args->callbackID)
-            break;
-    }
-    if (i == priv->nnetworkEventCallbacks) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("network event callback %d not registered"),
-                       args->callbackID);
-        goto cleanup;
-    }
-
-    if (virConnectNetworkEventDeregisterAny(priv->conn, args->callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->networkEventCallbacks, i,
-                       priv->nnetworkEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchConnectStoragePoolEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                 virNetServerClientPtr client,
-                                                 virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                 virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                                 remote_connect_storage_pool_event_register_any_args *args,
-                                                 remote_connect_storage_pool_event_register_any_ret *ret)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    virStoragePoolPtr  pool = NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    if (args->pool &&
-        !(pool = get_nonnull_storage_pool(priv->conn, *args->pool)))
-        goto cleanup;
-
-    if (args->eventID >= VIR_STORAGE_POOL_EVENT_ID_LAST || args->eventID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unsupported storage pool event ID %d"), args->eventID);
-        goto cleanup;
-    }
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
-     * success, we use 'ref' to save a copy of the pointer.  */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->eventID = args->eventID;
-    callback->callbackID = -1;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->storageEventCallbacks,
-                           priv->nstorageEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectStoragePoolEventRegisterAny(priv->conn,
-                                                            pool,
-                                                            args->eventID,
-                                                            storageEventCallbacks[args->eventID],
-                                                            ref,
-                                                            remoteEventCallbackFree)) < 0) {
-        VIR_SHRINK_N(priv->storageEventCallbacks,
-                     priv->nstorageEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-    ret->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(pool);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchConnectStoragePoolEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                               virNetServerClientPtr client,
-                                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                               virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                               remote_connect_storage_pool_event_deregister_any_args *args)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    for (i = 0; i < priv->nstorageEventCallbacks; i++) {
-        if (priv->storageEventCallbacks[i]->callbackID == args->callbackID)
-            break;
-    }
-    if (i == priv->nstorageEventCallbacks) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("storage pool event callback %d not registered"),
-                       args->callbackID);
-        goto cleanup;
-    }
-
-    if (virConnectStoragePoolEventDeregisterAny(priv->conn, args->callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->storageEventCallbacks, i,
-                       priv->nstorageEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchConnectNodeDeviceEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                virNetServerClientPtr client,
-                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                                remote_connect_node_device_event_register_any_args *args,
-                                                remote_connect_node_device_event_register_any_ret *ret)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    virNodeDevicePtr  dev = NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    if (args->dev &&
-        !(dev = get_nonnull_node_device(priv->conn, *args->dev)))
-        goto cleanup;
-
-    if (args->eventID >= VIR_NODE_DEVICE_EVENT_ID_LAST || args->eventID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unsupported node device event ID %d"), args->eventID);
-        goto cleanup;
-    }
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
-     * success, we use 'ref' to save a copy of the pointer.  */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->eventID = args->eventID;
-    callback->callbackID = -1;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->nodeDeviceEventCallbacks,
-                           priv->nnodeDeviceEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectNodeDeviceEventRegisterAny(priv->conn,
-                                                           dev,
-                                                           args->eventID,
-                                                           nodeDeviceEventCallbacks[args->eventID],
-                                                           ref,
-                                                           remoteEventCallbackFree)) < 0) {
-        VIR_SHRINK_N(priv->nodeDeviceEventCallbacks,
-                     priv->nnodeDeviceEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-    ret->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dev);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchConnectNodeDeviceEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                  virNetServerClientPtr client,
-                                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                  virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                                  remote_connect_node_device_event_deregister_any_args *args)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    for (i = 0; i < priv->nnodeDeviceEventCallbacks; i++) {
-        if (priv->nodeDeviceEventCallbacks[i]->callbackID == args->callbackID)
-            break;
-    }
-    if (i == priv->nnodeDeviceEventCallbacks) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("node device event callback %d not registered"),
-                       args->callbackID);
-        goto cleanup;
-    }
-
-    if (virConnectNodeDeviceEventDeregisterAny(priv->conn, args->callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->nodeDeviceEventCallbacks, i,
-                       priv->nnodeDeviceEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchConnectSecretEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                            virNetServerClientPtr client,
-                                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                            virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                            remote_connect_secret_event_register_any_args *args,
-                                            remote_connect_secret_event_register_any_ret *ret)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    virSecretPtr secret = NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    if (args->secret &&
-        !(secret = get_nonnull_secret(priv->conn, *args->secret)))
-        goto cleanup;
-
-    if (args->eventID >= VIR_SECRET_EVENT_ID_LAST || args->eventID < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unsupported secret event ID %d"), args->eventID);
-        goto cleanup;
-    }
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
-     * success, we use 'ref' to save a copy of the pointer.  */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->eventID = args->eventID;
-    callback->callbackID = -1;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->secretEventCallbacks,
-                           priv->nsecretEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectSecretEventRegisterAny(priv->conn,
-                                                       secret,
-                                                       args->eventID,
-                                                       secretEventCallbacks[args->eventID],
-                                                       ref,
-                                                       remoteEventCallbackFree)) < 0) {
-        VIR_SHRINK_N(priv->secretEventCallbacks,
-                     priv->nsecretEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-    ret->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(secret);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchConnectSecretEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                  virNetServerClientPtr client,
-                                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                  virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                                  remote_connect_secret_event_deregister_any_args *args)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    for (i = 0; i < priv->nsecretEventCallbacks; i++) {
-        if (priv->secretEventCallbacks[i]->callbackID == args->callbackID)
-            break;
-    }
-    if (i == priv->nsecretEventCallbacks) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("node device event callback %d not registered"),
-                       args->callbackID);
-        goto cleanup;
-    }
-
-    if (virConnectSecretEventDeregisterAny(priv->conn, args->callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->secretEventCallbacks, i,
-                       priv->nsecretEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                              virNetServerClientPtr client,
-                                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                              virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                              qemu_connect_domain_monitor_event_register_args *args,
-                                              qemu_connect_domain_monitor_event_register_ret *ret)
-{
-    int callbackID;
-    int rv = -1;
-    daemonClientEventCallbackPtr callback = NULL;
-    daemonClientEventCallbackPtr ref;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-    virDomainPtr dom = NULL;
-    const char *event = args->event ? *args->event : NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    if (args->dom &&
-        !(dom = get_nonnull_domain(priv->conn, *args->dom)))
-        goto cleanup;
-
-    /* If we call register first, we could append a complete callback
-     * to our array, but on OOM append failure, we'd have to then hope
-     * deregister works to undo our register.  So instead we append an
-     * incomplete callback to our array, then register, then fix up
-     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
-     * success, we use 'ref' to save a copy of the pointer.  */
-    if (VIR_ALLOC(callback) < 0)
-        goto cleanup;
-    callback->client = virObjectRef(client);
-    callback->callbackID = -1;
-    ref = callback;
-    if (VIR_APPEND_ELEMENT(priv->qemuEventCallbacks,
-                           priv->nqemuEventCallbacks,
-                           callback) < 0)
-        goto cleanup;
-
-    if ((callbackID = virConnectDomainQemuMonitorEventRegister(priv->conn,
-                                                               dom,
-                                                               event,
-                                                               remoteRelayDomainQemuMonitorEvent,
-                                                               ref,
-                                                               remoteEventCallbackFree,
-                                                               args->flags)) < 0) {
-        VIR_SHRINK_N(priv->qemuEventCallbacks,
-                     priv->nqemuEventCallbacks, 1);
-        callback = ref;
-        goto cleanup;
-    }
-
-    ref->callbackID = callbackID;
-    ret->callbackID = callbackID;
-
-    rv = 0;
-
- cleanup:
-    remoteEventCallbackFree(callback);
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-
-static int
-qemuDispatchConnectDomainMonitorEventDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                                virNetServerClientPtr client,
-                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                                virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
-                                                qemu_connect_domain_monitor_event_deregister_args *args)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    virMutexLock(&priv->lock);
-
-    for (i = 0; i < priv->nqemuEventCallbacks; i++) {
-        if (priv->qemuEventCallbacks[i]->callbackID == args->callbackID)
-            break;
-    }
-    if (i == priv->nqemuEventCallbacks) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("qemu monitor event callback %d not registered"),
-                       args->callbackID);
-        goto cleanup;
-    }
-
-    if (virConnectDomainQemuMonitorEventDeregister(priv->conn,
-                                                   args->callbackID) < 0)
-        goto cleanup;
-
-    VIR_DELETE_ELEMENT(priv->qemuEventCallbacks, i,
-                       priv->nqemuEventCallbacks);
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virMutexUnlock(&priv->lock);
-    return rv;
-}
-
-static int
-remoteDispatchDomainGetTime(virNetServerPtr server ATTRIBUTE_UNUSED,
-                            virNetServerClientPtr client,
-                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                            virNetMessageErrorPtr rerr,
-                            remote_domain_get_time_args *args,
-                            remote_domain_get_time_ret *ret)
-{
-    int rv = -1;
-    virDomainPtr dom = NULL;
-    long long seconds;
-    unsigned int nseconds;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if (virDomainGetTime(dom, &seconds, &nseconds, args->flags) < 0)
-        goto cleanup;
-
-    ret->seconds = seconds;
-    ret->nseconds = nseconds;
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(dom);
-    return rv;
-}
-
-
-static int
-remoteDispatchNodeGetFreePages(virNetServerPtr server ATTRIBUTE_UNUSED,
-                               virNetServerClientPtr client,
-                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                               virNetMessageErrorPtr rerr,
-                               remote_node_get_free_pages_args *args,
-                               remote_node_get_free_pages_ret *ret)
-{
-    int rv = -1;
-    int len;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->pages.pages_len * args->cellCount > REMOTE_NODE_MAX_CELLS) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("the result won't fit into REMOTE_NODE_MAX_CELLS"));
-        goto cleanup;
-    }
-
-    /* Allocate return buffer. */
-    if (VIR_ALLOC_N(ret->counts.counts_val,
-                    args->pages.pages_len * args->cellCount) < 0)
-        goto cleanup;
-
-    if ((len = virNodeGetFreePages(priv->conn,
-                                   args->pages.pages_len,
-                                   args->pages.pages_val,
-                                   args->startCell,
-                                   args->cellCount,
-                                   (unsigned long long *) ret->counts.counts_val,
-                                   args->flags)) <= 0)
-        goto cleanup;
-
-    ret->counts.counts_len = len;
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-        VIR_FREE(ret->counts.counts_val);
-    }
-    return rv;
-}
-
-/* Copy contents of virNetworkDHCPLeasePtr to remote_network_dhcp_lease */
-static int
-remoteSerializeDHCPLease(remote_network_dhcp_lease *lease_dst, virNetworkDHCPLeasePtr lease_src)
-{
-    char **mac_tmp = NULL;
-    char **iaid_tmp = NULL;
-    char **hostname_tmp = NULL;
-    char **clientid_tmp = NULL;
-
-    lease_dst->expirytime = lease_src->expirytime;
-    lease_dst->type = lease_src->type;
-    lease_dst->prefix = lease_src->prefix;
-
-    if (VIR_STRDUP(lease_dst->iface, lease_src->iface) < 0 ||
-        VIR_STRDUP(lease_dst->ipaddr, lease_src->ipaddr) < 0)
-        goto error;
-
-    if (lease_src->mac) {
-        if (VIR_ALLOC(mac_tmp) < 0 ||
-            VIR_STRDUP(*mac_tmp, lease_src->mac) < 0)
-            goto error;
-    }
-    if (lease_src->iaid) {
-        if (VIR_ALLOC(iaid_tmp) < 0 ||
-            VIR_STRDUP(*iaid_tmp, lease_src->iaid) < 0)
-            goto error;
-    }
-    if (lease_src->hostname) {
-        if (VIR_ALLOC(hostname_tmp) < 0 ||
-            VIR_STRDUP(*hostname_tmp, lease_src->hostname) < 0)
-            goto error;
-    }
-    if (lease_src->clientid) {
-        if (VIR_ALLOC(clientid_tmp) < 0 ||
-            VIR_STRDUP(*clientid_tmp, lease_src->clientid) < 0)
-            goto error;
-    }
-
-    lease_dst->mac = mac_tmp;
-    lease_dst->iaid = iaid_tmp;
-    lease_dst->hostname = hostname_tmp;
-    lease_dst->clientid = clientid_tmp;
-
-    return 0;
-
- error:
-    if (mac_tmp)
-        VIR_FREE(*mac_tmp);
-    if (iaid_tmp)
-        VIR_FREE(*iaid_tmp);
-    if (hostname_tmp)
-        VIR_FREE(*hostname_tmp);
-    if (clientid_tmp)
-        VIR_FREE(*clientid_tmp);
-    VIR_FREE(mac_tmp);
-    VIR_FREE(iaid_tmp);
-    VIR_FREE(hostname_tmp);
-    VIR_FREE(clientid_tmp);
-    VIR_FREE(lease_dst->ipaddr);
-    VIR_FREE(lease_dst->iface);
-    return -1;
-}
-
-
-static int
-remoteDispatchNetworkGetDHCPLeases(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                   virNetServerClientPtr client,
-                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                   virNetMessageErrorPtr rerr,
-                                   remote_network_get_dhcp_leases_args *args,
-                                   remote_network_get_dhcp_leases_ret *ret)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
-    virNetworkDHCPLeasePtr *leases = NULL;
-    virNetworkPtr net = NULL;
-    int nleases = 0;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(net = get_nonnull_network(priv->conn, args->net)))
-        goto cleanup;
-
-    if ((nleases = virNetworkGetDHCPLeases(net,
-                                           args->mac ? *args->mac : NULL,
-                                           args->need_results ? &leases : NULL,
-                                           args->flags)) < 0)
-        goto cleanup;
-
-    if (nleases > REMOTE_NETWORK_DHCP_LEASES_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Number of leases is %d, which exceeds max limit: %d"),
-                       nleases, REMOTE_NETWORK_DHCP_LEASES_MAX);
-        goto cleanup;
-    }
-
-    if (leases && nleases) {
-        if (VIR_ALLOC_N(ret->leases.leases_val, nleases) < 0)
-            goto cleanup;
-
-        ret->leases.leases_len = nleases;
-
-        for (i = 0; i < nleases; i++) {
-            if (remoteSerializeDHCPLease(ret->leases.leases_val + i, leases[i]) < 0)
-                goto cleanup;
-        }
-
-    } else {
-        ret->leases.leases_len = 0;
-        ret->leases.leases_val = NULL;
-    }
-
-    ret->ret = nleases;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    if (leases && nleases > 0)
-        for (i = 0; i < nleases; i++)
-            virNetworkDHCPLeaseFree(leases[i]);
-    VIR_FREE(leases);
-    virObjectUnref(net);
-    return rv;
-}
-
-
-static int
-remoteDispatchConnectGetAllDomainStats(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                       virNetServerClientPtr client,
-                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                       virNetMessageErrorPtr rerr,
-                                       remote_connect_get_all_domain_stats_args *args,
-                                       remote_connect_get_all_domain_stats_ret *ret)
-{
-    int rv = -1;
-    size_t i;
-    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
-    virDomainStatsRecordPtr *retStats = NULL;
-    int nrecords = 0;
-    virDomainPtr *doms = NULL;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (args->doms.doms_len) {
-        if (VIR_ALLOC_N(doms, args->doms.doms_len + 1) < 0)
-            goto cleanup;
-
-        for (i = 0; i < args->doms.doms_len; i++) {
-            if (!(doms[i] = get_nonnull_domain(priv->conn, args->doms.doms_val[i])))
-                goto cleanup;
-        }
-
-        if ((nrecords = virDomainListGetStats(doms,
-                                              args->stats,
-                                              &retStats,
-                                              args->flags)) < 0)
-            goto cleanup;
-    } else {
-        if ((nrecords = virConnectGetAllDomainStats(priv->conn,
-                                                    args->stats,
-                                                    &retStats,
-                                                    args->flags)) < 0)
-            goto cleanup;
-    }
-
-    if (nrecords > REMOTE_CONNECT_GET_ALL_DOMAIN_STATS_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Number of domain stats records is %d, "
-                         "which exceeds max limit: %d"),
-                       nrecords, REMOTE_CONNECT_GET_ALL_DOMAIN_STATS_MAX);
-        goto cleanup;
-    }
-
-    if (nrecords) {
-        if (VIR_ALLOC_N(ret->retStats.retStats_val, nrecords) < 0)
-            goto cleanup;
-
-        ret->retStats.retStats_len = nrecords;
-
-        for (i = 0; i < nrecords; i++) {
-            remote_domain_stats_record *dst = ret->retStats.retStats_val + i;
-
-            make_nonnull_domain(&dst->dom, retStats[i]->dom);
-
-            if (virTypedParamsSerialize(retStats[i]->params,
-                                        retStats[i]->nparams,
-                                        (virTypedParameterRemotePtr *) &dst->params.params_val,
-                                        &dst->params.params_len,
-                                        VIR_TYPED_PARAM_STRING_OKAY) < 0)
-                goto cleanup;
-        }
-    } else {
-        ret->retStats.retStats_len = 0;
-        ret->retStats.retStats_val = NULL;
-    }
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-
-    virDomainStatsRecordListFree(retStats);
-    virObjectListFree(doms);
-
-    return rv;
-}
-
-
-static int
-remoteDispatchNodeAllocPages(virNetServerPtr server ATTRIBUTE_UNUSED,
-                             virNetServerClientPtr client,
-                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                             virNetMessageErrorPtr rerr,
-                             remote_node_alloc_pages_args *args,
-                             remote_node_alloc_pages_ret *ret)
-{
-    int rv = -1;
-    int len;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if ((len = virNodeAllocPages(priv->conn,
-                                 args->pageSizes.pageSizes_len,
-                                 args->pageSizes.pageSizes_val,
-                                 (unsigned long long *) args->pageCounts.pageCounts_val,
-                                 args->startCell,
-                                 args->cellCount,
-                                 args->flags)) < 0)
-        goto cleanup;
-
-    ret->ret = len;
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    return rv;
-}
-
-
-static int
-remoteDispatchDomainGetFSInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
-                              virNetServerClientPtr client,
-                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                              virNetMessageErrorPtr rerr,
-                              remote_domain_get_fsinfo_args *args,
-                              remote_domain_get_fsinfo_ret *ret)
-{
-    int rv = -1;
-    size_t i, j;
-    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
-    virDomainFSInfoPtr *info = NULL;
-    virDomainPtr dom = NULL;
-    remote_domain_fsinfo *dst;
-    int ninfo = 0;
-    size_t ndisk;
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if ((ninfo = virDomainGetFSInfo(dom, &info, args->flags)) < 0)
-        goto cleanup;
-
-    if (ninfo > REMOTE_DOMAIN_FSINFO_MAX) {
-        virReportError(VIR_ERR_RPC,
-                       _("Too many mountpoints in fsinfo: %d for limit %d"),
-                       ninfo, REMOTE_DOMAIN_FSINFO_MAX);
-        goto cleanup;
-    }
-
-    if (ninfo) {
-        if (VIR_ALLOC_N(ret->info.info_val, ninfo) < 0)
-            goto cleanup;
-
-        ret->info.info_len = ninfo;
-
-        for (i = 0; i < ninfo; i++) {
-            dst = &ret->info.info_val[i];
-            if (VIR_STRDUP(dst->mountpoint, info[i]->mountpoint) < 0)
-                goto cleanup;
-
-            if (VIR_STRDUP(dst->name, info[i]->name) < 0)
-                goto cleanup;
-
-            if (VIR_STRDUP(dst->fstype, info[i]->fstype) < 0)
-                goto cleanup;
-
-            ndisk = info[i]->ndevAlias;
-            if (ndisk > REMOTE_DOMAIN_FSINFO_DISKS_MAX) {
-                virReportError(VIR_ERR_RPC,
-                               _("Too many disks in fsinfo: %zd for limit %d"),
-                               ndisk, REMOTE_DOMAIN_FSINFO_DISKS_MAX);
-                goto cleanup;
-            }
-
-            if (ndisk > 0) {
-                if (VIR_ALLOC_N(dst->dev_aliases.dev_aliases_val, ndisk) < 0)
-                    goto cleanup;
-
-                for (j = 0; j < ndisk; j++) {
-                    if (VIR_STRDUP(dst->dev_aliases.dev_aliases_val[j],
-                                   info[i]->devAlias[j]) < 0)
-                        goto cleanup;
-                }
-
-                dst->dev_aliases.dev_aliases_len = ndisk;
-            } else {
-                dst->dev_aliases.dev_aliases_val = NULL;
-                dst->dev_aliases.dev_aliases_len = 0;
-            }
-        }
-
-    } else {
-        ret->info.info_len = 0;
-        ret->info.info_val = NULL;
-    }
-
-    ret->ret = ninfo;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0) {
-        virNetMessageSaveError(rerr);
-
-        if (ret->info.info_val && ninfo > 0) {
-            for (i = 0; i < ninfo; i++) {
-                dst = &ret->info.info_val[i];
-                VIR_FREE(dst->mountpoint);
-                if (dst->dev_aliases.dev_aliases_val) {
-                    for (j = 0; j < dst->dev_aliases.dev_aliases_len; j++)
-                        VIR_FREE(dst->dev_aliases.dev_aliases_val[j]);
-                    VIR_FREE(dst->dev_aliases.dev_aliases_val);
-                }
-            }
-            VIR_FREE(ret->info.info_val);
-        }
-    }
-    virObjectUnref(dom);
-    if (ninfo >= 0)
-        for (i = 0; i < ninfo; i++)
-            virDomainFSInfoFree(info[i]);
-    VIR_FREE(info);
-
-    return rv;
-}
-
-
-static int
-remoteSerializeDomainInterface(virDomainInterfacePtr *ifaces,
-                               unsigned int ifaces_count,
-                               remote_domain_interface_addresses_ret *ret)
-{
-    size_t i, j;
-
-    if (ifaces_count > REMOTE_DOMAIN_INTERFACE_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Number of interfaces, %d exceeds the max limit: %d"),
-                       ifaces_count, REMOTE_DOMAIN_INTERFACE_MAX);
-        return -1;
-    }
-
-    if (VIR_ALLOC_N(ret->ifaces.ifaces_val, ifaces_count) < 0)
-        return -1;
-
-    ret->ifaces.ifaces_len = ifaces_count;
-
-    for (i = 0; i < ifaces_count; i++) {
-        virDomainInterfacePtr iface = ifaces[i];
-        remote_domain_interface *iface_ret = &(ret->ifaces.ifaces_val[i]);
-
-        if ((VIR_STRDUP(iface_ret->name, iface->name)) < 0)
-            goto cleanup;
-
-        if (iface->hwaddr &&
-            (VIR_ALLOC(iface_ret->hwaddr) < 0 ||
-             VIR_STRDUP(*iface_ret->hwaddr, iface->hwaddr) < 0))
-            goto cleanup;
-
-        if (iface->naddrs > REMOTE_DOMAIN_IP_ADDR_MAX) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Number of interfaces, %d exceeds the max limit: %d"),
-                           iface->naddrs, REMOTE_DOMAIN_IP_ADDR_MAX);
-            goto cleanup;
-        }
-
-        if (VIR_ALLOC_N(iface_ret->addrs.addrs_val,
-                        iface->naddrs) < 0)
-            goto cleanup;
-
-        iface_ret->addrs.addrs_len = iface->naddrs;
-
-        for (j = 0; j < iface->naddrs; j++) {
-            virDomainIPAddressPtr ip_addr = &(iface->addrs[j]);
-            remote_domain_ip_addr *ip_addr_ret =
-                &(iface_ret->addrs.addrs_val[j]);
-
-            if (VIR_STRDUP(ip_addr_ret->addr, ip_addr->addr) < 0)
-                goto cleanup;
-
-            ip_addr_ret->prefix = ip_addr->prefix;
-            ip_addr_ret->type = ip_addr->type;
-        }
-    }
-
-    return 0;
-
- cleanup:
-    if (ret->ifaces.ifaces_val) {
-        for (i = 0; i < ifaces_count; i++) {
-            remote_domain_interface *iface_ret = &(ret->ifaces.ifaces_val[i]);
-            VIR_FREE(iface_ret->name);
-            if (iface_ret->hwaddr) {
-                VIR_FREE(*iface_ret->hwaddr);
-                VIR_FREE(iface_ret->hwaddr);
-            }
-            for (j = 0; j < iface_ret->addrs.addrs_len; j++) {
-                remote_domain_ip_addr *ip_addr =
-                    &(iface_ret->addrs.addrs_val[j]);
-                VIR_FREE(ip_addr->addr);
-            }
-        }
-        VIR_FREE(ret->ifaces.ifaces_val);
-    }
-
-    return -1;
-}
-
-
-static int
-remoteDispatchDomainInterfaceAddresses(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                       virNetServerClientPtr client,
-                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                       virNetMessageErrorPtr rerr,
-                                       remote_domain_interface_addresses_args *args,
-                                       remote_domain_interface_addresses_ret *ret)
-{
-    size_t i;
-    int rv = -1;
-    virDomainPtr dom = NULL;
-    virDomainInterfacePtr *ifaces = NULL;
-    int ifaces_count = 0;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
-        goto cleanup;
-
-    if ((ifaces_count = virDomainInterfaceAddresses(dom, &ifaces, args->source, args->flags)) < 0)
-        goto cleanup;
-
-    if (remoteSerializeDomainInterface(ifaces, ifaces_count, ret) < 0)
-        goto cleanup;
-
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-
-    virObjectUnref(dom);
-
-    if (ifaces && ifaces_count > 0) {
-        for (i = 0; i < ifaces_count; i++)
-            virDomainInterfaceFree(ifaces[i]);
-    }
-    VIR_FREE(ifaces);
-
-    return rv;
-}
-
-
-static int
-remoteDispatchStorageVolGetInfoFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
-                                     virNetServerClientPtr client,
-                                     virNetMessagePtr msg ATTRIBUTE_UNUSED,
-                                     virNetMessageErrorPtr rerr,
-                                     remote_storage_vol_get_info_flags_args *args,
-                                     remote_storage_vol_get_info_flags_ret *ret)
-{
-    int rv = -1;
-    virStorageVolPtr vol = NULL;
-    virStorageVolInfo tmp;
-    struct daemonClientPrivate *priv =
-        virNetServerClientGetPrivateData(client);
-
-    if (!priv->conn) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
-        goto cleanup;
-    }
-
-    if (!(vol = get_nonnull_storage_vol(priv->conn, args->vol)))
-        goto cleanup;
-
-    if (virStorageVolGetInfoFlags(vol, &tmp, args->flags) < 0)
-        goto cleanup;
-
-    ret->type = tmp.type;
-    ret->capacity = tmp.capacity;
-    ret->allocation = tmp.allocation;
-    rv = 0;
-
- cleanup:
-    if (rv < 0)
-        virNetMessageSaveError(rerr);
-    virObjectUnref(vol);
-    return rv;
-}
-
-
-/*----- Helpers. -----*/
-
-/* 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)
-{
-    /* Should we believe the domain.id sent by the client?  Maybe
-     * this should be a check rather than an assignment? XXX
-     */
-    return virGetDomain(conn, domain.name, BAD_CAST domain.uuid, domain.id);
-}
-
-static virNetworkPtr
-get_nonnull_network(virConnectPtr conn, remote_nonnull_network network)
-{
-    return virGetNetwork(conn, network.name, BAD_CAST network.uuid);
-}
-
-static virInterfacePtr
-get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface)
-{
-    return virGetInterface(conn, iface.name, iface.mac);
-}
-
-static virStoragePoolPtr
-get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool)
-{
-    return virGetStoragePool(conn, pool.name, BAD_CAST pool.uuid,
-                             NULL, NULL);
-}
-
-static virStorageVolPtr
-get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol)
-{
-    virStorageVolPtr ret;
-    ret = virGetStorageVol(conn, vol.pool, vol.name, vol.key,
-                           NULL, NULL);
-    return ret;
-}
-
-static virSecretPtr
-get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret)
-{
-    return virGetSecret(conn, BAD_CAST secret.uuid, secret.usageType, secret.usageID);
-}
-
-static virNWFilterPtr
-get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter)
-{
-    return virGetNWFilter(conn, nwfilter.name, BAD_CAST nwfilter.uuid);
-}
-
-static virDomainSnapshotPtr
-get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot)
-{
-    return virGetDomainSnapshot(dom, snapshot.name);
-}
-
-static virNodeDevicePtr
-get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev)
-{
-    return virGetNodeDevice(conn, dev.name);
-}
-
-/* Make remote_nonnull_domain and remote_nonnull_network. */
-static void
-make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
-{
-    dom_dst->id = dom_src->id;
-    ignore_value(VIR_STRDUP_QUIET(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)
-{
-    ignore_value(VIR_STRDUP_QUIET(net_dst->name, net_src->name));
-    memcpy(net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN);
-}
-
-static void
-make_nonnull_interface(remote_nonnull_interface *interface_dst,
-                       virInterfacePtr interface_src)
-{
-    ignore_value(VIR_STRDUP_QUIET(interface_dst->name, interface_src->name));
-    ignore_value(VIR_STRDUP_QUIET(interface_dst->mac, interface_src->mac));
-}
-
-static void
-make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src)
-{
-    ignore_value(VIR_STRDUP_QUIET(pool_dst->name, pool_src->name));
-    memcpy(pool_dst->uuid, pool_src->uuid, VIR_UUID_BUFLEN);
-}
-
-static void
-make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src)
-{
-    ignore_value(VIR_STRDUP_QUIET(vol_dst->pool, vol_src->pool));
-    ignore_value(VIR_STRDUP_QUIET(vol_dst->name, vol_src->name));
-    ignore_value(VIR_STRDUP_QUIET(vol_dst->key, vol_src->key));
-}
-
-static void
-make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src)
-{
-    ignore_value(VIR_STRDUP_QUIET(dev_dst->name, dev_src->name));
-}
-
-static void
-make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src)
-{
-    memcpy(secret_dst->uuid, secret_src->uuid, VIR_UUID_BUFLEN);
-    secret_dst->usageType = secret_src->usageType;
-    ignore_value(VIR_STRDUP_QUIET(secret_dst->usageID, secret_src->usageID));
-}
-
-static void
-make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src)
-{
-    ignore_value(VIR_STRDUP_QUIET(nwfilter_dst->name, nwfilter_src->name));
-    memcpy(nwfilter_dst->uuid, nwfilter_src->uuid, VIR_UUID_BUFLEN);
-}
-
-static void
-make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src)
-{
-    ignore_value(VIR_STRDUP_QUIET(snapshot_dst->name, snapshot_src->name));
-    make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain);
-}
-
-static int
-remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors,
-                                int nerrors,
-                                remote_domain_disk_error **ret_errors_val,
-                                u_int *ret_errors_len)
-{
-    remote_domain_disk_error *val = NULL;
-    size_t i = 0;
-
-    if (VIR_ALLOC_N(val, nerrors) < 0)
-        goto error;
-
-    for (i = 0; i < nerrors; i++) {
-        if (VIR_STRDUP(val[i].disk, errors[i].disk) < 0)
-            goto error;
-        val[i].error = errors[i].error;
-    }
-
-    *ret_errors_len = nerrors;
-    *ret_errors_val = val;
-
-    return 0;
-
- error:
-    if (val) {
-        size_t j;
-        for (j = 0; j < i; j++)
-            VIR_FREE(val[j].disk);
-        VIR_FREE(val);
-    }
-    return -1;
-}
diff --git a/daemon/remote.h b/daemon/remote.h
deleted file mode 100644 (file)
index c1bce9b..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * remote.h: handlers for RPC method calls
- *
- * Copyright (C) 2007, 2008, 2009 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, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Richard W.M. Jones <rjones@redhat.com>
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#ifndef __LIBVIRTD_REMOTE_H__
-# define __LIBVIRTD_REMOTE_H__
-
-# include "remote_protocol.h"
-# include "rpc/virnetserverprogram.h"
-# include "rpc/virnetserverclient.h"
-
-
-extern virNetServerProgramProc remoteProcs[];
-extern size_t remoteNProcs;
-
-extern virNetServerProgramProc lxcProcs[];
-extern size_t lxcNProcs;
-
-extern virNetServerProgramProc qemuProcs[];
-extern size_t qemuNProcs;
-
-void remoteClientFree(void *data);
-void *remoteClientNew(virNetServerClientPtr client,
-                      void *opaque);
-
-#endif /* __LIBVIRTD_REMOTE_H__ */
diff --git a/daemon/stream.c b/daemon/stream.c
deleted file mode 100644 (file)
index 49682f1..0000000
+++ /dev/null
@@ -1,925 +0,0 @@
-/*
- * stream.c: APIs for managing client streams
- *
- * Copyright (C) 2009-2014 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, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-
-#include <config.h>
-
-#include "stream.h"
-#include "remote.h"
-#include "viralloc.h"
-#include "virlog.h"
-#include "virnetserverclient.h"
-#include "virerror.h"
-#include "libvirt_internal.h"
-
-#define VIR_FROM_THIS VIR_FROM_STREAMS
-
-VIR_LOG_INIT("daemon.stream");
-
-struct daemonClientStream {
-    daemonClientPrivatePtr priv;
-    int refs;
-
-    virNetServerProgramPtr prog;
-
-    virStreamPtr st;
-    int procedure;
-    unsigned int serial;
-
-    bool recvEOF;
-    bool closed;
-
-    int filterID;
-
-    virNetMessagePtr rx;
-    bool tx;
-
-    bool allowSkip;
-    size_t dataLen; /* How much data is there remaining until we see a hole */
-
-    daemonClientStreamPtr next;
-};
-
-static int
-daemonStreamHandleWrite(virNetServerClientPtr client,
-                        daemonClientStream *stream);
-static int
-daemonStreamHandleRead(virNetServerClientPtr client,
-                       daemonClientStream *stream);
-static int
-daemonStreamHandleFinish(virNetServerClientPtr client,
-                         daemonClientStream *stream,
-                         virNetMessagePtr msg);
-static int
-daemonStreamHandleAbort(virNetServerClientPtr client,
-                        daemonClientStream *stream,
-                        virNetMessagePtr msg);
-
-
-
-static void
-daemonStreamUpdateEvents(daemonClientStream *stream)
-{
-    int newEvents = 0;
-    if (stream->closed)
-        return;
-    if (stream->rx)
-        newEvents |= VIR_STREAM_EVENT_WRITABLE;
-    if (stream->tx && !stream->recvEOF)
-        newEvents |= VIR_STREAM_EVENT_READABLE;
-
-    virStreamEventUpdateCallback(stream->st, newEvents);
-}
-
-/*
- * Invoked when an outgoing data packet message has been fully sent.
- * This simply re-enables TX of further data.
- *
- * The idea is to stop the daemon growing without bound due to
- * fast stream, but slow client
- */
-static void
-daemonStreamMessageFinished(virNetMessagePtr msg,
-                            void *opaque)
-{
-    daemonClientStream *stream = opaque;
-    VIR_DEBUG("stream=%p proc=%d serial=%u",
-              stream, msg->header.proc, msg->header.serial);
-
-    stream->tx = true;
-    daemonStreamUpdateEvents(stream);
-
-    daemonFreeClientStream(NULL, stream);
-}
-
-
-/*
- * Callback that gets invoked when a stream becomes writable/readable
- */
-static void
-daemonStreamEvent(virStreamPtr st, int events, void *opaque)
-{
-    virNetServerClientPtr client = opaque;
-    daemonClientStream *stream;
-    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
-
-    virMutexLock(&priv->lock);
-
-    stream = priv->streams;
-    while (stream) {
-        if (stream->st == st)
-            break;
-        stream = stream->next;
-    }
-
-    if (!stream) {
-        VIR_WARN("event for client=%p stream st=%p, but missing stream state", client, st);
-        virStreamEventRemoveCallback(st);
-        goto cleanup;
-    }
-
-    VIR_DEBUG("st=%p events=%d EOF=%d closed=%d", st, events, stream->recvEOF, stream->closed);
-
-    if (!stream->closed &&
-        (events & VIR_STREAM_EVENT_WRITABLE)) {
-        if (daemonStreamHandleWrite(client, stream) < 0) {
-            daemonRemoveClientStream(client, stream);
-            virNetServerClientClose(client);
-            goto cleanup;
-        }
-    }
-
-    if (!stream->closed && !stream->recvEOF &&
-        (events & (VIR_STREAM_EVENT_READABLE))) {
-        events = events & ~(VIR_STREAM_EVENT_READABLE);
-        if (daemonStreamHandleRead(client, stream) < 0) {
-            daemonRemoveClientStream(client, stream);
-            virNetServerClientClose(client);
-            goto cleanup;
-        }
-        /* If we detected EOF during read processing,
-         * then clear hangup/error conditions, since
-         * we want the client to see the EOF message
-         * we just sent them
-         */
-        if (stream->recvEOF)
-            events = events & ~(VIR_STREAM_EVENT_HANGUP |
-                                VIR_STREAM_EVENT_ERROR);
-    }
-
-    /* If we have a completion/abort message, always process it */
-    if (stream->rx) {
-        virNetMessagePtr msg = stream->rx;
-        switch (msg->header.status) {
-        case VIR_NET_CONTINUE:
-            /* nada */
-            break;
-        case VIR_NET_OK:
-            virNetMessageQueueServe(&stream->rx);
-            if (daemonStreamHandleFinish(client, stream, msg) < 0) {
-                virNetMessageFree(msg);
-                daemonRemoveClientStream(client, stream);
-                virNetServerClientClose(client);
-                goto cleanup;
-            }
-            break;
-        case VIR_NET_ERROR:
-        default:
-            virNetMessageQueueServe(&stream->rx);
-            if (daemonStreamHandleAbort(client, stream, msg) < 0) {
-                virNetMessageFree(msg);
-                daemonRemoveClientStream(client, stream);
-                virNetServerClientClose(client);
-                goto cleanup;
-            }
-            break;
-        }
-    }
-
-
-    /* If we got HANGUP, we need to only send an empty
-     * packet so the client sees an EOF and cleans up
-     */
-    if (!stream->closed && !stream->recvEOF &&
-        (events & VIR_STREAM_EVENT_HANGUP)) {
-        virNetMessagePtr msg;
-        events &= ~(VIR_STREAM_EVENT_HANGUP);
-        stream->tx = false;
-        stream->recvEOF = true;
-        if (!(msg = virNetMessageNew(false))) {
-            daemonRemoveClientStream(client, stream);
-            virNetServerClientClose(client);
-            goto cleanup;
-        }
-        msg->cb = daemonStreamMessageFinished;
-        msg->opaque = stream;
-        stream->refs++;
-        if (virNetServerProgramSendStreamData(remoteProgram,
-                                              client,
-                                              msg,
-                                              stream->procedure,
-                                              stream->serial,
-                                              "", 0) < 0) {
-            virNetMessageFree(msg);
-            daemonRemoveClientStream(client, stream);
-            virNetServerClientClose(client);
-            goto cleanup;
-        }
-    }
-
-    if (!stream->closed &&
-        (events & (VIR_STREAM_EVENT_ERROR | VIR_STREAM_EVENT_HANGUP))) {
-        int ret;
-        virNetMessagePtr msg;
-        virNetMessageError rerr;
-        virErrorPtr origErr = virSaveLastError();
-
-        memset(&rerr, 0, sizeof(rerr));
-        stream->closed = true;
-        virStreamEventRemoveCallback(stream->st);
-        virStreamAbort(stream->st);
-        if (origErr && origErr->code != VIR_ERR_OK) {
-            virSetError(origErr);
-        } else {
-            if (events & VIR_STREAM_EVENT_HANGUP)
-                virReportError(VIR_ERR_RPC,
-                               "%s", _("stream had unexpected termination"));
-            else
-                virReportError(VIR_ERR_RPC,
-                               "%s", _("stream had I/O failure"));
-        }
-        virFreeError(origErr);
-
-        msg = virNetMessageNew(false);
-        if (!msg) {
-            ret = -1;
-        } else {
-            ret = virNetServerProgramSendStreamError(remoteProgram,
-                                                     client,
-                                                     msg,
-                                                     &rerr,
-                                                     stream->procedure,
-                                                     stream->serial);
-        }
-        daemonRemoveClientStream(client, stream);
-        if (ret < 0)
-            virNetServerClientClose(client);
-        goto cleanup;
-    }
-
-    if (stream->closed) {
-        daemonRemoveClientStream(client, stream);
-    } else {
-        daemonStreamUpdateEvents(stream);
-    }
-
- cleanup:
-    virMutexUnlock(&priv->lock);
-}
-
-
-/*
- * @client: a locked client object
- *
- * Invoked by the main loop when filtering incoming messages.
- *
- * Returns 1 if the message was processed, 0 if skipped,
- * -1 on fatal client error
- */
-static int
-daemonStreamFilter(virNetServerClientPtr client ATTRIBUTE_UNUSED,
-                   virNetMessagePtr msg,
-                   void *opaque)
-{
-    daemonClientStream *stream = opaque;
-    int ret = 0;
-
-    virMutexLock(&stream->priv->lock);
-
-    if (msg->header.type != VIR_NET_STREAM &&
-        msg->header.type != VIR_NET_STREAM_HOLE)
-        goto cleanup;
-
-    if (!virNetServerProgramMatches(stream->prog, msg))
-        goto cleanup;
-
-    if (msg->header.proc != stream->procedure ||
-        msg->header.serial != stream->serial)
-        goto cleanup;
-
-    VIR_DEBUG("Incoming client=%p, rx=%p, serial=%u, proc=%d, status=%d",
-              client, stream->rx, msg->header.proc,
-              msg->header.serial, msg->header.status);
-
-    virNetMessageQueuePush(&stream->rx, msg);
-    daemonStreamUpdateEvents(stream);
-    ret = 1;
-
- cleanup:
-    virMutexUnlock(&stream->priv->lock);
-    return ret;
-}
-
-
-/*
- * @conn: a connection object to associate the stream with
- * @header: the method call to associate with the stream
- *
- * Creates a new stream for this conn
- *
- * Returns a new stream object, or NULL upon OOM
- */
-daemonClientStream *
-daemonCreateClientStream(virNetServerClientPtr client,
-                         virStreamPtr st,
-                         virNetServerProgramPtr prog,
-                         virNetMessageHeaderPtr header,
-                         bool allowSkip)
-{
-    daemonClientStream *stream;
-    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
-
-    VIR_DEBUG("client=%p, proc=%d, serial=%u, st=%p",
-              client, header->proc, header->serial, st);
-
-    if (VIR_ALLOC(stream) < 0)
-        return NULL;
-
-    stream->refs = 1;
-    stream->priv = priv;
-    stream->prog = virObjectRef(prog);
-    stream->procedure = header->proc;
-    stream->serial = header->serial;
-    stream->filterID = -1;
-    stream->st = st;
-    stream->allowSkip = allowSkip;
-
-    return stream;
-}
-
-/*
- * @stream: an unused client stream
- *
- * Frees the memory associated with this inactive client
- * stream
- */
-int daemonFreeClientStream(virNetServerClientPtr client,
-                           daemonClientStream *stream)
-{
-    virNetMessagePtr msg;
-    int ret = 0;
-
-    if (!stream)
-        return 0;
-
-    stream->refs--;
-    if (stream->refs)
-        return 0;
-
-    VIR_DEBUG("client=%p, proc=%d, serial=%u",
-              client, stream->procedure, stream->serial);
-
-    virObjectUnref(stream->prog);
-
-    msg = stream->rx;
-    while (msg) {
-        virNetMessagePtr tmp = msg->next;
-        if (client) {
-            /* Send a dummy reply to free up 'msg' & unblock client rx */
-            virNetMessageClear(msg);
-            msg->header.type = VIR_NET_REPLY;
-            if (virNetServerClientSendMessage(client, msg) < 0) {
-                virNetServerClientImmediateClose(client);
-                virNetMessageFree(msg);
-                ret = -1;
-            }
-        } else {
-            virNetMessageFree(msg);
-        }
-        msg = tmp;
-    }
-
-    virObjectUnref(stream->st);
-    VIR_FREE(stream);
-
-    return ret;
-}
-
-
-/*
- * @client: a locked client to add the stream to
- * @stream: a stream to add
- */
-int daemonAddClientStream(virNetServerClientPtr client,
-                          daemonClientStream *stream,
-                          bool transmit)
-{
-    VIR_DEBUG("client=%p, proc=%d, serial=%u, st=%p, transmit=%d",
-              client, stream->procedure, stream->serial, stream->st, transmit);
-    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
-
-    if (stream->filterID != -1) {
-        VIR_WARN("Filter already added to client %p", client);
-        return -1;
-    }
-
-    if (virStreamEventAddCallback(stream->st, 0,
-                                  daemonStreamEvent, client,
-                                  virObjectFreeCallback) < 0)
-        return -1;
-
-    virObjectRef(client);
-
-    if ((stream->filterID = virNetServerClientAddFilter(client,
-                                                        daemonStreamFilter,
-                                                        stream)) < 0) {
-        virStreamEventRemoveCallback(stream->st);
-        return -1;
-    }
-
-    if (transmit)
-        stream->tx = true;
-
-    virMutexLock(&priv->lock);
-    stream->next = priv->streams;
-    priv->streams = stream;
-
-    daemonStreamUpdateEvents(stream);
-
-    virMutexUnlock(&priv->lock);
-
-    return 0;
-}
-
-
-/*
- * @client: a locked client object
- * @stream: an inactive, closed stream object
- *
- * Removes a stream from the list of active streams for the client
- *
- * Returns 0 if the stream was removed, -1 if it doesn't exist
- */
-int
-daemonRemoveClientStream(virNetServerClientPtr client,
-                         daemonClientStream *stream)
-{
-    VIR_DEBUG("client=%p, proc=%d, serial=%u, st=%p",
-              client, stream->procedure, stream->serial, stream->st);
-    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
-    daemonClientStream *curr = priv->streams;
-    daemonClientStream *prev = NULL;
-
-    if (stream->filterID != -1) {
-        virNetServerClientRemoveFilter(client,
-                                       stream->filterID);
-        stream->filterID = -1;
-    }
-
-    if (!stream->closed) {
-        stream->closed = true;
-        virStreamEventRemoveCallback(stream->st);
-        virStreamAbort(stream->st);
-    }
-
-    while (curr) {
-        if (curr == stream) {
-            if (prev)
-                prev->next = curr->next;
-            else
-                priv->streams = curr->next;
-            return daemonFreeClientStream(client, stream);
-        }
-        prev = curr;
-        curr = curr->next;
-    }
-    return -1;
-}
-
-
-void
-daemonRemoveAllClientStreams(daemonClientStream *stream)
-{
-    daemonClientStream *tmp;
-
-    VIR_DEBUG("stream=%p", stream);
-
-    while (stream) {
-        tmp = stream->next;
-
-        if (!stream->closed) {
-            stream->closed = true;
-            virStreamEventRemoveCallback(stream->st);
-            virStreamAbort(stream->st);
-        }
-
-        daemonFreeClientStream(NULL, stream);
-
-        VIR_DEBUG("next stream=%p", tmp);
-        stream = tmp;
-    }
-}
-
-/*
- * Returns:
- *   -1  if fatal error occurred
- *    0  if message was fully processed
- *    1  if message is still being processed
- */
-static int
-daemonStreamHandleWriteData(virNetServerClientPtr client,
-                            daemonClientStream *stream,
-                            virNetMessagePtr msg)
-{
-    int ret;
-
-    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u, len=%zu, offset=%zu",
-              client, stream, msg->header.proc, msg->header.serial,
-              msg->bufferLength, msg->bufferOffset);
-
-    ret = virStreamSend(stream->st,
-                        msg->buffer + msg->bufferOffset,
-                        msg->bufferLength - msg->bufferOffset);
-
-    if (ret > 0) {
-        msg->bufferOffset += ret;
-
-        /* Partial write, so indicate we have more todo later */
-        if (msg->bufferOffset < msg->bufferLength)
-            return 1;
-    } else if (ret == -2) {
-        /* Blocking, so indicate we have more todo later */
-        return 1;
-    } else {
-        virNetMessageError rerr;
-
-        memset(&rerr, 0, sizeof(rerr));
-
-        VIR_INFO("Stream send failed");
-        stream->closed = true;
-        virStreamEventRemoveCallback(stream->st);
-        virStreamAbort(stream->st);
-
-        return virNetServerProgramSendReplyError(stream->prog,
-                                                 client,
-                                                 msg,
-                                                 &rerr,
-                                                 &msg->header);
-    }
-
-    return 0;
-}
-
-
-/*
- * Process a finish handshake from the client.
- *
- * Returns a VIR_NET_OK confirmation if successful, or a VIR_NET_ERROR
- * if there was a stream error
- *
- * Returns 0 if successfully sent RPC reply, -1 upon fatal error
- */
-static int
-daemonStreamHandleFinish(virNetServerClientPtr client,
-                         daemonClientStream *stream,
-                         virNetMessagePtr msg)
-{
-    int ret;
-
-    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u",
-              client, stream, msg->header.proc, msg->header.serial);
-
-    stream->closed = true;
-    virStreamEventRemoveCallback(stream->st);
-    ret = virStreamFinish(stream->st);
-
-    if (ret < 0) {
-        virNetMessageError rerr;
-        memset(&rerr, 0, sizeof(rerr));
-        return virNetServerProgramSendReplyError(stream->prog,
-                                                 client,
-                                                 msg,
-                                                 &rerr,
-                                                 &msg->header);
-    } else {
-        /* Send zero-length confirm */
-        return virNetServerProgramSendStreamData(stream->prog,
-                                                 client,
-                                                 msg,
-                                                 stream->procedure,
-                                                 stream->serial,
-                                                 NULL, 0);
-    }
-}
-
-
-/*
- * Process an abort request from the client.
- *
- * Returns 0 if successfully aborted, -1 upon error
- */
-static int
-daemonStreamHandleAbort(virNetServerClientPtr client,
-                        daemonClientStream *stream,
-                        virNetMessagePtr msg)
-{
-    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u",
-              client, stream, msg->header.proc, msg->header.serial);
-    int ret;
-    bool raise_error = false;
-
-    stream->closed = true;
-    virStreamEventRemoveCallback(stream->st);
-    ret = virStreamAbort(stream->st);
-
-    if (msg->header.status == VIR_NET_ERROR) {
-        VIR_INFO("stream aborted at client request");
-        raise_error = (ret < 0);
-    } else {
-        virReportError(VIR_ERR_RPC,
-                       _("stream aborted with unexpected status %d"),
-                       msg->header.status);
-        raise_error = true;
-    }
-
-    if (raise_error) {
-        virNetMessageError rerr;
-        memset(&rerr, 0, sizeof(rerr));
-        return virNetServerProgramSendReplyError(remoteProgram,
-                                                 client,
-                                                 msg,
-                                                 &rerr,
-                                                 &msg->header);
-    } else {
-        /* Send zero-length confirm */
-        return virNetServerProgramSendStreamData(stream->prog,
-                                                 client,
-                                                 msg,
-                                                 stream->procedure,
-                                                 stream->serial,
-                                                 NULL, 0);
-    }
-}
-
-
-static int
-daemonStreamHandleHole(virNetServerClientPtr client,
-                       daemonClientStream *stream,
-                       virNetMessagePtr msg)
-{
-    int ret;
-    virNetStreamHole data;
-
-    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u",
-              client, stream, msg->header.proc, msg->header.serial);
-
-    /* Let's check if client plays nicely and advertised usage of
-     * sparse stream upfront. */
-    if (!stream->allowSkip) {
-        virReportError(VIR_ERR_RPC, "%s",
-                       _("Unexpected stream hole"));
-        return -1;
-    }
-
-    if (virNetMessageDecodePayload(msg,
-                                   (xdrproc_t) xdr_virNetStreamHole,
-                                   &data) < 0)
-        return -1;
-
-    ret = virStreamSendHole(stream->st, data.length, data.flags);
-
-    if (ret < 0) {
-        virNetMessageError rerr;
-
-        memset(&rerr, 0, sizeof(rerr));
-
-        VIR_INFO("Stream send hole failed");
-        stream->closed = true;
-        virStreamEventRemoveCallback(stream->st);
-        virStreamAbort(stream->st);
-
-        return virNetServerProgramSendReplyError(stream->prog,
-                                                 client,
-                                                 msg,
-                                                 &rerr,
-                                                 &msg->header);
-    }
-
-    return 0;
-}
-
-
-/*
- * Called when the stream is signalled has being able to accept
- * data writes. Will process all pending incoming messages
- * until they're all gone, or I/O blocks
- *
- * Returns 0 on success, or -1 upon fatal error
- */
-static int
-daemonStreamHandleWrite(virNetServerClientPtr client,
-                        daemonClientStream *stream)
-{
-    VIR_DEBUG("client=%p, stream=%p", client, stream);
-
-    while (stream->rx && !stream->closed) {
-        virNetMessagePtr msg = stream->rx;
-        int ret;
-
-        if (msg->header.type == VIR_NET_STREAM_HOLE) {
-            /* Handle special case when the client sent us a hole.
-             * Otherwise just carry on with processing stream
-             * data. */
-            ret = daemonStreamHandleHole(client, stream, msg);
-        } else if (msg->header.type == VIR_NET_STREAM) {
-            switch (msg->header.status) {
-            case VIR_NET_OK:
-                ret = daemonStreamHandleFinish(client, stream, msg);
-                break;
-
-            case VIR_NET_CONTINUE:
-                ret = daemonStreamHandleWriteData(client, stream, msg);
-                break;
-
-            case VIR_NET_ERROR:
-            default:
-                ret = daemonStreamHandleAbort(client, stream, msg);
-                break;
-            }
-        } else {
-            virReportError(VIR_ERR_RPC,
-                           _("Unexpected message type: %d"),
-                           msg->header.type);
-            ret = -1;
-        }
-
-        if (ret > 0)
-            break;  /* still processing data from msg */
-
-        virNetMessageQueueServe(&stream->rx);
-        if (ret < 0) {
-            virNetMessageFree(msg);
-            virNetServerClientImmediateClose(client);
-            return -1;
-        }
-
-        /* 'CONTINUE' messages don't send a reply (unless error
-         * occurred), so to release the 'msg' object we need to
-         * send a fake zero-length reply. Nothing actually gets
-         * onto the wire, but this causes the client to reset
-         * its active request count / throttling
-         */
-        if (msg->header.status == VIR_NET_CONTINUE) {
-            virNetMessageClear(msg);
-            msg->header.type = VIR_NET_REPLY;
-            if (virNetServerClientSendMessage(client, msg) < 0) {
-                virNetMessageFree(msg);
-                virNetServerClientImmediateClose(client);
-                return -1;
-            }
-        }
-    }
-
-    return 0;
-}
-
-
-
-/*
- * Invoked when a stream is signalled as having data
- * available to read. This reads up to one message
- * worth of data, and then queues that for transmission
- * to the client.
- *
- * Returns 0 if data was queued for TX, or an error RPC
- * was sent, or -1 on fatal error, indicating client should
- * be killed
- */
-static int
-daemonStreamHandleRead(virNetServerClientPtr client,
-                       daemonClientStream *stream)
-{
-    virNetMessagePtr msg = NULL;
-    virNetMessageError rerr;
-    char *buffer;
-    size_t bufferLen = VIR_NET_MESSAGE_LEGACY_PAYLOAD_MAX;
-    int ret = -1;
-    int rv;
-    int inData = 0;
-    long long length = 0;
-
-    VIR_DEBUG("client=%p, stream=%p tx=%d closed=%d",
-              client, stream, stream->tx, stream->closed);
-
-    /* We might have had an event pending before we shut
-     * down the stream, so if we're marked as closed,
-     * then do nothing
-     */
-    if (stream->closed)
-        return 0;
-
-    /* Shouldn't ever be called unless we're marked able to
-     * transmit, but doesn't hurt to check */
-    if (!stream->tx)
-        return 0;
-
-    memset(&rerr, 0, sizeof(rerr));
-
-    if (VIR_ALLOC_N(buffer, bufferLen) < 0)
-        return -1;
-
-    if (!(msg = virNetMessageNew(false)))
-        goto cleanup;
-
-    if (stream->allowSkip && stream->dataLen == 0) {
-        /* Handle skip. We want to send some data to the client. But we might
-         * be in a hole. Seek to next data. But if we are in data already, just
-         * carry on. */
-
-        rv = virStreamInData(stream->st, &inData, &length);
-        VIR_DEBUG("rv=%d inData=%d length=%lld", rv, inData, length);
-
-        if (rv < 0) {
-            if (virNetServerProgramSendStreamError(remoteProgram,
-                                                   client,
-                                                   msg,
-                                                   &rerr,
-                                                   stream->procedure,
-                                                   stream->serial) < 0)
-                goto cleanup;
-            msg = NULL;
-
-            /* We're done with this call */
-            goto done;
-        } else {
-            if (!inData && length) {
-                stream->tx = false;
-                msg->cb = daemonStreamMessageFinished;
-                msg->opaque = stream;
-                stream->refs++;
-                if (virNetServerProgramSendStreamHole(remoteProgram,
-                                                      client,
-                                                      msg,
-                                                      stream->procedure,
-                                                      stream->serial,
-                                                      length,
-                                                      0) < 0)
-                    goto cleanup;
-
-                msg = NULL;
-
-                /* We have successfully sent stream skip to the other side.
-                 * To keep streams in sync seek locally too. */
-                virStreamSendHole(stream->st, length, 0);
-                /* We're done with this call */
-                goto done;
-            }
-        }
-
-        stream->dataLen = length;
-    }
-
-    if (stream->allowSkip &&
-        bufferLen > stream->dataLen)
-        bufferLen = stream->dataLen;
-
-    rv = virStreamRecv(stream->st, buffer, bufferLen);
-    if (rv == -2) {
-        /* Should never get this, since we're only called when we know
-         * we're readable, but hey things change... */
-    } else if (rv < 0) {
-        if (virNetServerProgramSendStreamError(remoteProgram,
-                                               client,
-                                               msg,
-                                               &rerr,
-                                               stream->procedure,
-                                               stream->serial) < 0)
-            goto cleanup;
-        msg = NULL;
-    } else {
-        if (stream->allowSkip)
-            stream->dataLen -= rv;
-
-        stream->tx = false;
-        if (rv == 0)
-            stream->recvEOF = true;
-
-        msg->cb = daemonStreamMessageFinished;
-        msg->opaque = stream;
-        stream->refs++;
-        if (virNetServerProgramSendStreamData(remoteProgram,
-                                              client,
-                                              msg,
-                                              stream->procedure,
-                                              stream->serial,
-                                              buffer, rv) < 0)
-            goto cleanup;
-        msg = NULL;
-    }
-
- done:
-    ret = 0;
- cleanup:
-    VIR_FREE(buffer);
-    virNetMessageFree(msg);
-    return ret;
-}
diff --git a/daemon/stream.h b/daemon/stream.h
deleted file mode 100644 (file)
index e1f1067..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * stream.h: APIs for managing client streams
- *
- * Copyright (C) 2009 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, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-
-#ifndef __LIBVIRTD_STREAM_H__
-# define __LIBVIRTD_STREAM_H__
-
-# include "libvirtd.h"
-
-daemonClientStream *
-daemonCreateClientStream(virNetServerClientPtr client,
-                         virStreamPtr st,
-                         virNetServerProgramPtr prog,
-                         virNetMessageHeaderPtr hdr,
-                         bool allowSkip);
-
-int daemonFreeClientStream(virNetServerClientPtr client,
-                           daemonClientStream *stream);
-
-int daemonAddClientStream(virNetServerClientPtr client,
-                          daemonClientStream *stream,
-                          bool transmit);
-
-int
-daemonRemoveClientStream(virNetServerClientPtr client,
-                         daemonClientStream *stream);
-
-void
-daemonRemoveAllClientStreams(daemonClientStream *stream);
-
-#endif /* __LIBVIRTD_STREAM_H__ */
index 28595546905c167a76bee3a0028ce2ce41be18d6..cfdd4ebdd100929e2c681b859d0cbefd99a34f88 100644 (file)
@@ -1,9 +1,3 @@
-daemon/libvirtd-config.c
-daemon/libvirtd.c
-daemon/qemu_dispatch.h
-daemon/remote.c
-daemon/remote_dispatch.h
-daemon/stream.c
 gnulib/lib/gai_strerror.c
 gnulib/lib/getopt.c
 gnulib/lib/regcomp.c
@@ -148,6 +142,12 @@ src/qemu/qemu_monitor_text.c
 src/qemu/qemu_parse_command.c
 src/qemu/qemu_process.c
 src/remote/remote_client_bodies.h
+src/remote/remote_daemon.c
+src/remote/remote_daemon_config.c
+src/remote/remote_daemon_dispatch.c
+src/remote/remote_daemon_dispatch_stubs.h
+src/remote/remote_daemon_dispatch_qemu_stubs.h
+src/remote/remote_daemon_stream.c
 src/remote/remote_driver.c
 src/rpc/virkeepalive.c
 src/rpc/virnetclient.c
index 9fc4d876aea6576ebef10f81b352ff3137c3c6a2..7bdcb1113aba2f9ab4f7d8ec4b042f244d1592bf 100644 (file)
@@ -85,6 +85,7 @@ lib_LTLIBRARIES = libvirt.la libvirt-qemu.la libvirt-lxc.la
 
 moddir = $(libdir)/libvirt/connection-driver
 mod_LTLIBRARIES =
+sbin_PROGRAMS =
 
 confdir = $(sysconfdir)/libvirt
 conf_DATA = libvirt.conf libvirt-admin.conf
@@ -537,12 +538,45 @@ remote/qemu_client_bodies.h: $(srcdir)/rpc/gendispatch.pl \
          qemu QEMU $(QEMU_PROTOCOL) \
          > $(srcdir)/remote/qemu_client_bodies.h
 
+remote/remote_daemon_dispatch_stubs.h: $(srcdir)/rpc/gendispatch.pl \
+               $(REMOTE_PROTOCOL) Makefile.am
+       $(AM_V_GEN)$(PERL) -w $(top_srcdir)/src/rpc/gendispatch.pl \
+         --mode=server remote REMOTE $(REMOTE_PROTOCOL) \
+         > $(srcdir)/remote/remote_daemon_dispatch_stubs.h
+
+remote/remote_daemon_dispatch_lxc_stubs.h: $(srcdir)/rpc/gendispatch.pl \
+               $(LXC_PROTOCOL) Makefile.am
+       $(AM_V_GEN)$(PERL) -w $(top_srcdir)/src/rpc/gendispatch.pl \
+         --mode=server lxc LXC $(LXC_PROTOCOL) \
+         > $(srcdir)/remote/remote_daemon_dispatch_lxc_stubs.h
+
+remote/remote_daemon_dispatch_qemu_stubs.h: $(srcdir)/rpc/gendispatch.pl \
+               $(QEMU_PROTOCOL) Makefile.am
+       $(AM_V_GEN)$(PERL) -w $(top_srcdir)/src/rpc/gendispatch.pl \
+         --mode=server qemu QEMU $(QEMU_PROTOCOL) \
+         > $(srcdir)/remote/remote_daemon_dispatch_qemu_stubs.h
+
 REMOTE_DRIVER_SOURCES = \
                remote/remote_driver.c remote/remote_driver.h \
                $(REMOTE_DRIVER_GENERATED)
 
+LIBVIRTD_GENERATED = \
+               remote/remote_daemon_dispatch_stubs.h \
+               remote/remote_daemon_dispatch_lxc_stubs.h \
+               remote/remote_daemon_dispatch_qemu_stubs.h \
+               $(NULL)
+
+LIBVIRTD_SOURCES = \
+               remote/remote_daemon.c remote/remote_daemon.h \
+               remote/remote_daemon_config.c remote/remote_daemon_config.h \
+               remote/remote_daemon_dispatch.c remote/remote_daemon_dispatch.h \
+               remote/remote_daemon_stream.c remote/remote_daemon_stream.h \
+               $(LIBVIRTD_GENERATED)
+
 EXTRA_DIST +=  $(REMOTE_DRIVER_PROTOCOL) \
-               $(REMOTE_DRIVER_GENERATED)
+               $(REMOTE_DRIVER_GENERATED) \
+               $(LIBVIRTD_SOURCES) \
+               $(NULL)
 
 ADMIN_PROTOCOL = $(srcdir)/admin/admin_protocol.x
 
@@ -1329,6 +1363,55 @@ BUILT_SOURCES += $(REMOTE_DRIVER_GENERATED)
 
 endif WITH_REMOTE
 
+
+if WITH_LIBVIRTD
+
+sbin_PROGRAMS += libvirtd
+
+BUILT_SOURCES += $(LIBVIRTD_GENERATED)
+
+libvirtd_SOURCES = $(LIBVIRTD_SOURCES)
+
+libvirtd_CFLAGS = \
+       $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(SASL_CFLAGS) \
+       $(XDR_CFLAGS) $(DBUS_CFLAGS) $(LIBNL_CFLAGS) \
+       $(WARN_CFLAGS) $(PIE_CFLAGS) \
+       $(COVERAGE_CFLAGS) \
+       -I$(srcdir)/access \
+       -I$(srcdir)/conf \
+       -I$(srcdir)/rpc \
+       $(NULL)
+
+libvirtd_LDFLAGS = \
+       $(RELRO_LDFLAGS) \
+       $(PIE_LDFLAGS) \
+       $(COVERAGE_LDFLAGS) \
+       $(NO_INDIRECT_LDFLAGS) \
+       $(NO_UNDEFINED_LDFLAGS) \
+       $(NULL)
+
+libvirtd_LDADD = \
+       $(LIBXML_LIBS) \
+       $(GNUTLS_LIBS) \
+       $(SASL_LIBS) \
+       $(DBUS_LIBS) \
+       $(LIBNL_LIBS) \
+       $(NULL)
+
+if WITH_DTRACE_PROBES
+libvirtd_LDADD += ../src/libvirt_probes.lo
+endif WITH_DTRACE_PROBES
+
+libvirtd_LDADD += \
+       libvirt_driver_admin.la \
+       libvirt-lxc.la \
+       libvirt-qemu.la \
+       libvirt.la \
+       ../gnulib/lib/libgnu.la $(LIBSOCKET) \
+       $(NULL)
+
+endif WITH_LIBVIRTD
+
 %protocol.c: %protocol.x %protocol.h $(srcdir)/rpc/genprotocol.pl
        $(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/genprotocol.pl $(RPCGEN) -c \
               $< $(srcdir)/$(subst $(srcdir)/,,$@)
@@ -2620,7 +2703,7 @@ locking/%-lockd.conf: $(srcdir)/locking/lockd.conf
        cp $< $@
 
 
-sbin_PROGRAMS = virtlockd virtlogd
+sbin_PROGRAMS += virtlockd virtlogd
 
 virtlockd_SOURCES = \
                $(LOCK_DAEMON_SOURCES) \
@@ -3370,4 +3453,8 @@ endif WITH_NETWORK
 
 CLEANFILES += *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda *.i *.s
 DISTCLEANFILES += $(GENERATED_SYM_FILES)
-MAINTAINERCLEANFILES += $(REMOTE_DRIVER_GENERATED) $(VIR_NET_RPC_GENERATED)
+MAINTAINERCLEANFILES += \
+       $(REMOTE_DRIVER_GENERATED) \
+       $(LIBVIRTD_GENERATED) \
+       $(VIR_NET_RPC_GENERATED) \
+       $(NULL)
diff --git a/src/remote/remote_daemon.c b/src/remote/remote_daemon.c
new file mode 100644 (file)
index 0000000..f8082f6
--- /dev/null
@@ -0,0 +1,1550 @@
+/*
+ * remote_daemon.c: daemon start of day, guest process & i/o management
+ *
+ * Copyright (C) 2006-2018 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <grp.h>
+
+#include "libvirt_internal.h"
+#include "virerror.h"
+#include "virfile.h"
+#include "virlog.h"
+#include "virpidfile.h"
+#include "virprocess.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+#include "remote_daemon.h"
+#include "remote_daemon_config.h"
+
+#include "admin/admin_server_dispatch.h"
+#include "viruuid.h"
+#include "remote_driver.h"
+#include "viralloc.h"
+#include "virconf.h"
+#include "virnetlink.h"
+#include "virnetdaemon.h"
+#include "remote_daemon_dispatch.h"
+#include "virhook.h"
+#include "viraudit.h"
+#include "virstring.h"
+#include "locking/lock_manager.h"
+#include "viraccessmanager.h"
+#include "virutil.h"
+#include "virgettext.h"
+#include "util/virnetdevopenvswitch.h"
+
+#include "driver.h"
+
+#include "configmake.h"
+
+#include "virdbus.h"
+
+VIR_LOG_INIT("daemon.libvirtd");
+
+#if WITH_SASL
+virNetSASLContextPtr saslCtxt = NULL;
+#endif
+virNetServerProgramPtr remoteProgram = NULL;
+virNetServerProgramPtr adminProgram = NULL;
+virNetServerProgramPtr qemuProgram = NULL;
+virNetServerProgramPtr lxcProgram = NULL;
+
+volatile bool driversInitialized = false;
+
+enum {
+    VIR_DAEMON_ERR_NONE = 0,
+    VIR_DAEMON_ERR_PIDFILE,
+    VIR_DAEMON_ERR_RUNDIR,
+    VIR_DAEMON_ERR_INIT,
+    VIR_DAEMON_ERR_SIGNAL,
+    VIR_DAEMON_ERR_PRIVS,
+    VIR_DAEMON_ERR_NETWORK,
+    VIR_DAEMON_ERR_CONFIG,
+    VIR_DAEMON_ERR_HOOKS,
+    VIR_DAEMON_ERR_AUDIT,
+
+    VIR_DAEMON_ERR_LAST
+};
+
+VIR_ENUM_DECL(virDaemonErr)
+VIR_ENUM_IMPL(virDaemonErr, VIR_DAEMON_ERR_LAST,
+              "Initialization successful",
+              "Unable to obtain pidfile",
+              "Unable to create rundir",
+              "Unable to initialize libvirt",
+              "Unable to setup signal handlers",
+              "Unable to drop privileges",
+              "Unable to initialize network sockets",
+              "Unable to load configuration file",
+              "Unable to look for hook scripts",
+              "Unable to initialize audit system")
+
+static int daemonForkIntoBackground(const char *argv0)
+{
+    int statuspipe[2];
+    if (pipe(statuspipe) < 0)
+        return -1;
+
+    pid_t pid = fork();
+    switch (pid) {
+    case 0:
+        {
+            /* intermediate child */
+            int stdinfd = -1;
+            int stdoutfd = -1;
+            int nextpid;
+
+            VIR_FORCE_CLOSE(statuspipe[0]);
+
+            if ((stdinfd = open("/dev/null", O_RDONLY)) <= STDERR_FILENO)
+                goto cleanup;
+            if ((stdoutfd = open("/dev/null", O_WRONLY)) <= STDERR_FILENO)
+                goto cleanup;
+            if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
+                goto cleanup;
+            if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
+                goto cleanup;
+            if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
+                goto cleanup;
+            if (VIR_CLOSE(stdinfd) < 0)
+                goto cleanup;
+            if (VIR_CLOSE(stdoutfd) < 0)
+                goto cleanup;
+
+            if (setsid() < 0)
+                goto cleanup;
+
+            nextpid = fork();
+            switch (nextpid) {
+            case 0: /* grandchild */
+                return statuspipe[1];
+            case -1: /* error */
+                goto cleanup;
+            default: /* intermediate child succeeded */
+                _exit(EXIT_SUCCESS);
+            }
+
+        cleanup:
+            VIR_FORCE_CLOSE(stdoutfd);
+            VIR_FORCE_CLOSE(stdinfd);
+            VIR_FORCE_CLOSE(statuspipe[1]);
+            _exit(EXIT_FAILURE);
+
+        }
+
+    case -1: /* error in parent */
+        goto error;
+
+    default:
+        {
+            /* parent */
+            int ret;
+            char status;
+
+            VIR_FORCE_CLOSE(statuspipe[1]);
+
+            /* We wait to make sure the first child forked successfully */
+            if (virProcessWait(pid, NULL, false) < 0)
+                goto error;
+
+            /* If we get here, then the grandchild was spawned, so we
+             * must exit.  Block until the second child initializes
+             * successfully */
+        again:
+            ret = read(statuspipe[0], &status, 1);
+            if (ret == -1 && errno == EINTR)
+                goto again;
+
+            VIR_FORCE_CLOSE(statuspipe[0]);
+
+            if (ret != 1) {
+                char ebuf[1024];
+
+                fprintf(stderr,
+                        _("%s: error: unable to determine if daemon is "
+                          "running: %s\n"), argv0,
+                        virStrerror(errno, ebuf, sizeof(ebuf)));
+                exit(EXIT_FAILURE);
+            } else if (status != 0) {
+                fprintf(stderr,
+                        _("%s: error: %s. Check /var/log/messages or run "
+                          "without --daemon for more info.\n"), argv0,
+                        virDaemonErrTypeToString(status));
+                exit(EXIT_FAILURE);
+            }
+            _exit(EXIT_SUCCESS);
+        }
+    }
+
+ error:
+    VIR_FORCE_CLOSE(statuspipe[0]);
+    VIR_FORCE_CLOSE(statuspipe[1]);
+    return -1;
+}
+
+
+static int
+daemonUnixSocketPaths(struct daemonConfig *config,
+                      bool privileged,
+                      char **sockfile,
+                      char **rosockfile,
+                      char **admsockfile)
+{
+    int ret = -1;
+    char *rundir = NULL;
+
+    if (config->unix_sock_dir) {
+        if (virAsprintf(sockfile, "%s/libvirt-sock", config->unix_sock_dir) < 0)
+            goto cleanup;
+
+        if (privileged) {
+            if (virAsprintf(rosockfile, "%s/libvirt-sock-ro", config->unix_sock_dir) < 0 ||
+                virAsprintf(admsockfile, "%s/libvirt-admin-sock", config->unix_sock_dir) < 0)
+                goto cleanup;
+        }
+    } else {
+        if (privileged) {
+            if (VIR_STRDUP(*sockfile, LOCALSTATEDIR "/run/libvirt/libvirt-sock") < 0 ||
+                VIR_STRDUP(*rosockfile, LOCALSTATEDIR "/run/libvirt/libvirt-sock-ro") < 0 ||
+                VIR_STRDUP(*admsockfile, LOCALSTATEDIR "/run/libvirt/libvirt-admin-sock") < 0)
+                goto cleanup;
+        } else {
+            mode_t old_umask;
+
+            if (!(rundir = virGetUserRuntimeDirectory()))
+                goto cleanup;
+
+            old_umask = umask(077);
+            if (virFileMakePath(rundir) < 0) {
+                umask(old_umask);
+                goto cleanup;
+            }
+            umask(old_umask);
+
+            if (virAsprintf(sockfile, "%s/libvirt-sock", rundir) < 0 ||
+                virAsprintf(admsockfile, "%s/libvirt-admin-sock", rundir) < 0)
+                goto cleanup;
+        }
+    }
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(rundir);
+    return ret;
+}
+
+
+static void daemonErrorHandler(void *opaque ATTRIBUTE_UNUSED,
+                               virErrorPtr err ATTRIBUTE_UNUSED)
+{
+    /* Don't do anything, since logging infrastructure already
+     * took care of reporting the error */
+}
+
+static int daemonErrorLogFilter(virErrorPtr err, int priority)
+{
+    /* These error codes don't really reflect real errors. They
+     * are expected events that occur when an app tries to check
+     * whether a particular guest already exists. This filters
+     * them to a lower log level to prevent pollution of syslog
+     */
+    switch (err->code) {
+    case VIR_ERR_NO_DOMAIN:
+    case VIR_ERR_NO_NETWORK:
+    case VIR_ERR_NO_STORAGE_POOL:
+    case VIR_ERR_NO_STORAGE_VOL:
+    case VIR_ERR_NO_NODE_DEVICE:
+    case VIR_ERR_NO_INTERFACE:
+    case VIR_ERR_NO_NWFILTER:
+    case VIR_ERR_NO_SECRET:
+    case VIR_ERR_NO_DOMAIN_SNAPSHOT:
+    case VIR_ERR_OPERATION_INVALID:
+    case VIR_ERR_NO_DOMAIN_METADATA:
+    case VIR_ERR_NO_SERVER:
+    case VIR_ERR_NO_CLIENT:
+        return VIR_LOG_DEBUG;
+    }
+
+    return priority;
+}
+
+
+#define VIR_DAEMON_LOAD_MODULE(func, module) \
+    virDriverLoadModule(module, #func)
+static void daemonInitialize(void)
+{
+    /*
+     * Note that the order is important: the first ones have a higher
+     * priority when calling virStateInitialize. We must register the
+     * network, storage and nodedev drivers before any stateful domain
+     * driver, since their resources must be auto-started before any
+     * domains can be auto-started.
+     */
+    /* We don't care if any of these fail, because the whole point
+     * is to allow users to only install modules they want to use.
+     * If they try to open a connection for a module that
+     * is not loaded they'll get a suitable error at that point
+     */
+#ifdef WITH_NETWORK
+    VIR_DAEMON_LOAD_MODULE(networkRegister, "network");
+#endif
+#ifdef WITH_INTERFACE
+    VIR_DAEMON_LOAD_MODULE(interfaceRegister, "interface");
+#endif
+#ifdef WITH_STORAGE
+    VIR_DAEMON_LOAD_MODULE(storageRegister, "storage");
+#endif
+#ifdef WITH_NODE_DEVICES
+    VIR_DAEMON_LOAD_MODULE(nodedevRegister, "nodedev");
+#endif
+#ifdef WITH_SECRETS
+    VIR_DAEMON_LOAD_MODULE(secretRegister, "secret");
+#endif
+#ifdef WITH_NWFILTER
+    VIR_DAEMON_LOAD_MODULE(nwfilterRegister, "nwfilter");
+#endif
+#ifdef WITH_XEN
+    VIR_DAEMON_LOAD_MODULE(xenRegister, "xen");
+#endif
+#ifdef WITH_LIBXL
+    VIR_DAEMON_LOAD_MODULE(libxlRegister, "libxl");
+#endif
+#ifdef WITH_QEMU
+    VIR_DAEMON_LOAD_MODULE(qemuRegister, "qemu");
+#endif
+#ifdef WITH_LXC
+    VIR_DAEMON_LOAD_MODULE(lxcRegister, "lxc");
+#endif
+#ifdef WITH_UML
+    VIR_DAEMON_LOAD_MODULE(umlRegister, "uml");
+#endif
+#ifdef WITH_VBOX
+    VIR_DAEMON_LOAD_MODULE(vboxRegister, "vbox");
+#endif
+#ifdef WITH_BHYVE
+    VIR_DAEMON_LOAD_MODULE(bhyveRegister, "bhyve");
+#endif
+#ifdef WITH_VZ
+    VIR_DAEMON_LOAD_MODULE(vzRegister, "vz");
+#endif
+}
+#undef VIR_DAEMON_LOAD_MODULE
+
+
+static int ATTRIBUTE_NONNULL(3)
+daemonSetupNetworking(virNetServerPtr srv,
+                      virNetServerPtr srvAdm,
+                      struct daemonConfig *config,
+                      const char *sock_path,
+                      const char *sock_path_ro,
+                      const char *sock_path_adm,
+                      bool ipsock,
+                      bool privileged)
+{
+    virNetServerServicePtr svc = NULL;
+    virNetServerServicePtr svcAdm = NULL;
+    virNetServerServicePtr svcRO = NULL;
+    virNetServerServicePtr svcTCP = NULL;
+#if WITH_GNUTLS
+    virNetServerServicePtr svcTLS = NULL;
+#endif
+    gid_t unix_sock_gid = 0;
+    int unix_sock_ro_mask = 0;
+    int unix_sock_rw_mask = 0;
+    int unix_sock_adm_mask = 0;
+    int ret = -1;
+
+    unsigned int cur_fd = STDERR_FILENO + 1;
+    unsigned int nfds = virGetListenFDs();
+
+    if (config->unix_sock_group) {
+        if (virGetGroupID(config->unix_sock_group, &unix_sock_gid) < 0)
+            return ret;
+    }
+
+    if (nfds > (sock_path_ro ? 2 : 1)) {
+        VIR_ERROR(_("Too many (%u) FDs passed from caller"), nfds);
+        return ret;
+    }
+
+    if (virStrToLong_i(config->unix_sock_ro_perms, NULL, 8, &unix_sock_ro_mask) != 0) {
+        VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_ro_perms);
+        goto cleanup;
+    }
+
+    if (virStrToLong_i(config->unix_sock_admin_perms, NULL, 8, &unix_sock_adm_mask) != 0) {
+        VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_admin_perms);
+        goto cleanup;
+    }
+
+    if (virStrToLong_i(config->unix_sock_rw_perms, NULL, 8, &unix_sock_rw_mask) != 0) {
+        VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_rw_perms);
+        goto cleanup;
+    }
+
+    if (!(svc = virNetServerServiceNewFDOrUNIX(sock_path,
+                                               unix_sock_rw_mask,
+                                               unix_sock_gid,
+                                               config->auth_unix_rw,
+#if WITH_GNUTLS
+                                               NULL,
+#endif
+                                               false,
+                                               config->max_queued_clients,
+                                               config->max_client_requests,
+                                               nfds, &cur_fd)))
+        goto cleanup;
+    if (sock_path_ro) {
+        if (!(svcRO = virNetServerServiceNewFDOrUNIX(sock_path_ro,
+                                                     unix_sock_ro_mask,
+                                                     unix_sock_gid,
+                                                     config->auth_unix_ro,
+#if WITH_GNUTLS
+                                                     NULL,
+#endif
+                                                     true,
+                                                     config->max_queued_clients,
+                                                     config->max_client_requests,
+                                                     nfds, &cur_fd)))
+            goto cleanup;
+    }
+
+    if (virNetServerAddService(srv, svc,
+                               config->mdns_adv && !ipsock ?
+                               "_libvirt._tcp" :
+                               NULL) < 0)
+        goto cleanup;
+
+    if (svcRO &&
+        virNetServerAddService(srv, svcRO, NULL) < 0)
+        goto cleanup;
+
+    if (sock_path_adm) {
+        VIR_DEBUG("Registering unix socket %s", sock_path_adm);
+        if (!(svcAdm = virNetServerServiceNewUNIX(sock_path_adm,
+                                                  unix_sock_adm_mask,
+                                                  unix_sock_gid,
+                                                  REMOTE_AUTH_NONE,
+#if WITH_GNUTLS
+                                                  NULL,
+#endif
+                                                  false,
+                                                  config->admin_max_queued_clients,
+                                                  config->admin_max_client_requests)))
+            goto cleanup;
+
+        if (virNetServerAddService(srvAdm, svcAdm, NULL) < 0)
+            goto cleanup;
+    }
+
+    if (ipsock) {
+        if (config->listen_tcp) {
+            VIR_DEBUG("Registering TCP socket %s:%s",
+                      config->listen_addr, config->tcp_port);
+            if (!(svcTCP = virNetServerServiceNewTCP(config->listen_addr,
+                                                     config->tcp_port,
+                                                     AF_UNSPEC,
+                                                     config->auth_tcp,
+#if WITH_GNUTLS
+                                                     NULL,
+#endif
+                                                     false,
+                                                     config->max_queued_clients,
+                                                     config->max_client_requests)))
+                goto cleanup;
+
+            if (virNetServerAddService(srv, svcTCP,
+                                       config->mdns_adv ? "_libvirt._tcp" : NULL) < 0)
+                goto cleanup;
+        }
+
+#if WITH_GNUTLS
+        if (config->listen_tls) {
+            virNetTLSContextPtr ctxt = NULL;
+
+            if (config->ca_file ||
+                config->cert_file ||
+                config->key_file) {
+                if (!config->ca_file) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                   _("No CA certificate path set to match server key/cert"));
+                    goto cleanup;
+                }
+                if (!config->cert_file) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                   _("No server certificate path set to match server key"));
+                    goto cleanup;
+                }
+                if (!config->key_file) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                   _("No server key path set to match server cert"));
+                    goto cleanup;
+                }
+                VIR_DEBUG("Using CA='%s' cert='%s' key='%s'",
+                          config->ca_file, config->cert_file, config->key_file);
+                if (!(ctxt = virNetTLSContextNewServer(config->ca_file,
+                                                       config->crl_file,
+                                                       config->cert_file,
+                                                       config->key_file,
+                                                       (const char *const*)config->tls_allowed_dn_list,
+                                                       config->tls_priority,
+                                                       config->tls_no_sanity_certificate ? false : true,
+                                                       config->tls_no_verify_certificate ? false : true)))
+                    goto cleanup;
+            } else {
+                if (!(ctxt = virNetTLSContextNewServerPath(NULL,
+                                                           !privileged,
+                                                           (const char *const*)config->tls_allowed_dn_list,
+                                                           config->tls_priority,
+                                                           config->tls_no_sanity_certificate ? false : true,
+                                                           config->tls_no_verify_certificate ? false : true)))
+                    goto cleanup;
+            }
+
+            VIR_DEBUG("Registering TLS socket %s:%s",
+                      config->listen_addr, config->tls_port);
+            if (!(svcTLS =
+                  virNetServerServiceNewTCP(config->listen_addr,
+                                            config->tls_port,
+                                            AF_UNSPEC,
+                                            config->auth_tls,
+                                            ctxt,
+                                            false,
+                                            config->max_queued_clients,
+                                            config->max_client_requests))) {
+                virObjectUnref(ctxt);
+                goto cleanup;
+            }
+            if (virNetServerAddService(srv, svcTLS,
+                                       config->mdns_adv &&
+                                       !config->listen_tcp ? "_libvirt._tcp" : NULL) < 0)
+                goto cleanup;
+
+            virObjectUnref(ctxt);
+        }
+#else
+        (void)privileged;
+        if (config->listen_tls) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("This libvirtd build does not support TLS"));
+            goto cleanup;
+        }
+#endif
+    }
+
+#if WITH_SASL
+    if (config->auth_unix_rw == REMOTE_AUTH_SASL ||
+        (sock_path_ro && config->auth_unix_ro == REMOTE_AUTH_SASL) ||
+# if WITH_GNUTLS
+        (ipsock && config->listen_tls && config->auth_tls == REMOTE_AUTH_SASL) ||
+# endif
+        (ipsock && config->listen_tcp && config->auth_tcp == REMOTE_AUTH_SASL)) {
+        saslCtxt = virNetSASLContextNewServer(
+            (const char *const*)config->sasl_allowed_username_list);
+        if (!saslCtxt)
+            goto cleanup;
+    }
+#endif
+
+    ret = 0;
+
+ cleanup:
+#if WITH_GNUTLS
+    virObjectUnref(svcTLS);
+#endif
+    virObjectUnref(svcTCP);
+    virObjectUnref(svcRO);
+    virObjectUnref(svcAdm);
+    virObjectUnref(svc);
+    return ret;
+}
+
+
+/*
+ * Set up the openvswitch timeout
+ */
+static void
+daemonSetupNetDevOpenvswitch(struct daemonConfig *config)
+{
+    virNetDevOpenvswitchSetTimeout(config->ovs_timeout);
+}
+
+
+/*
+ * Set up the logging environment
+ * By default if daemonized all errors go to the logfile libvirtd.log,
+ * but if verbose or error debugging is asked for then also output
+ * informational and debug messages. Default size if 64 kB.
+ */
+static int
+daemonSetupLogging(struct daemonConfig *config,
+                   bool privileged,
+                   bool verbose,
+                   bool godaemon)
+{
+    virLogReset();
+
+    /*
+     * Libvirtd's order of precedence is:
+     * cmdline > environment > config
+     *
+     * Given the precedence, we must process the variables in the opposite
+     * order, each one overriding the previous.
+     */
+    if (config->log_level != 0)
+        virLogSetDefaultPriority(config->log_level);
+
+    /* In case the config is empty, both filters and outputs will become empty,
+     * however we can't start with empty outputs, thus we'll need to define and
+     * setup a default one.
+     */
+    ignore_value(virLogSetFilters(config->log_filters));
+    ignore_value(virLogSetOutputs(config->log_outputs));
+
+    /* If there are some environment variables defined, use those instead */
+    virLogSetFromEnv();
+
+    /*
+     * Command line override for --verbose
+     */
+    if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
+        virLogSetDefaultPriority(VIR_LOG_INFO);
+
+    /* Define the default output. This is only applied if there was no setting
+     * from either the config or the environment.
+     */
+    if (virLogSetDefaultOutput("libvirtd.log", godaemon, privileged) < 0)
+        return -1;
+
+    if (virLogGetNbOutputs() == 0)
+        virLogSetOutputs(virLogGetDefaultOutput());
+
+    return 0;
+}
+
+
+static int
+daemonSetupAccessManager(struct daemonConfig *config)
+{
+    virAccessManagerPtr mgr;
+    const char *none[] = { "none", NULL };
+    const char **drv = (const char **)config->access_drivers;
+
+    if (!drv ||
+        !drv[0])
+        drv = none;
+
+    if (!(mgr = virAccessManagerNewStack(drv)))
+        return -1;
+
+    virAccessManagerSetDefault(mgr);
+    virObjectUnref(mgr);
+    return 0;
+}
+
+
+/* Display version information. */
+static void
+daemonVersion(const char *argv0)
+{
+    printf("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
+}
+
+#ifdef __sun
+static int
+daemonSetupPrivs(void)
+{
+    chown("/var/run/libvirt", SYSTEM_UID, SYSTEM_UID);
+
+    if (__init_daemon_priv(PU_RESETGROUPS | PU_CLEARLIMITSET,
+                           SYSTEM_UID, SYSTEM_UID, PRIV_XVM_CONTROL, NULL)) {
+        VIR_ERROR(_("additional privileges are required"));
+        return -1;
+    }
+
+    if (priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_FILE_LINK_ANY, PRIV_PROC_INFO,
+                 PRIV_PROC_SESSION, PRIV_PROC_EXEC, PRIV_PROC_FORK, NULL)) {
+        VIR_ERROR(_("failed to set reduced privileges"));
+        return -1;
+    }
+
+    return 0;
+}
+#else
+# define daemonSetupPrivs() 0
+#endif
+
+
+static void daemonShutdownHandler(virNetDaemonPtr dmn,
+                                  siginfo_t *sig ATTRIBUTE_UNUSED,
+                                  void *opaque ATTRIBUTE_UNUSED)
+{
+    virNetDaemonQuit(dmn);
+}
+
+static void daemonReloadHandler(virNetDaemonPtr dmn ATTRIBUTE_UNUSED,
+                                siginfo_t *sig ATTRIBUTE_UNUSED,
+                                void *opaque ATTRIBUTE_UNUSED)
+{
+    if (!driversInitialized) {
+        VIR_WARN("Drivers are not initialized, reload ignored");
+        return;
+    }
+
+    VIR_INFO("Reloading configuration on SIGHUP");
+    virHookCall(VIR_HOOK_DRIVER_DAEMON, "-",
+                VIR_HOOK_DAEMON_OP_RELOAD, SIGHUP, "SIGHUP", NULL, NULL);
+    if (virStateReload() < 0)
+        VIR_WARN("Error while reloading drivers");
+}
+
+static int daemonSetupSignals(virNetDaemonPtr dmn)
+{
+    if (virNetDaemonAddSignalHandler(dmn, SIGINT, daemonShutdownHandler, NULL) < 0)
+        return -1;
+    if (virNetDaemonAddSignalHandler(dmn, SIGQUIT, daemonShutdownHandler, NULL) < 0)
+        return -1;
+    if (virNetDaemonAddSignalHandler(dmn, SIGTERM, daemonShutdownHandler, NULL) < 0)
+        return -1;
+    if (virNetDaemonAddSignalHandler(dmn, SIGHUP, daemonReloadHandler, NULL) < 0)
+        return -1;
+    return 0;
+}
+
+
+static void daemonInhibitCallback(bool inhibit, void *opaque)
+{
+    virNetDaemonPtr dmn = opaque;
+
+    if (inhibit)
+        virNetDaemonAddShutdownInhibition(dmn);
+    else
+        virNetDaemonRemoveShutdownInhibition(dmn);
+}
+
+
+#ifdef WITH_DBUS
+static DBusConnection *sessionBus;
+static DBusConnection *systemBus;
+
+static void daemonStopWorker(void *opaque)
+{
+    virNetDaemonPtr dmn = opaque;
+
+    VIR_DEBUG("Begin stop dmn=%p", dmn);
+
+    ignore_value(virStateStop());
+
+    VIR_DEBUG("Completed stop dmn=%p", dmn);
+
+    /* Exit libvirtd cleanly */
+    virNetDaemonQuit(dmn);
+}
+
+
+/* We do this in a thread to not block the main loop */
+static void daemonStop(virNetDaemonPtr dmn)
+{
+    virThread thr;
+    virObjectRef(dmn);
+    if (virThreadCreate(&thr, false, daemonStopWorker, dmn) < 0)
+        virObjectUnref(dmn);
+}
+
+
+static DBusHandlerResult
+handleSessionMessageFunc(DBusConnection *connection ATTRIBUTE_UNUSED,
+                         DBusMessage *message,
+                         void *opaque)
+{
+    virNetDaemonPtr dmn = opaque;
+
+    VIR_DEBUG("dmn=%p", dmn);
+
+    if (dbus_message_is_signal(message,
+                               DBUS_INTERFACE_LOCAL,
+                               "Disconnected"))
+        daemonStop(dmn);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult
+handleSystemMessageFunc(DBusConnection *connection ATTRIBUTE_UNUSED,
+                        DBusMessage *message,
+                        void *opaque)
+{
+    virNetDaemonPtr dmn = opaque;
+
+    VIR_DEBUG("dmn=%p", dmn);
+
+    if (dbus_message_is_signal(message,
+                               "org.freedesktop.login1.Manager",
+                               "PrepareForShutdown"))
+        daemonStop(dmn);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+#endif
+
+
+static void daemonRunStateInit(void *opaque)
+{
+    virNetDaemonPtr dmn = opaque;
+    virIdentityPtr sysident = virIdentityGetSystem();
+
+    virIdentitySetCurrent(sysident);
+
+    /* Since driver initialization can take time inhibit daemon shutdown until
+       we're done so clients get a chance to connect */
+    daemonInhibitCallback(true, dmn);
+
+    /* Start the stateful HV drivers
+     * This is deliberately done after telling the parent process
+     * we're ready, since it can take a long time and this will
+     * seriously delay OS bootup process */
+    if (virStateInitialize(virNetDaemonIsPrivileged(dmn),
+                           daemonInhibitCallback,
+                           dmn) < 0) {
+        VIR_ERROR(_("Driver state initialization failed"));
+        /* Ensure the main event loop quits */
+        kill(getpid(), SIGTERM);
+        goto cleanup;
+    }
+
+    driversInitialized = true;
+
+#ifdef WITH_DBUS
+    /* Tie the non-privileged libvirtd to the session/shutdown lifecycle */
+    if (!virNetDaemonIsPrivileged(dmn)) {
+
+        sessionBus = virDBusGetSessionBus();
+        if (sessionBus != NULL)
+            dbus_connection_add_filter(sessionBus,
+                                       handleSessionMessageFunc, dmn, NULL);
+
+        systemBus = virDBusGetSystemBus();
+        if (systemBus != NULL) {
+            dbus_connection_add_filter(systemBus,
+                                       handleSystemMessageFunc, dmn, NULL);
+            dbus_bus_add_match(systemBus,
+                               "type='signal',sender='org.freedesktop.login1', interface='org.freedesktop.login1.Manager'",
+                               NULL);
+        }
+    }
+#endif
+    /* Only now accept clients from network */
+    virNetDaemonUpdateServices(dmn, true);
+ cleanup:
+    daemonInhibitCallback(false, dmn);
+    virObjectUnref(dmn);
+    virObjectUnref(sysident);
+    virIdentitySetCurrent(NULL);
+}
+
+static int daemonStateInit(virNetDaemonPtr dmn)
+{
+    virThread thr;
+    virObjectRef(dmn);
+    if (virThreadCreate(&thr, false, daemonRunStateInit, dmn) < 0) {
+        virObjectUnref(dmn);
+        return -1;
+    }
+    return 0;
+}
+
+static int migrateProfile(void)
+{
+    char *old_base = NULL;
+    char *updated = NULL;
+    char *home = NULL;
+    char *xdg_dir = NULL;
+    char *config_dir = NULL;
+    const char *config_home;
+    int ret = -1;
+    mode_t old_umask;
+
+    VIR_DEBUG("Checking if user profile needs migrating");
+
+    if (!(home = virGetUserDirectory()))
+        goto cleanup;
+
+    if (virAsprintf(&old_base, "%s/.libvirt", home) < 0)
+        goto cleanup;
+
+    /* if the new directory is there or the old one is not: do nothing */
+    if (!(config_dir = virGetUserConfigDirectory()))
+        goto cleanup;
+
+    if (!virFileIsDir(old_base) || virFileExists(config_dir)) {
+        VIR_DEBUG("No old profile in '%s' / "
+                  "new profile directory already present '%s'",
+                  old_base, config_dir);
+        ret = 0;
+        goto cleanup;
+    }
+
+    /* test if we already attempted to migrate first */
+    if (virAsprintf(&updated, "%s/DEPRECATED-DIRECTORY", old_base) < 0)
+        goto cleanup;
+    if (virFileExists(updated))
+        goto cleanup;
+
+    config_home = virGetEnvBlockSUID("XDG_CONFIG_HOME");
+    if (config_home && config_home[0] != '\0') {
+        if (VIR_STRDUP(xdg_dir, config_home) < 0)
+            goto cleanup;
+    } else {
+        if (virAsprintf(&xdg_dir, "%s/.config", home) < 0)
+            goto cleanup;
+    }
+
+    old_umask = umask(077);
+    if (virFileMakePath(xdg_dir) < 0) {
+        umask(old_umask);
+        goto cleanup;
+    }
+    umask(old_umask);
+
+    if (rename(old_base, config_dir) < 0) {
+        int fd = creat(updated, 0600);
+        VIR_FORCE_CLOSE(fd);
+        VIR_ERROR(_("Unable to migrate %s to %s"), old_base, config_dir);
+        goto cleanup;
+    }
+
+    VIR_DEBUG("Profile migrated from %s to %s", old_base, config_dir);
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(home);
+    VIR_FREE(old_base);
+    VIR_FREE(xdg_dir);
+    VIR_FREE(config_dir);
+    VIR_FREE(updated);
+
+    return ret;
+}
+
+static int
+daemonSetupHostUUID(const struct daemonConfig *config)
+{
+    static const char *machine_id = "/etc/machine-id";
+    char buf[VIR_UUID_STRING_BUFLEN];
+    const char *uuid;
+
+    if (config->host_uuid) {
+        uuid = config->host_uuid;
+    } else if (!config->host_uuid_source ||
+               STREQ(config->host_uuid_source, "smbios")) {
+        /* smbios UUID is fetched on demand in virGetHostUUID */
+        return 0;
+    } else if (STREQ(config->host_uuid_source, "machine-id")) {
+        if (virFileReadBufQuiet(machine_id, buf, sizeof(buf)) < 0) {
+            VIR_ERROR(_("Can't read %s"), machine_id);
+            return -1;
+        }
+
+        uuid = buf;
+    } else {
+        VIR_ERROR(_("invalid UUID source: %s"), config->host_uuid_source);
+        return -1;
+    }
+
+    if (virSetHostUUIDStr(uuid)) {
+        VIR_ERROR(_("invalid host UUID: %s"), uuid);
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Print command-line usage. */
+static void
+daemonUsage(const char *argv0, bool privileged)
+{
+    fprintf(stderr,
+            _("\n"
+              "Usage:\n"
+              "  %s [options]\n"
+              "\n"
+              "Options:\n"
+              "  -h | --help            Display program help:\n"
+              "  -v | --verbose         Verbose messages.\n"
+              "  -d | --daemon          Run as a daemon & write PID file.\n"
+              "  -l | --listen          Listen for TCP/IP connections.\n"
+              "  -t | --timeout <secs>  Exit after timeout period.\n"
+              "  -f | --config <file>   Configuration file.\n"
+              "  -V | --version         Display version information.\n"
+              "  -p | --pid-file <file> Change name of PID file.\n"
+              "\n"
+              "libvirt management daemon:\n"),
+            argv0);
+
+    if (privileged) {
+        fprintf(stderr,
+                _("\n"
+                  "  Default paths:\n"
+                  "\n"
+                  "    Configuration file (unless overridden by -f):\n"
+                  "      %s\n"
+                  "\n"
+                  "    Sockets:\n"
+                  "      %s\n"
+                  "      %s\n"
+                  "\n"
+                  "    TLS:\n"
+                  "      CA certificate:     %s\n"
+                  "      Server certificate: %s\n"
+                  "      Server private key: %s\n"
+                  "\n"
+                  "    PID file (unless overridden by -p):\n"
+                  "      %s/run/libvirtd.pid\n"
+                  "\n"),
+                LIBVIRTD_CONFIGURATION_FILE,
+                LIBVIRTD_PRIV_UNIX_SOCKET,
+                LIBVIRTD_PRIV_UNIX_SOCKET_RO,
+                LIBVIRT_CACERT,
+                LIBVIRT_SERVERCERT,
+                LIBVIRT_SERVERKEY,
+                LOCALSTATEDIR);
+    } else {
+        fprintf(stderr, "%s",
+                _("\n"
+                  "  Default paths:\n"
+                  "\n"
+                  "    Configuration file (unless overridden by -f):\n"
+                  "      $XDG_CONFIG_HOME/libvirt/libvirtd.conf\n"
+                  "\n"
+                  "    Sockets:\n"
+                  "      $XDG_RUNTIME_DIR/libvirt/libvirt-sock\n"
+                  "\n"
+                  "    TLS:\n"
+                  "      CA certificate:     $HOME/.pki/libvirt/cacert.pem\n"
+                  "      Server certificate: $HOME/.pki/libvirt/servercert.pem\n"
+                  "      Server private key: $HOME/.pki/libvirt/serverkey.pem\n"
+                  "\n"
+                  "    PID file:\n"
+                  "      $XDG_RUNTIME_DIR/libvirt/libvirtd.pid\n"
+                  "\n"));
+    }
+}
+
+int main(int argc, char **argv) {
+    virNetDaemonPtr dmn = NULL;
+    virNetServerPtr srv = NULL;
+    virNetServerPtr srvAdm = NULL;
+    char *remote_config_file = NULL;
+    int statuswrite = -1;
+    int ret = 1;
+    int pid_file_fd = -1;
+    char *pid_file = NULL;
+    char *sock_file = NULL;
+    char *sock_file_ro = NULL;
+    char *sock_file_adm = NULL;
+    int timeout = -1;        /* -t: Shutdown timeout */
+    int verbose = 0;
+    int godaemon = 0;
+    int ipsock = 0;
+    struct daemonConfig *config;
+    bool privileged = geteuid() == 0 ? true : false;
+    bool implicit_conf = false;
+    char *run_dir = NULL;
+    mode_t old_umask;
+
+    struct option opts[] = {
+        { "verbose", no_argument, &verbose, 'v'},
+        { "daemon", no_argument, &godaemon, 'd'},
+        { "listen", no_argument, &ipsock, 'l'},
+        { "config", required_argument, NULL, 'f'},
+        { "timeout", required_argument, NULL, 't'},
+        { "pid-file", required_argument, NULL, 'p'},
+        { "version", no_argument, NULL, 'V' },
+        { "help", no_argument, NULL, 'h' },
+        {0, 0, 0, 0}
+    };
+
+    if (virGettextInitialize() < 0 ||
+        virInitialize() < 0) {
+        fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    virUpdateSelfLastChanged(argv[0]);
+
+    virFileActivateDirOverride(argv[0]);
+
+    while (1) {
+        int optidx = 0;
+        int c;
+        char *tmp;
+
+        c = getopt_long(argc, argv, "ldf:p:t:vVh", opts, &optidx);
+
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 0:
+            /* Got one of the flags */
+            break;
+        case 'v':
+            verbose = 1;
+            break;
+        case 'd':
+            godaemon = 1;
+            break;
+        case 'l':
+            ipsock = 1;
+            break;
+
+        case 't':
+            if (virStrToLong_i(optarg, &tmp, 10, &timeout) != 0
+                || timeout <= 0
+                /* Ensure that we can multiply by 1000 without overflowing.  */
+                || timeout > INT_MAX / 1000) {
+                VIR_ERROR(_("Invalid value for timeout"));
+                exit(EXIT_FAILURE);
+            }
+            break;
+
+        case 'p':
+            VIR_FREE(pid_file);
+            if (VIR_STRDUP_QUIET(pid_file, optarg) < 0) {
+                VIR_ERROR(_("Can't allocate memory"));
+                exit(EXIT_FAILURE);
+            }
+            break;
+
+        case 'f':
+            VIR_FREE(remote_config_file);
+            if (VIR_STRDUP_QUIET(remote_config_file, optarg) < 0) {
+                VIR_ERROR(_("Can't allocate memory"));
+                exit(EXIT_FAILURE);
+            }
+            break;
+
+        case 'V':
+            daemonVersion(argv[0]);
+            exit(EXIT_SUCCESS);
+
+        case 'h':
+            daemonUsage(argv[0], privileged);
+            exit(EXIT_SUCCESS);
+
+        case '?':
+        default:
+            daemonUsage(argv[0], privileged);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    if (optind != argc) {
+        fprintf(stderr, "%s: unexpected, non-option, command line arguments\n",
+                argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!(config = daemonConfigNew(privileged))) {
+        VIR_ERROR(_("Can't create initial configuration"));
+        exit(EXIT_FAILURE);
+    }
+
+    /* No explicit config, so try and find a default one */
+    if (remote_config_file == NULL) {
+        implicit_conf = true;
+        if (daemonConfigFilePath(privileged,
+                                 &remote_config_file) < 0) {
+            VIR_ERROR(_("Can't determine config path"));
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    /* Read the config file if it exists*/
+    if (remote_config_file &&
+        daemonConfigLoadFile(config, remote_config_file, implicit_conf) < 0) {
+        VIR_ERROR(_("Can't load config file: %s: %s"),
+                  virGetLastErrorMessage(), remote_config_file);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!privileged &&
+        migrateProfile() < 0) {
+        VIR_ERROR(_("Exiting due to failure to migrate profile"));
+        exit(EXIT_FAILURE);
+    }
+
+    if (daemonSetupHostUUID(config) < 0) {
+        VIR_ERROR(_("Can't setup host uuid"));
+        exit(EXIT_FAILURE);
+    }
+
+    if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0) {
+        VIR_ERROR(_("Can't initialize logging"));
+        exit(EXIT_FAILURE);
+    }
+
+    daemonSetupNetDevOpenvswitch(config);
+
+    if (daemonSetupAccessManager(config) < 0) {
+        VIR_ERROR(_("Can't initialize access manager"));
+        exit(EXIT_FAILURE);
+    }
+
+    if (!pid_file &&
+        virPidFileConstructPath(privileged,
+                                LOCALSTATEDIR,
+                                "libvirtd",
+                                &pid_file) < 0) {
+        VIR_ERROR(_("Can't determine pid file path."));
+        exit(EXIT_FAILURE);
+    }
+    VIR_DEBUG("Decided on pid file path '%s'", NULLSTR(pid_file));
+
+    if (daemonUnixSocketPaths(config,
+                              privileged,
+                              &sock_file,
+                              &sock_file_ro,
+                              &sock_file_adm) < 0) {
+        VIR_ERROR(_("Can't determine socket paths"));
+        exit(EXIT_FAILURE);
+    }
+    VIR_DEBUG("Decided on socket paths '%s', '%s' and '%s'",
+              sock_file,
+              NULLSTR(sock_file_ro),
+              NULLSTR(sock_file_adm));
+
+    if (godaemon) {
+        char ebuf[1024];
+
+        if (chdir("/") < 0) {
+            VIR_ERROR(_("cannot change to root directory: %s"),
+                      virStrerror(errno, ebuf, sizeof(ebuf)));
+            goto cleanup;
+        }
+
+        if ((statuswrite = daemonForkIntoBackground(argv[0])) < 0) {
+            VIR_ERROR(_("Failed to fork as daemon: %s"),
+                      virStrerror(errno, ebuf, sizeof(ebuf)));
+            goto cleanup;
+        }
+    }
+
+    /* Try to claim the pidfile, exiting if we can't */
+    if ((pid_file_fd = virPidFileAcquirePath(pid_file, false, getpid())) < 0) {
+        ret = VIR_DAEMON_ERR_PIDFILE;
+        goto cleanup;
+    }
+
+    /* Ensure the rundir exists (on tmpfs on some systems) */
+    if (privileged) {
+        if (VIR_STRDUP_QUIET(run_dir, LOCALSTATEDIR "/run/libvirt") < 0) {
+            VIR_ERROR(_("Can't allocate memory"));
+            goto cleanup;
+        }
+    } else {
+        run_dir = virGetUserRuntimeDirectory();
+
+        if (!run_dir) {
+            VIR_ERROR(_("Can't determine user directory"));
+            goto cleanup;
+        }
+    }
+    if (privileged)
+        old_umask = umask(022);
+    else
+        old_umask = umask(077);
+    VIR_DEBUG("Ensuring run dir '%s' exists", run_dir);
+    if (virFileMakePath(run_dir) < 0) {
+        char ebuf[1024];
+        VIR_ERROR(_("unable to create rundir %s: %s"), run_dir,
+                  virStrerror(errno, ebuf, sizeof(ebuf)));
+        ret = VIR_DAEMON_ERR_RUNDIR;
+        goto cleanup;
+    }
+    umask(old_umask);
+
+    if (virNetlinkStartup() < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (!(dmn = virNetDaemonNew())) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (!(srv = virNetServerNew("libvirtd", 1,
+                                config->min_workers,
+                                config->max_workers,
+                                config->prio_workers,
+                                config->max_clients,
+                                config->max_anonymous_clients,
+                                config->keepalive_interval,
+                                config->keepalive_count,
+                                config->mdns_adv ? config->mdns_name : NULL,
+                                remoteClientNew,
+                                NULL,
+                                remoteClientFree,
+                                NULL))) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (virNetDaemonAddServer(dmn, srv) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    /* Beyond this point, nothing should rely on using
+     * getuid/geteuid() == 0, for privilege level checks.
+     */
+    VIR_DEBUG("Dropping privileges (if required)");
+    if (daemonSetupPrivs() < 0) {
+        ret = VIR_DAEMON_ERR_PRIVS;
+        goto cleanup;
+    }
+
+    daemonInitialize();
+
+    remoteProcs[REMOTE_PROC_AUTH_LIST].needAuth = false;
+    remoteProcs[REMOTE_PROC_AUTH_SASL_INIT].needAuth = false;
+    remoteProcs[REMOTE_PROC_AUTH_SASL_STEP].needAuth = false;
+    remoteProcs[REMOTE_PROC_AUTH_SASL_START].needAuth = false;
+    remoteProcs[REMOTE_PROC_AUTH_POLKIT].needAuth = false;
+    if (!(remoteProgram = virNetServerProgramNew(REMOTE_PROGRAM,
+                                                 REMOTE_PROTOCOL_VERSION,
+                                                 remoteProcs,
+                                                 remoteNProcs))) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+    if (virNetServerAddProgram(srv, remoteProgram) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (!(lxcProgram = virNetServerProgramNew(LXC_PROGRAM,
+                                              LXC_PROTOCOL_VERSION,
+                                              lxcProcs,
+                                              lxcNProcs))) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+    if (virNetServerAddProgram(srv, lxcProgram) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (!(qemuProgram = virNetServerProgramNew(QEMU_PROGRAM,
+                                               QEMU_PROTOCOL_VERSION,
+                                               qemuProcs,
+                                               qemuNProcs))) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+    if (virNetServerAddProgram(srv, qemuProgram) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (!(srvAdm = virNetServerNew("admin", 1,
+                                   config->admin_min_workers,
+                                   config->admin_max_workers,
+                                   0,
+                                   config->admin_max_clients,
+                                   0,
+                                   config->admin_keepalive_interval,
+                                   config->admin_keepalive_count,
+                                   NULL,
+                                   remoteAdmClientNew,
+                                   NULL,
+                                   remoteAdmClientFree,
+                                   dmn))) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (virNetDaemonAddServer(dmn, srvAdm) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (!(adminProgram = virNetServerProgramNew(ADMIN_PROGRAM,
+                                                ADMIN_PROTOCOL_VERSION,
+                                                adminProcs,
+                                                adminNProcs))) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+    if (virNetServerAddProgram(srvAdm, adminProgram) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+    if (timeout != -1) {
+        VIR_DEBUG("Registering shutdown timeout %d", timeout);
+        virNetDaemonAutoShutdown(dmn, timeout);
+    }
+
+    if ((daemonSetupSignals(dmn)) < 0) {
+        ret = VIR_DAEMON_ERR_SIGNAL;
+        goto cleanup;
+    }
+
+    if (config->audit_level) {
+        VIR_DEBUG("Attempting to configure auditing subsystem");
+        if (virAuditOpen(config->audit_level) < 0) {
+            if (config->audit_level > 1) {
+                ret = VIR_DAEMON_ERR_AUDIT;
+                goto cleanup;
+            }
+            VIR_DEBUG("Proceeding without auditing");
+        }
+    }
+    virAuditLog(config->audit_logging > 0);
+
+    /* setup the hooks if any */
+    if (virHookInitialize() < 0) {
+        ret = VIR_DAEMON_ERR_HOOKS;
+        goto cleanup;
+    }
+
+    /* Disable error func, now logging is setup */
+    virSetErrorFunc(NULL, daemonErrorHandler);
+    virSetErrorLogPriorityFunc(daemonErrorLogFilter);
+
+    /*
+     * Call the daemon startup hook
+     * TODO: should we abort the daemon startup if the script returned
+     *       an error ?
+     */
+    virHookCall(VIR_HOOK_DRIVER_DAEMON, "-", VIR_HOOK_DAEMON_OP_START,
+                0, "start", NULL, NULL);
+
+    if (daemonSetupNetworking(srv, srvAdm,
+                              config,
+                              sock_file,
+                              sock_file_ro,
+                              sock_file_adm,
+                              ipsock, privileged) < 0) {
+        ret = VIR_DAEMON_ERR_NETWORK;
+        goto cleanup;
+    }
+
+    /* Tell parent of daemon that basic initialization is complete
+     * In particular we're ready to accept net connections & have
+     * written the pidfile
+     */
+    if (statuswrite != -1) {
+        char status = 0;
+        ignore_value(safewrite(statuswrite, &status, 1));
+        VIR_FORCE_CLOSE(statuswrite);
+    }
+
+    /* Initialize drivers & then start accepting new clients from network */
+    if (daemonStateInit(dmn) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
+#if defined(__linux__) && defined(NETLINK_ROUTE)
+    /* Register the netlink event service for NETLINK_ROUTE */
+    if (virNetlinkEventServiceStart(NETLINK_ROUTE, 0) < 0) {
+        ret = VIR_DAEMON_ERR_NETWORK;
+        goto cleanup;
+    }
+#endif
+
+#if defined(__linux__) && defined(NETLINK_KOBJECT_UEVENT)
+    /* Register the netlink event service for NETLINK_KOBJECT_UEVENT */
+    if (virNetlinkEventServiceStart(NETLINK_KOBJECT_UEVENT, 1) < 0) {
+        ret = VIR_DAEMON_ERR_NETWORK;
+        goto cleanup;
+    }
+#endif
+
+    /* Run event loop. */
+    virNetDaemonRun(dmn);
+
+    ret = 0;
+
+    virHookCall(VIR_HOOK_DRIVER_DAEMON, "-", VIR_HOOK_DAEMON_OP_SHUTDOWN,
+                0, "shutdown", NULL, NULL);
+
+ cleanup:
+    /* Keep cleanup order in inverse order of startup */
+    virNetDaemonClose(dmn);
+
+    virNetlinkEventServiceStopAll();
+
+    if (driversInitialized) {
+        /* NB: Possible issue with timing window between driversInitialized
+         * setting if virNetlinkEventServerStart fails */
+        driversInitialized = false;
+        virStateCleanup();
+    }
+
+    virObjectUnref(adminProgram);
+    virObjectUnref(srvAdm);
+    virObjectUnref(qemuProgram);
+    virObjectUnref(lxcProgram);
+    virObjectUnref(remoteProgram);
+    virObjectUnref(srv);
+    virObjectUnref(dmn);
+
+    virNetlinkShutdown();
+
+    if (pid_file_fd != -1)
+        virPidFileReleasePath(pid_file, pid_file_fd);
+
+    VIR_FREE(run_dir);
+
+    if (statuswrite != -1) {
+        if (ret != 0) {
+            /* Tell parent of daemon what failed */
+            char status = ret;
+            ignore_value(safewrite(statuswrite, &status, 1));
+        }
+        VIR_FORCE_CLOSE(statuswrite);
+    }
+
+    VIR_FREE(sock_file);
+    VIR_FREE(sock_file_ro);
+    VIR_FREE(sock_file_adm);
+
+    VIR_FREE(pid_file);
+
+    VIR_FREE(remote_config_file);
+    daemonConfigFree(config);
+
+    return ret;
+}
diff --git a/src/remote/remote_daemon.h b/src/remote/remote_daemon.h
new file mode 100644 (file)
index 0000000..4467f71
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * remote_daemon.h: daemon data structure definitions
+ *
+ * Copyright (C) 2006-2018 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#ifndef __REMOTE_DAEMON_H__
+# define __REMOTE_DAEMON_H__
+
+# define VIR_ENUM_SENTINELS
+
+# include <rpc/types.h>
+# include <rpc/xdr.h>
+# include "remote_protocol.h"
+# include "lxc_protocol.h"
+# include "qemu_protocol.h"
+# include "virthread.h"
+
+# if WITH_SASL
+#  include "virnetsaslcontext.h"
+# endif
+# include "virnetserverprogram.h"
+
+typedef struct daemonClientStream daemonClientStream;
+typedef daemonClientStream *daemonClientStreamPtr;
+typedef struct daemonClientPrivate daemonClientPrivate;
+typedef daemonClientPrivate *daemonClientPrivatePtr;
+typedef struct daemonClientEventCallback daemonClientEventCallback;
+typedef daemonClientEventCallback *daemonClientEventCallbackPtr;
+
+/* Stores the per-client connection state */
+struct daemonClientPrivate {
+    /* Hold while accessing any data except conn */
+    virMutex lock;
+
+    daemonClientEventCallbackPtr *domainEventCallbacks;
+    size_t ndomainEventCallbacks;
+    daemonClientEventCallbackPtr *networkEventCallbacks;
+    size_t nnetworkEventCallbacks;
+    daemonClientEventCallbackPtr *qemuEventCallbacks;
+    size_t nqemuEventCallbacks;
+    daemonClientEventCallbackPtr *storageEventCallbacks;
+    size_t nstorageEventCallbacks;
+    daemonClientEventCallbackPtr *nodeDeviceEventCallbacks;
+    size_t nnodeDeviceEventCallbacks;
+    daemonClientEventCallbackPtr *secretEventCallbacks;
+    size_t nsecretEventCallbacks;
+    bool closeRegistered;
+
+# if WITH_SASL
+    virNetSASLSessionPtr sasl;
+# endif
+
+    /* This is only valid if a remote open call has been made on this
+     * connection, otherwise it will be NULL.  Also if remote close is
+     * called, it will be set back to NULL if that succeeds.
+     */
+    virConnectPtr conn;
+
+    daemonClientStreamPtr streams;
+};
+
+
+# if WITH_SASL
+extern virNetSASLContextPtr saslCtxt;
+# endif
+extern virNetServerProgramPtr remoteProgram;
+extern virNetServerProgramPtr qemuProgram;
+
+#endif /* __REMOTE_DAEMON_H__ */
diff --git a/src/remote/remote_daemon_config.c b/src/remote/remote_daemon_config.c
new file mode 100644 (file)
index 0000000..b1516be
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * remote_daemon_config.c: libvirtd config file handling
+ *
+ * Copyright (C) 2006-2018 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include "remote_daemon_config.h"
+#include "virconf.h"
+#include "viralloc.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "rpc/virnetserver.h"
+#include "configmake.h"
+#include "remote_protocol.h"
+#include "remote_driver.h"
+#include "util/virnetdevopenvswitch.h"
+#include "virstring.h"
+#include "virutil.h"
+
+#define VIR_FROM_THIS VIR_FROM_CONF
+
+VIR_LOG_INIT("daemon.libvirtd-config");
+
+
+static int
+remoteConfigGetAuth(virConfPtr conf,
+                    const char *filename,
+                    const char *key,
+                    int *auth)
+{
+    char *authstr = NULL;
+
+    if (virConfGetValueString(conf, key, &authstr) < 0)
+        return -1;
+
+    if (!authstr)
+        return 0;
+
+    if (STREQ(authstr, "none")) {
+        *auth = VIR_NET_SERVER_SERVICE_AUTH_NONE;
+#if WITH_SASL
+    } else if (STREQ(authstr, "sasl")) {
+        *auth = VIR_NET_SERVER_SERVICE_AUTH_SASL;
+#endif
+    } else if (STREQ(authstr, "polkit")) {
+        *auth = VIR_NET_SERVER_SERVICE_AUTH_POLKIT;
+    } else {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("%s: %s: unsupported auth %s"),
+                       filename, key, authstr);
+        VIR_FREE(authstr);
+        return -1;
+    }
+
+    VIR_FREE(authstr);
+    return 0;
+}
+
+int
+daemonConfigFilePath(bool privileged, char **configfile)
+{
+    if (privileged) {
+        if (VIR_STRDUP(*configfile, SYSCONFDIR "/libvirt/libvirtd.conf") < 0)
+            goto error;
+    } else {
+        char *configdir = NULL;
+
+        if (!(configdir = virGetUserConfigDirectory()))
+            goto error;
+
+        if (virAsprintf(configfile, "%s/libvirtd.conf", configdir) < 0) {
+            VIR_FREE(configdir);
+            goto error;
+        }
+        VIR_FREE(configdir);
+    }
+
+    return 0;
+
+ error:
+    return -1;
+}
+
+struct daemonConfig*
+daemonConfigNew(bool privileged ATTRIBUTE_UNUSED)
+{
+    struct daemonConfig *data;
+    char *localhost;
+    int ret;
+
+    if (VIR_ALLOC(data) < 0)
+        return NULL;
+
+    data->listen_tls = 1;
+    data->listen_tcp = 0;
+
+    if (VIR_STRDUP(data->tls_port, LIBVIRTD_TLS_PORT) < 0 ||
+        VIR_STRDUP(data->tcp_port, LIBVIRTD_TCP_PORT) < 0)
+        goto error;
+
+    /* Only default to PolicyKit if running as root */
+#if WITH_POLKIT
+    if (privileged) {
+        data->auth_unix_rw = REMOTE_AUTH_POLKIT;
+        data->auth_unix_ro = REMOTE_AUTH_POLKIT;
+    } else {
+#endif
+        data->auth_unix_rw = REMOTE_AUTH_NONE;
+        data->auth_unix_ro = REMOTE_AUTH_NONE;
+#if WITH_POLKIT
+    }
+#endif
+
+    if (VIR_STRDUP(data->unix_sock_rw_perms,
+                   data->auth_unix_rw == REMOTE_AUTH_POLKIT ? "0777" : "0700") < 0 ||
+        VIR_STRDUP(data->unix_sock_ro_perms, "0777") < 0 ||
+        VIR_STRDUP(data->unix_sock_admin_perms, "0700") < 0)
+        goto error;
+
+#if WITH_SASL
+    data->auth_tcp = REMOTE_AUTH_SASL;
+#else
+    data->auth_tcp = REMOTE_AUTH_NONE;
+#endif
+    data->auth_tls = REMOTE_AUTH_NONE;
+
+    data->mdns_adv = 0;
+
+    data->min_workers = 5;
+    data->max_workers = 20;
+    data->max_clients = 5000;
+    data->max_queued_clients = 1000;
+    data->max_anonymous_clients = 20;
+
+    data->prio_workers = 5;
+
+    data->max_client_requests = 5;
+
+    data->audit_level = 1;
+    data->audit_logging = 0;
+
+    data->keepalive_interval = 5;
+    data->keepalive_count = 5;
+
+    data->admin_min_workers = 5;
+    data->admin_max_workers = 20;
+    data->admin_max_clients = 5000;
+    data->admin_max_queued_clients = 20;
+    data->admin_max_client_requests = 5;
+
+    data->admin_keepalive_interval = 5;
+    data->admin_keepalive_count = 5;
+
+    data->ovs_timeout = VIR_NETDEV_OVS_DEFAULT_TIMEOUT;
+
+    localhost = virGetHostname();
+    if (localhost == NULL) {
+        /* we couldn't resolve the hostname; assume that we are
+         * running in disconnected operation, and report a less
+         * useful Avahi string
+         */
+        ret = VIR_STRDUP(data->mdns_name, "Virtualization Host");
+    } else {
+        char *tmp;
+        /* Extract the host part of the potentially FQDN */
+        if ((tmp = strchr(localhost, '.')))
+            *tmp = '\0';
+        ret = virAsprintf(&data->mdns_name, "Virtualization Host %s",
+                          localhost);
+    }
+    VIR_FREE(localhost);
+    if (ret < 0)
+        goto error;
+
+    return data;
+
+ error:
+    daemonConfigFree(data);
+    return NULL;
+}
+
+void
+daemonConfigFree(struct daemonConfig *data)
+{
+    char **tmp;
+
+    if (!data)
+        return;
+
+    VIR_FREE(data->listen_addr);
+    VIR_FREE(data->tls_port);
+    VIR_FREE(data->tcp_port);
+    tmp = data->access_drivers;
+    while (tmp && *tmp) {
+        VIR_FREE(*tmp);
+        tmp++;
+    }
+    VIR_FREE(data->access_drivers);
+
+    VIR_FREE(data->unix_sock_admin_perms);
+    VIR_FREE(data->unix_sock_ro_perms);
+    VIR_FREE(data->unix_sock_rw_perms);
+    VIR_FREE(data->unix_sock_group);
+    VIR_FREE(data->unix_sock_dir);
+    VIR_FREE(data->mdns_name);
+
+    tmp = data->tls_allowed_dn_list;
+    while (tmp && *tmp) {
+        VIR_FREE(*tmp);
+        tmp++;
+    }
+    VIR_FREE(data->tls_allowed_dn_list);
+
+    tmp = data->sasl_allowed_username_list;
+    while (tmp && *tmp) {
+        VIR_FREE(*tmp);
+        tmp++;
+    }
+    VIR_FREE(data->sasl_allowed_username_list);
+    VIR_FREE(data->tls_priority);
+
+    VIR_FREE(data->key_file);
+    VIR_FREE(data->ca_file);
+    VIR_FREE(data->cert_file);
+    VIR_FREE(data->crl_file);
+
+    VIR_FREE(data->host_uuid);
+    VIR_FREE(data->host_uuid_source);
+    VIR_FREE(data->log_filters);
+    VIR_FREE(data->log_outputs);
+
+    VIR_FREE(data);
+}
+
+static int
+daemonConfigLoadOptions(struct daemonConfig *data,
+                        const char *filename,
+                        virConfPtr conf)
+{
+    if (virConfGetValueBool(conf, "listen_tcp", &data->listen_tcp) < 0)
+        goto error;
+    if (virConfGetValueBool(conf, "listen_tls", &data->listen_tls) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "tls_port", &data->tls_port) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "tcp_port", &data->tcp_port) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "listen_addr", &data->listen_addr) < 0)
+        goto error;
+
+    if (remoteConfigGetAuth(conf, filename, "auth_unix_rw", &data->auth_unix_rw) < 0)
+        goto error;
+#if WITH_POLKIT
+    /* Change default perms to be wide-open if PolicyKit is enabled.
+     * Admin can always override in config file
+     */
+    if (data->auth_unix_rw == REMOTE_AUTH_POLKIT) {
+        VIR_FREE(data->unix_sock_rw_perms);
+        if (VIR_STRDUP(data->unix_sock_rw_perms, "0777") < 0)
+            goto error;
+    }
+#endif
+    if (remoteConfigGetAuth(conf, filename, "auth_unix_ro", &data->auth_unix_ro) < 0)
+        goto error;
+    if (remoteConfigGetAuth(conf, filename, "auth_tcp", &data->auth_tcp) < 0)
+        goto error;
+    if (remoteConfigGetAuth(conf, filename, "auth_tls", &data->auth_tls) < 0)
+        goto error;
+
+    if (virConfGetValueStringList(conf, "access_drivers", false,
+                                  &data->access_drivers) < 0)
+        goto error;
+
+    if (virConfGetValueString(conf, "unix_sock_group", &data->unix_sock_group) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "unix_sock_admin_perms", &data->unix_sock_admin_perms) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "unix_sock_ro_perms", &data->unix_sock_ro_perms) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "unix_sock_rw_perms", &data->unix_sock_rw_perms) < 0)
+        goto error;
+
+    if (virConfGetValueString(conf, "unix_sock_dir", &data->unix_sock_dir) < 0)
+        goto error;
+
+    if (virConfGetValueBool(conf, "mdns_adv", &data->mdns_adv) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "mdns_name", &data->mdns_name) < 0)
+        goto error;
+
+    if (virConfGetValueBool(conf, "tls_no_sanity_certificate", &data->tls_no_sanity_certificate) < 0)
+        goto error;
+    if (virConfGetValueBool(conf, "tls_no_verify_certificate", &data->tls_no_verify_certificate) < 0)
+        goto error;
+
+    if (virConfGetValueString(conf, "key_file", &data->key_file) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "cert_file", &data->cert_file) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "ca_file", &data->ca_file) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "crl_file", &data->crl_file) < 0)
+        goto error;
+
+    if (virConfGetValueStringList(conf, "tls_allowed_dn_list", false,
+                                  &data->tls_allowed_dn_list) < 0)
+        goto error;
+
+
+    if (virConfGetValueStringList(conf, "sasl_allowed_username_list", false,
+                                  &data->sasl_allowed_username_list) < 0)
+        goto error;
+
+    if (virConfGetValueString(conf, "tls_priority", &data->tls_priority) < 0)
+        goto error;
+
+    if (virConfGetValueUInt(conf, "min_workers", &data->min_workers) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "max_workers", &data->max_workers) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "max_clients", &data->max_clients) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "max_queued_clients", &data->max_queued_clients) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "max_anonymous_clients", &data->max_anonymous_clients) < 0)
+        goto error;
+
+    if (virConfGetValueUInt(conf, "prio_workers", &data->prio_workers) < 0)
+        goto error;
+
+    if (virConfGetValueUInt(conf, "max_client_requests", &data->max_client_requests) < 0)
+        goto error;
+
+    if (virConfGetValueUInt(conf, "admin_min_workers", &data->admin_min_workers) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "admin_max_workers", &data->admin_max_workers) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "admin_max_clients", &data->admin_max_clients) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "admin_max_queued_clients", &data->admin_max_queued_clients) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "admin_max_client_requests", &data->admin_max_client_requests) < 0)
+        goto error;
+
+    if (virConfGetValueUInt(conf, "audit_level", &data->audit_level) < 0)
+        goto error;
+    if (virConfGetValueBool(conf, "audit_logging", &data->audit_logging) < 0)
+        goto error;
+
+    if (virConfGetValueString(conf, "host_uuid", &data->host_uuid) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "host_uuid_source", &data->host_uuid_source) < 0)
+        goto error;
+
+    if (virConfGetValueUInt(conf, "log_level", &data->log_level) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "log_filters", &data->log_filters) < 0)
+        goto error;
+    if (virConfGetValueString(conf, "log_outputs", &data->log_outputs) < 0)
+        goto error;
+
+    if (virConfGetValueInt(conf, "keepalive_interval", &data->keepalive_interval) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "keepalive_count", &data->keepalive_count) < 0)
+        goto error;
+
+    if (virConfGetValueInt(conf, "admin_keepalive_interval", &data->admin_keepalive_interval) < 0)
+        goto error;
+    if (virConfGetValueUInt(conf, "admin_keepalive_count", &data->admin_keepalive_count) < 0)
+        goto error;
+
+    if (virConfGetValueUInt(conf, "ovs_timeout", &data->ovs_timeout) < 0)
+        goto error;
+
+    return 0;
+
+ error:
+    return -1;
+}
+
+
+/* Read the config file if it exists.
+ * Only used in the remote case, hence the name.
+ */
+int
+daemonConfigLoadFile(struct daemonConfig *data,
+                     const char *filename,
+                     bool allow_missing)
+{
+    virConfPtr conf;
+    int ret;
+
+    if (allow_missing &&
+        access(filename, R_OK) == -1 &&
+        errno == ENOENT)
+        return 0;
+
+    conf = virConfReadFile(filename, 0);
+    if (!conf)
+        return -1;
+
+    ret = daemonConfigLoadOptions(data, filename, conf);
+    virConfFree(conf);
+    return ret;
+}
+
+int daemonConfigLoadData(struct daemonConfig *data,
+                         const char *filename,
+                         const char *filedata)
+{
+    virConfPtr conf;
+    int ret;
+
+    conf = virConfReadString(filedata, 0);
+    if (!conf)
+        return -1;
+
+    ret = daemonConfigLoadOptions(data, filename, conf);
+    virConfFree(conf);
+    return ret;
+}
diff --git a/src/remote/remote_daemon_config.h b/src/remote/remote_daemon_config.h
new file mode 100644 (file)
index 0000000..49ea801
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * remote_daemon_config.h: libvirtd config file handling
+ *
+ * Copyright (C) 2006-2018 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#ifndef __REMOTE_DAEMON_CONFIG_H__
+# define __REMOTE_DAEMON_CONFIG_H__
+
+# include "internal.h"
+
+struct daemonConfig {
+    char *host_uuid;
+    char *host_uuid_source;
+
+    bool listen_tls;
+    bool listen_tcp;
+    char *listen_addr;
+    char *tls_port;
+    char *tcp_port;
+
+    char *unix_sock_admin_perms;
+    char *unix_sock_ro_perms;
+    char *unix_sock_rw_perms;
+    char *unix_sock_group;
+    char *unix_sock_dir;
+
+    int auth_unix_rw;
+    int auth_unix_ro;
+    int auth_tcp;
+    int auth_tls;
+
+    char **access_drivers;
+
+    bool mdns_adv;
+    char *mdns_name;
+
+    bool tls_no_verify_certificate;
+    bool tls_no_sanity_certificate;
+    char **tls_allowed_dn_list;
+    char **sasl_allowed_username_list;
+    char *tls_priority;
+
+    char *key_file;
+    char *cert_file;
+    char *ca_file;
+    char *crl_file;
+
+    unsigned int min_workers;
+    unsigned int max_workers;
+    unsigned int max_clients;
+    unsigned int max_queued_clients;
+    unsigned int max_anonymous_clients;
+
+    unsigned int prio_workers;
+
+    unsigned int max_client_requests;
+
+    unsigned int log_level;
+    char *log_filters;
+    char *log_outputs;
+
+    unsigned int audit_level;
+    bool audit_logging;
+
+    int keepalive_interval;
+    unsigned int keepalive_count;
+
+    unsigned int admin_min_workers;
+    unsigned int admin_max_workers;
+    unsigned int admin_max_clients;
+    unsigned int admin_max_queued_clients;
+    unsigned int admin_max_client_requests;
+
+    int admin_keepalive_interval;
+    unsigned int admin_keepalive_count;
+
+    unsigned int ovs_timeout;
+};
+
+
+int daemonConfigFilePath(bool privileged, char **configfile);
+struct daemonConfig* daemonConfigNew(bool privileged);
+void daemonConfigFree(struct daemonConfig *data);
+int daemonConfigLoadFile(struct daemonConfig *data,
+                         const char *filename,
+                         bool allow_missing);
+int daemonConfigLoadData(struct daemonConfig *data,
+                         const char *filename,
+                         const char *filedata);
+
+#endif /* __REMOTE_DAEMON_CONFIG_H__ */
diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c
new file mode 100644 (file)
index 0000000..fdb0a36
--- /dev/null
@@ -0,0 +1,7074 @@
+/*
+ * remote_daemon_dispatch.c: handlers for RPC method calls
+ *
+ * Copyright (C) 2007-2018 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Richard W.M. Jones <rjones@redhat.com>
+ */
+
+#include <config.h>
+
+#include "virerror.h"
+
+#include "remote_daemon_dispatch.h"
+#include "remote_daemon.h"
+#include "libvirt_internal.h"
+#include "datatypes.h"
+#include "viralloc.h"
+#include "virlog.h"
+#include "remote_daemon_stream.h"
+#include "viruuid.h"
+#include "vircommand.h"
+#include "intprops.h"
+#include "virnetserverservice.h"
+#include "virnetserver.h"
+#include "virfile.h"
+#include "virtypedparam.h"
+#include "virdbus.h"
+#include "virprocess.h"
+#include "remote_protocol.h"
+#include "qemu_protocol.h"
+#include "lxc_protocol.h"
+#include "virstring.h"
+#include "object_event.h"
+#include "domain_conf.h"
+#include "network_conf.h"
+#include "virprobe.h"
+#include "viraccessapicheck.h"
+#include "viraccessapicheckqemu.h"
+#include "virpolkit.h"
+#include "virthreadjob.h"
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+
+VIR_LOG_INIT("daemon.remote");
+
+#if SIZEOF_LONG < 8
+# define HYPER_TO_TYPE(_type, _to, _from) \
+    do { \
+        if ((_from) != (_type)(_from)) { \
+            virReportError(VIR_ERR_OVERFLOW, \
+                           _("conversion from hyper to %s overflowed"), \
+                           #_type); \
+            goto cleanup; \
+        } \
+        (_to) = (_from); \
+    } while (0)
+
+# define HYPER_TO_LONG(_to, _from) HYPER_TO_TYPE(long, _to, _from)
+# define HYPER_TO_ULONG(_to, _from) HYPER_TO_TYPE(unsigned long, _to, _from)
+#else
+# define HYPER_TO_LONG(_to, _from) (_to) = (_from)
+# define HYPER_TO_ULONG(_to, _from) (_to) = (_from)
+#endif
+
+struct daemonClientEventCallback {
+    virNetServerClientPtr client;
+    int eventID;
+    int callbackID;
+    bool legacy;
+};
+
+static virDomainPtr get_nonnull_domain(virConnectPtr conn, remote_nonnull_domain domain);
+static virNetworkPtr get_nonnull_network(virConnectPtr conn, remote_nonnull_network network);
+static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface);
+static virStoragePoolPtr get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool);
+static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol);
+static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret);
+static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter);
+static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot);
+static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev);
+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);
+static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src);
+static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src);
+static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src);
+static void make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src);
+static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src);
+static void make_nonnull_nwfilter(remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src);
+static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src);
+
+static int
+remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors,
+                                int nerrors,
+                                remote_domain_disk_error **ret_errors_val,
+                                u_int *ret_errors_len);
+
+#include "remote_daemon_dispatch_stubs.h"
+#include "remote_daemon_dispatch_qemu_stubs.h"
+#include "remote_daemon_dispatch_lxc_stubs.h"
+
+
+/* Prototypes */
+static void
+remoteDispatchObjectEventSend(virNetServerClientPtr client,
+                              virNetServerProgramPtr program,
+                              int procnr,
+                              xdrproc_t proc,
+                              void *data);
+
+static void
+remoteEventCallbackFree(void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    if (!callback)
+        return;
+    virObjectUnref(callback->client);
+    VIR_FREE(callback);
+}
+
+
+static bool
+remoteRelayDomainEventCheckACL(virNetServerClientPtr client,
+                               virConnectPtr conn, virDomainPtr dom)
+{
+    virDomainDef def;
+    virIdentityPtr identity = NULL;
+    bool ret = false;
+
+    /* For now, we just create a virDomainDef with enough contents to
+     * satisfy what viraccessdriverpolkit.c references.  This is a bit
+     * fragile, but I don't know of anything better.  */
+    memset(&def, 0, sizeof(def));
+    def.name = dom->name;
+    memcpy(def.uuid, dom->uuid, VIR_UUID_BUFLEN);
+
+    if (!(identity = virNetServerClientGetIdentity(client)))
+        goto cleanup;
+    if (virIdentitySetCurrent(identity) < 0)
+        goto cleanup;
+    ret = virConnectDomainEventRegisterAnyCheckACL(conn, &def);
+
+ cleanup:
+    ignore_value(virIdentitySetCurrent(NULL));
+    virObjectUnref(identity);
+    return ret;
+}
+
+
+static bool
+remoteRelayNetworkEventCheckACL(virNetServerClientPtr client,
+                                virConnectPtr conn, virNetworkPtr net)
+{
+    virNetworkDef def;
+    virIdentityPtr identity = NULL;
+    bool ret = false;
+
+    /* For now, we just create a virNetworkDef with enough contents to
+     * satisfy what viraccessdriverpolkit.c references.  This is a bit
+     * fragile, but I don't know of anything better.  */
+    def.name = net->name;
+    memcpy(def.uuid, net->uuid, VIR_UUID_BUFLEN);
+
+    if (!(identity = virNetServerClientGetIdentity(client)))
+        goto cleanup;
+    if (virIdentitySetCurrent(identity) < 0)
+        goto cleanup;
+    ret = virConnectNetworkEventRegisterAnyCheckACL(conn, &def);
+
+ cleanup:
+    ignore_value(virIdentitySetCurrent(NULL));
+    virObjectUnref(identity);
+    return ret;
+}
+
+static bool
+remoteRelayStoragePoolEventCheckACL(virNetServerClientPtr client,
+                                    virConnectPtr conn,
+                                    virStoragePoolPtr pool)
+{
+    virStoragePoolDef def;
+    virIdentityPtr identity = NULL;
+    bool ret = false;
+
+    /* For now, we just create a virStoragePoolDef with enough contents to
+     * satisfy what viraccessdriverpolkit.c references.  This is a bit
+     * fragile, but I don't know of anything better.  */
+    def.name = pool->name;
+    memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN);
+
+    if (!(identity = virNetServerClientGetIdentity(client)))
+        goto cleanup;
+    if (virIdentitySetCurrent(identity) < 0)
+        goto cleanup;
+    ret = virConnectStoragePoolEventRegisterAnyCheckACL(conn, &def);
+
+ cleanup:
+    ignore_value(virIdentitySetCurrent(NULL));
+    virObjectUnref(identity);
+    return ret;
+}
+
+static bool
+remoteRelayNodeDeviceEventCheckACL(virNetServerClientPtr client,
+                                   virConnectPtr conn,
+                                   virNodeDevicePtr dev)
+{
+    virNodeDeviceDef def;
+    virIdentityPtr identity = NULL;
+    bool ret = false;
+
+    /* For now, we just create a virNodeDeviceDef with enough contents to
+     * satisfy what viraccessdriverpolkit.c references.  This is a bit
+     * fragile, but I don't know of anything better.  */
+    def.name = dev->name;
+
+    if (!(identity = virNetServerClientGetIdentity(client)))
+        goto cleanup;
+    if (virIdentitySetCurrent(identity) < 0)
+        goto cleanup;
+    ret = virConnectNodeDeviceEventRegisterAnyCheckACL(conn, &def);
+
+ cleanup:
+    ignore_value(virIdentitySetCurrent(NULL));
+    virObjectUnref(identity);
+    return ret;
+}
+
+static bool
+remoteRelaySecretEventCheckACL(virNetServerClientPtr client,
+                               virConnectPtr conn,
+                               virSecretPtr secret)
+{
+    virSecretDef def;
+    virIdentityPtr identity = NULL;
+    bool ret = false;
+
+    /* For now, we just create a virSecretDef with enough contents to
+     * satisfy what viraccessdriverpolkit.c references.  This is a bit
+     * fragile, but I don't know of anything better.  */
+    memcpy(def.uuid, secret->uuid, VIR_UUID_BUFLEN);
+    def.usage_type = secret->usageType;
+    def.usage_id = secret->usageID;
+
+    if (!(identity = virNetServerClientGetIdentity(client)))
+        goto cleanup;
+    if (virIdentitySetCurrent(identity) < 0)
+        goto cleanup;
+    ret = virConnectSecretEventRegisterAnyCheckACL(conn, &def);
+
+ cleanup:
+    ignore_value(virIdentitySetCurrent(NULL));
+    virObjectUnref(identity);
+    return ret;
+}
+
+static bool
+remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client,
+                                          virConnectPtr conn, virDomainPtr dom)
+{
+    virDomainDef def;
+    virIdentityPtr identity = NULL;
+    bool ret = false;
+
+    /* For now, we just create a virDomainDef with enough contents to
+     * satisfy what viraccessdriverpolkit.c references.  This is a bit
+     * fragile, but I don't know of anything better.  */
+    def.name = dom->name;
+    memcpy(def.uuid, dom->uuid, VIR_UUID_BUFLEN);
+
+    if (!(identity = virNetServerClientGetIdentity(client)))
+        goto cleanup;
+    if (virIdentitySetCurrent(identity) < 0)
+        goto cleanup;
+    ret = virConnectDomainQemuMonitorEventRegisterCheckACL(conn, &def);
+
+ cleanup:
+    ignore_value(virIdentitySetCurrent(NULL));
+    virObjectUnref(identity);
+    return ret;
+}
+
+
+static int
+remoteRelayDomainEventLifecycle(virConnectPtr conn,
+                                virDomainPtr dom,
+                                int event,
+                                int detail,
+                                void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_lifecycle_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain lifecycle event %d %d, callback %d legacy %d",
+              event, detail, callback->callbackID, callback->legacy);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+    data.event = event;
+    data.detail = detail;
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE,
+                                      (xdrproc_t)xdr_remote_domain_event_lifecycle_msg,
+                                      &data);
+    } else {
+        remote_domain_event_callback_lifecycle_msg msg = { callback->callbackID,
+                                                           data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_LIFECYCLE,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_lifecycle_msg,
+                                      &msg);
+    }
+
+    return 0;
+}
+
+static int
+remoteRelayDomainEventReboot(virConnectPtr conn,
+                             virDomainPtr dom,
+                             void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_reboot_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain reboot event %s %d, callback %d legacy %d",
+              dom->name, dom->id, callback->callbackID, callback->legacy);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_REBOOT,
+                                      (xdrproc_t)xdr_remote_domain_event_reboot_msg, &data);
+    } else {
+        remote_domain_event_callback_reboot_msg msg = { callback->callbackID,
+                                                        data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_REBOOT,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_reboot_msg, &msg);
+    }
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventRTCChange(virConnectPtr conn,
+                                virDomainPtr dom,
+                                long long offset,
+                                void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_rtc_change_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain rtc change event %s %d %lld, callback %d legacy %d",
+              dom->name, dom->id, offset,
+              callback->callbackID, callback->legacy);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+    data.offset = offset;
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_rtc_change_msg, &data);
+    } else {
+        remote_domain_event_callback_rtc_change_msg msg = { callback->callbackID,
+                                                            data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_RTC_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_rtc_change_msg, &msg);
+    }
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventWatchdog(virConnectPtr conn,
+                               virDomainPtr dom,
+                               int action,
+                               void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_watchdog_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain watchdog event %s %d %d, callback %d",
+              dom->name, dom->id, action, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+    data.action = action;
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_WATCHDOG,
+                                      (xdrproc_t)xdr_remote_domain_event_watchdog_msg, &data);
+    } else {
+        remote_domain_event_callback_watchdog_msg msg = { callback->callbackID,
+                                                          data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_WATCHDOG,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_watchdog_msg, &msg);
+    }
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventIOError(virConnectPtr conn,
+                              virDomainPtr dom,
+                              const char *srcPath,
+                              const char *devAlias,
+                              int action,
+                              void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_io_error_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain io error %s %d %s %s %d, callback %d",
+              dom->name, dom->id, srcPath, devAlias, action,
+              callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    if (VIR_STRDUP(data.srcPath, srcPath) < 0 ||
+        VIR_STRDUP(data.devAlias, devAlias) < 0)
+        goto error;
+    make_nonnull_domain(&data.dom, dom);
+    data.action = action;
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_IO_ERROR,
+                                      (xdrproc_t)xdr_remote_domain_event_io_error_msg, &data);
+    } else {
+        remote_domain_event_callback_io_error_msg msg = { callback->callbackID,
+                                                          data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_IO_ERROR,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_io_error_msg, &msg);
+    }
+
+    return 0;
+ error:
+    VIR_FREE(data.srcPath);
+    VIR_FREE(data.devAlias);
+    return -1;
+}
+
+
+static int
+remoteRelayDomainEventIOErrorReason(virConnectPtr conn,
+                                    virDomainPtr dom,
+                                    const char *srcPath,
+                                    const char *devAlias,
+                                    int action,
+                                    const char *reason,
+                                    void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_io_error_reason_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain io error %s %d %s %s %d %s, callback %d",
+              dom->name, dom->id, srcPath, devAlias, action, reason,
+              callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    if (VIR_STRDUP(data.srcPath, srcPath) < 0 ||
+        VIR_STRDUP(data.devAlias, devAlias) < 0 ||
+        VIR_STRDUP(data.reason, reason) < 0)
+        goto error;
+    data.action = action;
+
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON,
+                                      (xdrproc_t)xdr_remote_domain_event_io_error_reason_msg, &data);
+    } else {
+        remote_domain_event_callback_io_error_reason_msg msg = { callback->callbackID,
+                                                                 data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_IO_ERROR_REASON,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_io_error_reason_msg, &msg);
+    }
+
+    return 0;
+
+ error:
+    VIR_FREE(data.srcPath);
+    VIR_FREE(data.devAlias);
+    VIR_FREE(data.reason);
+    return -1;
+}
+
+
+static int
+remoteRelayDomainEventGraphics(virConnectPtr conn,
+                               virDomainPtr dom,
+                               int phase,
+                               virDomainEventGraphicsAddressPtr local,
+                               virDomainEventGraphicsAddressPtr remote,
+                               const char *authScheme,
+                               virDomainEventGraphicsSubjectPtr subject,
+                               void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_graphics_msg data;
+    size_t i;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain graphics event %s %d %d - %d %s %s  - %d %s %s - %s, callback %d",
+              dom->name, dom->id, phase,
+              local->family, local->service, local->node,
+              remote->family, remote->service, remote->node,
+              authScheme, callback->callbackID);
+
+    VIR_DEBUG("Subject %d", subject->nidentity);
+    for (i = 0; i < subject->nidentity; i++)
+        VIR_DEBUG("  %s=%s", subject->identities[i].type, subject->identities[i].name);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.phase = phase;
+    data.local.family = local->family;
+    data.remote.family = remote->family;
+    if (VIR_STRDUP(data.authScheme, authScheme) < 0 ||
+        VIR_STRDUP(data.local.node, local->node) < 0 ||
+        VIR_STRDUP(data.local.service, local->service) < 0 ||
+        VIR_STRDUP(data.remote.node, remote->node) < 0 ||
+        VIR_STRDUP(data.remote.service, remote->service) < 0)
+        goto error;
+
+    data.subject.subject_len = subject->nidentity;
+    if (VIR_ALLOC_N(data.subject.subject_val, data.subject.subject_len) < 0)
+        goto error;
+
+    for (i = 0; i < data.subject.subject_len; i++) {
+        if (VIR_STRDUP(data.subject.subject_val[i].type, subject->identities[i].type) < 0 ||
+            VIR_STRDUP(data.subject.subject_val[i].name, subject->identities[i].name) < 0)
+            goto error;
+    }
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_GRAPHICS,
+                                      (xdrproc_t)xdr_remote_domain_event_graphics_msg, &data);
+    } else {
+        remote_domain_event_callback_graphics_msg msg = { callback->callbackID,
+                                                          data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_GRAPHICS,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_graphics_msg, &msg);
+    }
+
+    return 0;
+
+ error:
+    VIR_FREE(data.authScheme);
+    VIR_FREE(data.local.node);
+    VIR_FREE(data.local.service);
+    VIR_FREE(data.remote.node);
+    VIR_FREE(data.remote.service);
+    if (data.subject.subject_val != NULL) {
+        for (i = 0; i < data.subject.subject_len; i++) {
+            VIR_FREE(data.subject.subject_val[i].type);
+            VIR_FREE(data.subject.subject_val[i].name);
+        }
+        VIR_FREE(data.subject.subject_val);
+    }
+    return -1;
+}
+
+static int
+remoteRelayDomainEventBlockJob(virConnectPtr conn,
+                               virDomainPtr dom,
+                               const char *path,
+                               int type,
+                               int status,
+                               void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_block_job_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain block job event %s %d %s %i, %i, callback %d",
+              dom->name, dom->id, path, type, status, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    if (VIR_STRDUP(data.path, path) < 0)
+        return -1;
+    data.type = type;
+    data.status = status;
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB,
+                                      (xdrproc_t)xdr_remote_domain_event_block_job_msg, &data);
+    } else {
+        remote_domain_event_callback_block_job_msg msg = { callback->callbackID,
+                                                           data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_BLOCK_JOB,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_block_job_msg, &msg);
+    }
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventControlError(virConnectPtr conn,
+                                   virDomainPtr dom,
+                                   void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_control_error_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain control error %s %d, callback %d",
+              dom->name, dom->id, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR,
+                                      (xdrproc_t)xdr_remote_domain_event_control_error_msg, &data);
+    } else {
+        remote_domain_event_callback_control_error_msg msg = { callback->callbackID,
+                                                               data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CONTROL_ERROR,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_control_error_msg, &msg);
+    }
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventDiskChange(virConnectPtr conn,
+                                 virDomainPtr dom,
+                                 const char *oldSrcPath,
+                                 const char *newSrcPath,
+                                 const char *devAlias,
+                                 int reason,
+                                 void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_disk_change_msg data;
+    char **oldSrcPath_p = NULL, **newSrcPath_p = NULL;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain %s %d disk change %s %s %s %d, callback %d",
+              dom->name, dom->id, oldSrcPath, newSrcPath, devAlias, reason,
+              callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    if (oldSrcPath &&
+        ((VIR_ALLOC(oldSrcPath_p) < 0) ||
+         VIR_STRDUP(*oldSrcPath_p, oldSrcPath) < 0))
+        goto error;
+
+    if (newSrcPath &&
+        ((VIR_ALLOC(newSrcPath_p) < 0) ||
+         VIR_STRDUP(*newSrcPath_p, newSrcPath) < 0))
+        goto error;
+
+    data.oldSrcPath = oldSrcPath_p;
+    data.newSrcPath = newSrcPath_p;
+    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
+        goto error;
+    data.reason = reason;
+
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_DISK_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_disk_change_msg, &data);
+    } else {
+        remote_domain_event_callback_disk_change_msg msg = { callback->callbackID,
+                                                             data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DISK_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_disk_change_msg, &msg);
+    }
+
+    return 0;
+
+ error:
+    VIR_FREE(oldSrcPath_p);
+    VIR_FREE(newSrcPath_p);
+    return -1;
+}
+
+
+static int
+remoteRelayDomainEventTrayChange(virConnectPtr conn,
+                                 virDomainPtr dom,
+                                 const char *devAlias,
+                                 int reason,
+                                 void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_tray_change_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain %s %d tray change devAlias: %s reason: %d, callback %d",
+              dom->name, dom->id, devAlias, reason, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+
+    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
+        return -1;
+    data.reason = reason;
+
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_TRAY_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_tray_change_msg, &data);
+    } else {
+        remote_domain_event_callback_tray_change_msg msg = { callback->callbackID,
+                                                             data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_TRAY_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_tray_change_msg, &msg);
+    }
+
+    return 0;
+}
+
+static int
+remoteRelayDomainEventPMWakeup(virConnectPtr conn,
+                               virDomainPtr dom,
+                               int reason,
+                               void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_pmwakeup_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain %s %d system pmwakeup, callback %d",
+              dom->name, dom->id, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_PMWAKEUP,
+                                      (xdrproc_t)xdr_remote_domain_event_pmwakeup_msg, &data);
+    } else {
+        remote_domain_event_callback_pmwakeup_msg msg = { callback->callbackID,
+                                                          reason, data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_PMWAKEUP,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_pmwakeup_msg, &msg);
+    }
+
+    return 0;
+}
+
+static int
+remoteRelayDomainEventPMSuspend(virConnectPtr conn,
+                                virDomainPtr dom,
+                                int reason,
+                                void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_pmsuspend_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain %s %d system pmsuspend, callback %d",
+              dom->name, dom->id, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND,
+                                      (xdrproc_t)xdr_remote_domain_event_pmsuspend_msg, &data);
+    } else {
+        remote_domain_event_callback_pmsuspend_msg msg = { callback->callbackID,
+                                                           reason, data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_PMSUSPEND,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_pmsuspend_msg, &msg);
+    }
+
+    return 0;
+}
+
+static int
+remoteRelayDomainEventBalloonChange(virConnectPtr conn,
+                                    virDomainPtr dom,
+                                    unsigned long long actual,
+                                    void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_balloon_change_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain balloon change event %s %d %lld, callback %d",
+              dom->name, dom->id, actual, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+    data.actual = actual;
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_balloon_change_msg, &data);
+    } else {
+        remote_domain_event_callback_balloon_change_msg msg = { callback->callbackID,
+                                                                data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_BALLOON_CHANGE,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_balloon_change_msg, &msg);
+    }
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventPMSuspendDisk(virConnectPtr conn,
+                                    virDomainPtr dom,
+                                    int reason,
+                                    void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_pmsuspend_disk_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain %s %d system pmsuspend-disk, callback %d",
+              dom->name, dom->id, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND_DISK,
+                                      (xdrproc_t)xdr_remote_domain_event_pmsuspend_disk_msg, &data);
+    } else {
+        remote_domain_event_callback_pmsuspend_disk_msg msg = { callback->callbackID,
+                                                                reason, data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_PMSUSPEND_DISK,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_pmsuspend_disk_msg, &msg);
+    }
+
+    return 0;
+}
+
+static int
+remoteRelayDomainEventDeviceRemoved(virConnectPtr conn,
+                                    virDomainPtr dom,
+                                    const char *devAlias,
+                                    void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_device_removed_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain device removed event %s %d %s, callback %d",
+              dom->name, dom->id, devAlias, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+
+    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
+        return -1;
+
+    make_nonnull_domain(&data.dom, dom);
+
+    if (callback->legacy) {
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_DEVICE_REMOVED,
+                                      (xdrproc_t)xdr_remote_domain_event_device_removed_msg,
+                                      &data);
+    } else {
+        remote_domain_event_callback_device_removed_msg msg = { callback->callbackID,
+                                                                data };
+
+        remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                      REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVED,
+                                      (xdrproc_t)xdr_remote_domain_event_callback_device_removed_msg,
+                                      &msg);
+    }
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventBlockJob2(virConnectPtr conn,
+                                virDomainPtr dom,
+                                const char *dst,
+                                int type,
+                                int status,
+                                void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_block_job_2_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain block job 2 event %s %d %s %i, %i, callback %d",
+              dom->name, dom->id, dst, type, status, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    if (VIR_STRDUP(data.dst, dst) < 0)
+        return -1;
+    data.type = type;
+    data.status = status;
+    make_nonnull_domain(&data.dom, dom);
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_2,
+                                  (xdrproc_t)xdr_remote_domain_event_block_job_2_msg, &data);
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventTunable(virConnectPtr conn,
+                              virDomainPtr dom,
+                              virTypedParameterPtr params,
+                              int nparams,
+                              void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_callback_tunable_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain tunable event %s %d, callback %d, params %p %d",
+              dom->name, dom->id, callback->callbackID, params, nparams);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    make_nonnull_domain(&data.dom, dom);
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &data.params.params_val,
+                                &data.params.params_len,
+                                VIR_TYPED_PARAM_STRING_OKAY) < 0) {
+        VIR_FREE(data.dom.name);
+        return -1;
+    }
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_TUNABLE,
+                                  (xdrproc_t)xdr_remote_domain_event_callback_tunable_msg,
+                                  &data);
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventAgentLifecycle(virConnectPtr conn,
+                                     virDomainPtr dom,
+                                     int state,
+                                     int reason,
+                                     void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_callback_agent_lifecycle_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain agent lifecycle event %s %d, callback %d, "
+              " state %d, reason %d",
+              dom->name, dom->id, callback->callbackID, state, reason);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    make_nonnull_domain(&data.dom, dom);
+
+    data.state = state;
+    data.reason = reason;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE,
+                                  (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_msg,
+                                  &data);
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventDeviceAdded(virConnectPtr conn,
+                                  virDomainPtr dom,
+                                  const char *devAlias,
+                                  void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_callback_device_added_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain device added event %s %d %s, callback %d",
+              dom->name, dom->id, devAlias, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+
+    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
+        return -1;
+
+    make_nonnull_domain(&data.dom, dom);
+    data.callbackID = callback->callbackID;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_ADDED,
+                                  (xdrproc_t)xdr_remote_domain_event_callback_device_added_msg,
+                                  &data);
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventMigrationIteration(virConnectPtr conn,
+                                         virDomainPtr dom,
+                                         int iteration,
+                                         void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_callback_migration_iteration_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain migration pass event %s %d, "
+              "callback %d, iteration %d",
+              dom->name, dom->id, callback->callbackID, iteration);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    make_nonnull_domain(&data.dom, dom);
+
+    data.iteration = iteration;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_MIGRATION_ITERATION,
+                                  (xdrproc_t)xdr_remote_domain_event_callback_migration_iteration_msg,
+                                  &data);
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventJobCompleted(virConnectPtr conn,
+                                   virDomainPtr dom,
+                                   virTypedParameterPtr params,
+                                   int nparams,
+                                   void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_callback_job_completed_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain migration completed event %s %d, "
+              "callback %d, params %p %d",
+              dom->name, dom->id, callback->callbackID, params, nparams);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    make_nonnull_domain(&data.dom, dom);
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &data.params.params_val,
+                                &data.params.params_len,
+                                VIR_TYPED_PARAM_STRING_OKAY) < 0) {
+        VIR_FREE(data.dom.name);
+        return -1;
+    }
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_JOB_COMPLETED,
+                                  (xdrproc_t)xdr_remote_domain_event_callback_job_completed_msg,
+                                  &data);
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventDeviceRemovalFailed(virConnectPtr conn,
+                                          virDomainPtr dom,
+                                          const char *devAlias,
+                                          void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_callback_device_removal_failed_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain device removal failed event %s %d %s, callback %d",
+              dom->name, dom->id, devAlias, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+
+    if (VIR_STRDUP(data.devAlias, devAlias) < 0)
+        return -1;
+
+    make_nonnull_domain(&data.dom, dom);
+    data.callbackID = callback->callbackID;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED,
+                                  (xdrproc_t)xdr_remote_domain_event_callback_device_removal_failed_msg,
+                                  &data);
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventMetadataChange(virConnectPtr conn,
+                                     virDomainPtr dom,
+                                     int type,
+                                     const char *nsuri,
+                                     void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_callback_metadata_change_msg data;
+    char **nsurip;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain metadata change %s %d %d %s, callback %d",
+              dom->name, dom->id, type, NULLSTR(nsuri), callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+
+    data.type = type;
+    if (nsuri) {
+        if (VIR_ALLOC(nsurip) < 0)
+            return -1;
+        if (VIR_STRDUP(*nsurip, nsuri) < 0) {
+            VIR_FREE(nsurip);
+            return -1;
+        }
+        data.nsuri = nsurip;
+    }
+
+    make_nonnull_domain(&data.dom, dom);
+    data.callbackID = callback->callbackID;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE,
+                                  (xdrproc_t)xdr_remote_domain_event_callback_metadata_change_msg,
+                                  &data);
+
+    return 0;
+}
+
+
+static int
+remoteRelayDomainEventBlockThreshold(virConnectPtr conn,
+                                     virDomainPtr dom,
+                                     const char *dev,
+                                     const char *path,
+                                     unsigned long long threshold,
+                                     unsigned long long excess,
+                                     void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_block_threshold_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain block threshold event %s %d %s %s %llu %llu, callback %d",
+              dom->name, dom->id, dev, NULLSTR(path), threshold, excess, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    if (VIR_STRDUP(data.dev, dev) < 0)
+        goto error;
+    if (path) {
+        if (VIR_ALLOC(data.path) < 0)
+            goto error;
+        if (VIR_STRDUP(*(data.path), path) < 0)
+            goto error;
+    }
+    data.threshold = threshold;
+    data.excess = excess;
+    make_nonnull_domain(&data.dom, dom);
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD,
+                                  (xdrproc_t)xdr_remote_domain_event_block_threshold_msg, &data);
+
+    return 0;
+ error:
+    VIR_FREE(data.dev);
+    return -1;
+}
+
+
+static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventWatchdog),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskChange),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTrayChange),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMWakeup),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspend),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBalloonChange),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspendDisk),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemoved),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob2),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTunable),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventAgentLifecycle),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceAdded),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMigrationIteration),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold),
+};
+
+verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
+
+static int
+remoteRelayNetworkEventLifecycle(virConnectPtr conn,
+                                 virNetworkPtr net,
+                                 int event,
+                                 int detail,
+                                 void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_network_event_lifecycle_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayNetworkEventCheckACL(callback->client, conn, net))
+        return -1;
+
+    VIR_DEBUG("Relaying network lifecycle event %d, detail %d, callback %d",
+              event, detail, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_network(&data.net, net);
+    data.callbackID = callback->callbackID;
+    data.event = event;
+    data.detail = detail;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_NETWORK_EVENT_LIFECYCLE,
+                                  (xdrproc_t)xdr_remote_network_event_lifecycle_msg, &data);
+
+    return 0;
+}
+
+static virConnectNetworkEventGenericCallback networkEventCallbacks[] = {
+    VIR_NETWORK_EVENT_CALLBACK(remoteRelayNetworkEventLifecycle),
+};
+
+verify(ARRAY_CARDINALITY(networkEventCallbacks) == VIR_NETWORK_EVENT_ID_LAST);
+
+static int
+remoteRelayStoragePoolEventLifecycle(virConnectPtr conn,
+                                     virStoragePoolPtr pool,
+                                     int event,
+                                     int detail,
+                                     void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_storage_pool_event_lifecycle_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayStoragePoolEventCheckACL(callback->client, conn, pool))
+        return -1;
+
+    VIR_DEBUG("Relaying storage pool lifecycle event %d, detail %d, callback %d",
+              event, detail, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_storage_pool(&data.pool, pool);
+    data.callbackID = callback->callbackID;
+    data.event = event;
+    data.detail = detail;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE,
+                                  (xdrproc_t)xdr_remote_storage_pool_event_lifecycle_msg,
+                                  &data);
+
+    return 0;
+}
+
+static int
+remoteRelayStoragePoolEventRefresh(virConnectPtr conn,
+                                   virStoragePoolPtr pool,
+                                   void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_storage_pool_event_refresh_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayStoragePoolEventCheckACL(callback->client, conn, pool))
+        return -1;
+
+    VIR_DEBUG("Relaying storage pool refresh event callback %d",
+              callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_storage_pool(&data.pool, pool);
+    data.callbackID = callback->callbackID;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH,
+                                  (xdrproc_t)xdr_remote_storage_pool_event_refresh_msg,
+                                  &data);
+
+    return 0;
+}
+
+static virConnectStoragePoolEventGenericCallback storageEventCallbacks[] = {
+    VIR_STORAGE_POOL_EVENT_CALLBACK(remoteRelayStoragePoolEventLifecycle),
+    VIR_STORAGE_POOL_EVENT_CALLBACK(remoteRelayStoragePoolEventRefresh),
+};
+
+verify(ARRAY_CARDINALITY(storageEventCallbacks) == VIR_STORAGE_POOL_EVENT_ID_LAST);
+
+static int
+remoteRelayNodeDeviceEventLifecycle(virConnectPtr conn,
+                                    virNodeDevicePtr dev,
+                                    int event,
+                                    int detail,
+                                    void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_node_device_event_lifecycle_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayNodeDeviceEventCheckACL(callback->client, conn, dev))
+        return -1;
+
+    VIR_DEBUG("Relaying node device lifecycle event %d, detail %d, callback %d",
+              event, detail, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_node_device(&data.dev, dev);
+    data.callbackID = callback->callbackID;
+    data.event = event;
+    data.detail = detail;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE,
+                                  (xdrproc_t)xdr_remote_node_device_event_lifecycle_msg,
+                                  &data);
+
+    return 0;
+}
+
+static int
+remoteRelayNodeDeviceEventUpdate(virConnectPtr conn,
+                                 virNodeDevicePtr dev,
+                                 void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_node_device_event_update_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayNodeDeviceEventCheckACL(callback->client, conn, dev))
+        return -1;
+
+    VIR_DEBUG("Relaying node device update event callback %d",
+              callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_node_device(&data.dev, dev);
+    data.callbackID = callback->callbackID;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE,
+                                  (xdrproc_t)xdr_remote_node_device_event_update_msg,
+                                  &data);
+
+    return 0;
+}
+
+static virConnectNodeDeviceEventGenericCallback nodeDeviceEventCallbacks[] = {
+    VIR_NODE_DEVICE_EVENT_CALLBACK(remoteRelayNodeDeviceEventLifecycle),
+    VIR_NODE_DEVICE_EVENT_CALLBACK(remoteRelayNodeDeviceEventUpdate),
+};
+
+verify(ARRAY_CARDINALITY(nodeDeviceEventCallbacks) == VIR_NODE_DEVICE_EVENT_ID_LAST);
+
+static int
+remoteRelaySecretEventLifecycle(virConnectPtr conn,
+                                virSecretPtr secret,
+                                int event,
+                                int detail,
+                                void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_secret_event_lifecycle_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelaySecretEventCheckACL(callback->client, conn, secret))
+        return -1;
+
+    VIR_DEBUG("Relaying node secretice lifecycle event %d, detail %d, callback %d",
+              event, detail, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_secret(&data.secret, secret);
+    data.callbackID = callback->callbackID;
+    data.event = event;
+    data.detail = detail;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_SECRET_EVENT_LIFECYCLE,
+                                  (xdrproc_t)xdr_remote_secret_event_lifecycle_msg,
+                                  &data);
+
+    return 0;
+}
+
+static int
+remoteRelaySecretEventValueChanged(virConnectPtr conn,
+                                   virSecretPtr secret,
+                                   void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_secret_event_value_changed_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelaySecretEventCheckACL(callback->client, conn, secret))
+        return -1;
+
+    VIR_DEBUG("Relaying node secret value changed callback %d",
+              callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_secret(&data.secret, secret);
+    data.callbackID = callback->callbackID;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED,
+                                  (xdrproc_t)xdr_remote_secret_event_value_changed_msg,
+                                  &data);
+
+    return 0;
+}
+
+static virConnectSecretEventGenericCallback secretEventCallbacks[] = {
+    VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventLifecycle),
+    VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventValueChanged),
+};
+
+verify(ARRAY_CARDINALITY(secretEventCallbacks) == VIR_SECRET_EVENT_ID_LAST);
+
+static void
+remoteRelayDomainQemuMonitorEvent(virConnectPtr conn,
+                                  virDomainPtr dom,
+                                  const char *event,
+                                  long long seconds,
+                                  unsigned int micros,
+                                  const char *details,
+                                  void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    qemu_domain_monitor_event_msg data;
+    char **details_p = NULL;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainQemuMonitorEventCheckACL(callback->client, conn,
+                                                   dom))
+        return;
+
+    VIR_DEBUG("Relaying qemu monitor event %s %s, callback %d",
+              event, details, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    if (VIR_STRDUP(data.event, event) < 0)
+        goto error;
+    data.seconds = seconds;
+    data.micros = micros;
+    if (details &&
+        ((VIR_ALLOC(details_p) < 0) ||
+         VIR_STRDUP(*details_p, details) < 0))
+        goto error;
+    data.details = details_p;
+    make_nonnull_domain(&data.dom, dom);
+
+    remoteDispatchObjectEventSend(callback->client, qemuProgram,
+                                  QEMU_PROC_DOMAIN_MONITOR_EVENT,
+                                  (xdrproc_t)xdr_qemu_domain_monitor_event_msg,
+                                  &data);
+    return;
+
+ error:
+    VIR_FREE(data.event);
+    VIR_FREE(details_p);
+}
+
+static
+void remoteRelayConnectionClosedEvent(virConnectPtr conn ATTRIBUTE_UNUSED, int reason, void *opaque)
+{
+    virNetServerClientPtr client = opaque;
+
+    VIR_DEBUG("Relaying connection closed event, reason %d", reason);
+
+    remote_connect_event_connection_closed_msg msg = { reason };
+    remoteDispatchObjectEventSend(client, remoteProgram,
+                                  REMOTE_PROC_CONNECT_EVENT_CONNECTION_CLOSED,
+                                  (xdrproc_t)xdr_remote_connect_event_connection_closed_msg,
+                                  &msg);
+}
+
+#define DEREG_CB(conn, eventCallbacks, neventCallbacks, deregFcn, name) \
+    do { \
+        size_t i; \
+        for (i = 0; i < neventCallbacks; i++) { \
+            int callbackID = eventCallbacks[i]->callbackID; \
+            if (callbackID < 0) { \
+                VIR_WARN("unexpected incomplete %s callback %zu", name, i); \
+                continue; \
+            } \
+            VIR_DEBUG("Deregistering remote %s event relay %d", \
+                      name, callbackID); \
+            eventCallbacks[i]->callbackID = -1; \
+            if (deregFcn(conn, callbackID) < 0) \
+                VIR_WARN("unexpected %s event deregister failure", name); \
+        } \
+        VIR_FREE(eventCallbacks); \
+        neventCallbacks = 0; \
+    } while (0);
+
+
+static void
+remoteClientFreePrivateCallbacks(struct daemonClientPrivate *priv)
+{
+    virIdentityPtr sysident = virIdentityGetSystem();
+    virIdentitySetCurrent(sysident);
+
+    DEREG_CB(priv->conn, priv->domainEventCallbacks,
+             priv->ndomainEventCallbacks,
+             virConnectDomainEventDeregisterAny, "domain");
+    DEREG_CB(priv->conn, priv->networkEventCallbacks,
+             priv->nnetworkEventCallbacks,
+             virConnectNetworkEventDeregisterAny, "network");
+    DEREG_CB(priv->conn, priv->storageEventCallbacks,
+             priv->nstorageEventCallbacks,
+             virConnectStoragePoolEventDeregisterAny, "storage");
+    DEREG_CB(priv->conn, priv->nodeDeviceEventCallbacks,
+             priv->nnodeDeviceEventCallbacks,
+             virConnectNodeDeviceEventDeregisterAny, "node device");
+    DEREG_CB(priv->conn, priv->secretEventCallbacks,
+             priv->nsecretEventCallbacks,
+             virConnectSecretEventDeregisterAny, "secret");
+    DEREG_CB(priv->conn, priv->qemuEventCallbacks,
+             priv->nqemuEventCallbacks,
+             virConnectDomainQemuMonitorEventDeregister, "qemu monitor");
+
+    if (priv->closeRegistered) {
+        if (virConnectUnregisterCloseCallback(priv->conn,
+                                              remoteRelayConnectionClosedEvent) < 0)
+            VIR_WARN("unexpected close callback event deregister failure");
+    }
+
+    virIdentitySetCurrent(NULL);
+    virObjectUnref(sysident);
+}
+#undef DEREG_CB
+
+
+/*
+ * You must hold lock for at least the client
+ * We don't free stuff here, merely disconnect the client's
+ * network socket & resources.
+ * We keep the libvirt connection open until any async
+ * jobs have finished, then clean it up elsewhere
+ */
+void remoteClientFree(void *data)
+{
+    struct daemonClientPrivate *priv = data;
+
+    if (priv->conn)
+        virConnectClose(priv->conn);
+
+    VIR_FREE(priv);
+}
+
+
+static void remoteClientCloseFunc(virNetServerClientPtr client)
+{
+    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
+
+    daemonRemoveAllClientStreams(priv->streams);
+
+    /* Deregister event delivery callback */
+    if (priv->conn)
+        remoteClientFreePrivateCallbacks(priv);
+}
+
+
+void *remoteClientNew(virNetServerClientPtr client,
+                      void *opaque ATTRIBUTE_UNUSED)
+{
+    struct daemonClientPrivate *priv;
+
+    if (VIR_ALLOC(priv) < 0)
+        return NULL;
+
+    if (virMutexInit(&priv->lock) < 0) {
+        VIR_FREE(priv);
+        virReportSystemError(errno, "%s", _("unable to init mutex"));
+        return NULL;
+    }
+
+    virNetServerClientSetCloseHook(client, remoteClientCloseFunc);
+    return priv;
+}
+
+/*----- Functions. -----*/
+
+static int
+remoteDispatchConnectOpen(virNetServerPtr server ATTRIBUTE_UNUSED,
+                          virNetServerClientPtr client,
+                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                          virNetMessageErrorPtr rerr,
+                          struct remote_connect_open_args *args)
+{
+    const char *name;
+    unsigned int flags;
+    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
+    int rv = -1;
+
+    VIR_DEBUG("priv=%p conn=%p", priv, priv->conn);
+    virMutexLock(&priv->lock);
+    /* Already opened? */
+    if (priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection already open"));
+        goto cleanup;
+    }
+
+    name = args->name ? *args->name : NULL;
+
+    /* If this connection arrived on a readonly socket, force
+     * the connection to be readonly.
+     */
+    flags = args->flags;
+    if (virNetServerClientGetReadonly(client))
+        flags |= VIR_CONNECT_RO;
+
+    priv->conn =
+        flags & VIR_CONNECT_RO
+        ? virConnectOpenReadOnly(name)
+        : virConnectOpen(name);
+
+    if (priv->conn == NULL)
+        goto cleanup;
+
+    /* force update the @readonly attribute which was inherited from the
+     * virNetServerService object - this is important for sockets that are RW
+     * by default, but do accept RO flags, e.g. TCP
+     */
+    virNetServerClientSetReadonly(client, (flags & VIR_CONNECT_RO));
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectClose(virNetServerPtr server ATTRIBUTE_UNUSED,
+                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                           virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED)
+{
+    virNetServerClientDelayedClose(client);
+    return 0;
+}
+
+
+static int
+remoteDispatchDomainGetSchedulerType(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                     virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                     virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                     virNetMessageErrorPtr rerr,
+                                     remote_domain_get_scheduler_type_args *args,
+                                     remote_domain_get_scheduler_type_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    char *type;
+    int nparams;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (!(type = virDomainGetSchedulerType(dom, &nparams)))
+        goto cleanup;
+
+    ret->type = type;
+    ret->nparams = nparams;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetSchedulerParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                           virNetMessageErrorPtr rerr,
+                                           remote_domain_get_scheduler_parameters_args *args,
+                                           remote_domain_get_scheduler_parameters_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetSchedulerParameters(dom, params, &nparams) < 0)
+        goto cleanup;
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                0) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetSchedulerParametersFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                virNetMessageErrorPtr rerr,
+                                                remote_domain_get_scheduler_parameters_flags_args *args,
+                                                remote_domain_get_scheduler_parameters_flags_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetSchedulerParametersFlags(dom, params, &nparams,
+                                             args->flags) < 0)
+        goto cleanup;
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                args->flags) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                virNetMessageErrorPtr rerr,
+                                remote_domain_memory_stats_args *args,
+                                remote_domain_memory_stats_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virDomainMemoryStatPtr stats = NULL;
+    int nr_stats;
+    size_t i;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    /* Allocate stats array for making dispatch call */
+    if (VIR_ALLOC_N(stats, args->maxStats) < 0)
+        goto cleanup;
+
+    nr_stats = virDomainMemoryStats(dom, stats, args->maxStats, args->flags);
+    if (nr_stats < 0)
+        goto cleanup;
+
+    /* Allocate return buffer */
+    if (VIR_ALLOC_N(ret->stats.stats_val, args->maxStats) < 0)
+        goto cleanup;
+
+    /* Copy the stats into the xdr return structure */
+    for (i = 0; i < nr_stats; i++) {
+        ret->stats.stats_val[i].tag = stats[i].tag;
+        ret->stats.stats_val[i].val = stats[i].val;
+    }
+    ret->stats.stats_len = nr_stats;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    VIR_FREE(stats);
+    return rv;
+}
+
+static int
+remoteDispatchDomainBlockPeek(virNetServerPtr server ATTRIBUTE_UNUSED,
+                              virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                              virNetMessageErrorPtr rerr,
+                              remote_domain_block_peek_args *args,
+                              remote_domain_block_peek_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    char *path;
+    unsigned long long offset;
+    size_t size;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+    path = args->path;
+    offset = args->offset;
+    size = args->size;
+    flags = args->flags;
+
+    if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("size > maximum buffer size"));
+        goto cleanup;
+    }
+
+    ret->buffer.buffer_len = size;
+    if (VIR_ALLOC_N(ret->buffer.buffer_val, size) < 0)
+        goto cleanup;
+
+    if (virDomainBlockPeek(dom, path, offset, size,
+                           ret->buffer.buffer_val, flags) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(ret->buffer.buffer_val);
+    }
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainBlockStatsFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                    virNetMessageErrorPtr rerr,
+                                    remote_domain_block_stats_flags_args *args,
+                                    remote_domain_block_stats_flags_ret *ret)
+{
+    virTypedParameterPtr params = NULL;
+    virDomainPtr dom = NULL;
+    const char *path = args->path;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_DOMAIN_BLOCK_STATS_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (virDomainBlockStatsFlags(dom, path, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    /* Serialize the block stats. */
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                args->flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainMemoryPeek(virNetServerPtr server ATTRIBUTE_UNUSED,
+                               virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                               virNetMessageErrorPtr rerr,
+                               remote_domain_memory_peek_args *args,
+                               remote_domain_memory_peek_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    unsigned long long offset;
+    size_t size;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+    offset = args->offset;
+    size = args->size;
+    flags = args->flags;
+
+    if (size > REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("size > maximum buffer size"));
+        goto cleanup;
+    }
+
+    ret->buffer.buffer_len = size;
+    if (VIR_ALLOC_N(ret->buffer.buffer_val, size) < 0)
+        goto cleanup;
+
+    if (virDomainMemoryPeek(dom, offset, size,
+                            ret->buffer.buffer_val, flags) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(ret->buffer.buffer_val);
+    }
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetSecurityLabel(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                     virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                     virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                     virNetMessageErrorPtr rerr,
+                                     remote_domain_get_security_label_args *args,
+                                     remote_domain_get_security_label_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virSecurityLabelPtr seclabel = NULL;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (VIR_ALLOC(seclabel) < 0)
+        goto cleanup;
+
+    if (virDomainGetSecurityLabel(dom, seclabel) < 0)
+        goto cleanup;
+
+    ret->label.label_len = strlen(seclabel->label) + 1;
+    if (VIR_ALLOC_N(ret->label.label_val, ret->label.label_len) < 0)
+        goto cleanup;
+    strcpy(ret->label.label_val, seclabel->label);
+    ret->enforcing = seclabel->enforcing;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    VIR_FREE(seclabel);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetSecurityLabelList(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                         virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                         virNetMessageErrorPtr rerr,
+                                         remote_domain_get_security_label_list_args *args,
+                                         remote_domain_get_security_label_list_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virSecurityLabelPtr seclabels = NULL;
+    int len, rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if ((len = virDomainGetSecurityLabelList(dom, &seclabels)) < 0) {
+        ret->ret = len;
+        ret->labels.labels_len = 0;
+        ret->labels.labels_val = NULL;
+        goto done;
+    }
+
+    if (VIR_ALLOC_N(ret->labels.labels_val, len) < 0)
+        goto cleanup;
+
+    for (i = 0; i < len; i++) {
+        size_t label_len = strlen(seclabels[i].label) + 1;
+        remote_domain_get_security_label_ret *cur = &ret->labels.labels_val[i];
+        if (VIR_ALLOC_N(cur->label.label_val, label_len) < 0)
+            goto cleanup;
+        if (virStrcpy(cur->label.label_val, seclabels[i].label, label_len) == NULL) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to copy security label"));
+            goto cleanup;
+        }
+        cur->label.label_len = label_len;
+        cur->enforcing = seclabels[i].enforcing;
+    }
+    ret->labels.labels_len = ret->ret = len;
+
+ done:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    VIR_FREE(seclabels);
+    return rv;
+}
+
+static int
+remoteDispatchNodeGetSecurityModel(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                   virNetMessageErrorPtr rerr,
+                                   remote_node_get_security_model_ret *ret)
+{
+    virSecurityModel secmodel;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    memset(&secmodel, 0, sizeof(secmodel));
+    if (virNodeGetSecurityModel(priv->conn, &secmodel) < 0)
+        goto cleanup;
+
+    ret->model.model_len = strlen(secmodel.model) + 1;
+    if (VIR_ALLOC_N(ret->model.model_val, ret->model.model_len) < 0)
+        goto cleanup;
+    strcpy(ret->model.model_val, secmodel.model);
+
+    ret->doi.doi_len = strlen(secmodel.doi) + 1;
+    if (VIR_ALLOC_N(ret->doi.doi_val, ret->doi.doi_len) < 0)
+        goto cleanup;
+    strcpy(ret->doi.doi_val, secmodel.doi);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetVcpuPinInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                   virNetMessageErrorPtr rerr,
+                                   remote_domain_get_vcpu_pin_info_args *args,
+                                   remote_domain_get_vcpu_pin_info_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    unsigned char *cpumaps = NULL;
+    int num;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (args->ncpumaps > REMOTE_VCPUINFO_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("ncpumaps > REMOTE_VCPUINFO_MAX"));
+        goto cleanup;
+    }
+
+    if (INT_MULTIPLY_OVERFLOW(args->ncpumaps, args->maplen) ||
+        args->ncpumaps * args->maplen > REMOTE_CPUMAPS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo * maplen > REMOTE_CPUMAPS_MAX"));
+        goto cleanup;
+    }
+
+    /* Allocate buffers to take the results. */
+    if (args->maplen > 0 &&
+        VIR_ALLOC_N(cpumaps, args->ncpumaps * args->maplen) < 0)
+        goto cleanup;
+
+    if ((num = virDomainGetVcpuPinInfo(dom,
+                                       args->ncpumaps,
+                                       cpumaps,
+                                       args->maplen,
+                                       args->flags)) < 0)
+        goto cleanup;
+
+    ret->num = num;
+    /* Don't need to allocate/copy the cpumaps if we make the reasonable
+     * assumption that unsigned char and char are the same size.
+     * Note that remoteDispatchClientRequest will free.
+     */
+    ret->cpumaps.cpumaps_len = args->ncpumaps * args->maplen;
+    ret->cpumaps.cpumaps_val = (char *) cpumaps;
+    cpumaps = NULL;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    VIR_FREE(cpumaps);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainPinEmulator(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                virNetServerClientPtr client,
+                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                virNetMessageErrorPtr rerr,
+                                remote_domain_pin_emulator_args *args)
+{
+    int rv = -1;
+    virDomainPtr dom = NULL;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainPinEmulator(dom,
+                             (unsigned char *) args->cpumap.cpumap_val,
+                             args->cpumap.cpumap_len,
+                             args->flags) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainGetEmulatorPinInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                       virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                       virNetMessageErrorPtr rerr,
+                                       remote_domain_get_emulator_pin_info_args *args,
+                                       remote_domain_get_emulator_pin_info_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    unsigned char *cpumaps = NULL;
+    int r;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    /* Allocate buffers to take the results */
+    if (args->maplen > 0 &&
+        VIR_ALLOC_N(cpumaps, args->maplen) < 0)
+        goto cleanup;
+
+    if ((r = virDomainGetEmulatorPinInfo(dom,
+                                         cpumaps,
+                                         args->maplen,
+                                         args->flags)) < 0)
+        goto cleanup;
+
+    ret->ret = r;
+    ret->cpumaps.cpumaps_len = args->maplen;
+    ret->cpumaps.cpumaps_val = (char *) cpumaps;
+    cpumaps = NULL;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    VIR_FREE(cpumaps);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetVcpus(virNetServerPtr server ATTRIBUTE_UNUSED,
+                             virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                             virNetMessageErrorPtr rerr,
+                             remote_domain_get_vcpus_args *args,
+                             remote_domain_get_vcpus_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virVcpuInfoPtr info = NULL;
+    unsigned char *cpumaps = NULL;
+    int info_len;
+    size_t i;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (args->maxinfo > REMOTE_VCPUINFO_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo > REMOTE_VCPUINFO_MAX"));
+        goto cleanup;
+    }
+
+    if (INT_MULTIPLY_OVERFLOW(args->maxinfo, args->maplen) ||
+        args->maxinfo * args->maplen > REMOTE_CPUMAPS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo * maplen > REMOTE_CPUMAPS_MAX"));
+        goto cleanup;
+    }
+
+    /* Allocate buffers to take the results. */
+    if (VIR_ALLOC_N(info, args->maxinfo) < 0)
+        goto cleanup;
+    if (args->maplen > 0 &&
+        VIR_ALLOC_N(cpumaps, args->maxinfo * args->maplen) < 0)
+        goto cleanup;
+
+    if ((info_len = virDomainGetVcpus(dom,
+                                      info, args->maxinfo,
+                                      cpumaps, args->maplen)) < 0)
+        goto cleanup;
+
+    /* Allocate the return buffer for info. */
+    ret->info.info_len = info_len;
+    if (VIR_ALLOC_N(ret->info.info_val, info_len) < 0)
+        goto cleanup;
+
+    for (i = 0; i < info_len; ++i) {
+        ret->info.info_val[i].number = info[i].number;
+        ret->info.info_val[i].state = info[i].state;
+        ret->info.info_val[i].cpu_time = info[i].cpuTime;
+        ret->info.info_val[i].cpu = info[i].cpu;
+    }
+
+    /* Don't need to allocate/copy the cpumaps if we make the reasonable
+     * assumption that unsigned char and char are the same size.
+     * Note that remoteDispatchClientRequest will free.
+     */
+    ret->cpumaps.cpumaps_len = args->maxinfo * args->maplen;
+    ret->cpumaps.cpumaps_val = (char *) cpumaps;
+    cpumaps = NULL;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(ret->info.info_val);
+    }
+    VIR_FREE(cpumaps);
+    VIR_FREE(info);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetIOThreadInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                    virNetServerClientPtr client,
+                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                    virNetMessageErrorPtr rerr,
+                                    remote_domain_get_iothread_info_args *args,
+                                    remote_domain_get_iothread_info_ret *ret)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
+    virDomainIOThreadInfoPtr *info = NULL;
+    virDomainPtr dom = NULL;
+    remote_domain_iothread_info *dst;
+    int ninfo = 0;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if ((ninfo = virDomainGetIOThreadInfo(dom, &info, args->flags)) < 0)
+        goto cleanup;
+
+    if (ninfo > REMOTE_IOTHREAD_INFO_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many IOThreads in info: %d for limit %d"),
+                       ninfo, REMOTE_IOTHREAD_INFO_MAX);
+        goto cleanup;
+    }
+
+    if (ninfo) {
+        if (VIR_ALLOC_N(ret->info.info_val, ninfo) < 0)
+            goto cleanup;
+
+        ret->info.info_len = ninfo;
+
+        for (i = 0; i < ninfo; i++) {
+            dst = &ret->info.info_val[i];
+            dst->iothread_id = info[i]->iothread_id;
+
+            /* No need to allocate/copy the cpumap if we make the reasonable
+             * assumption that unsigned char and char are the same size.
+             */
+            dst->cpumap.cpumap_len = info[i]->cpumaplen;
+            dst->cpumap.cpumap_val = (char *)info[i]->cpumap;
+            info[i]->cpumap = NULL;
+        }
+    } else {
+        ret->info.info_len = 0;
+        ret->info.info_val = NULL;
+    }
+
+    ret->ret = ninfo;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    if (ninfo >= 0)
+        for (i = 0; i < ninfo; i++)
+            virDomainIOThreadInfoFree(info[i]);
+    VIR_FREE(info);
+
+    return rv;
+}
+
+static int
+remoteDispatchDomainMigratePrepare(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                   virNetMessageErrorPtr rerr,
+                                   remote_domain_migrate_prepare_args *args,
+                                   remote_domain_migrate_prepare_ret *ret)
+{
+    char *cookie = NULL;
+    int cookielen = 0;
+    char *uri_in;
+    char **uri_out;
+    char *dname;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
+    dname = args->dname == NULL ? NULL : *args->dname;
+
+    /* Wacky world of XDR ... */
+    if (VIR_ALLOC(uri_out) < 0)
+        goto cleanup;
+
+    if (virDomainMigratePrepare(priv->conn, &cookie, &cookielen,
+                                uri_in, uri_out,
+                                args->flags, dname, args->resource) < 0)
+        goto cleanup;
+
+    /* remoteDispatchClientRequest will free cookie, uri_out and
+     * the string if there is one.
+     */
+    ret->cookie.cookie_len = cookielen;
+    ret->cookie.cookie_val = cookie;
+    if (*uri_out == NULL) {
+        ret->uri_out = NULL;
+    } else {
+        ret->uri_out = uri_out;
+        uri_out = NULL;
+    }
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    VIR_FREE(uri_out);
+    return rv;
+}
+
+static int
+remoteDispatchDomainMigratePrepare2(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                    virNetMessageErrorPtr rerr,
+                                    remote_domain_migrate_prepare2_args *args,
+                                    remote_domain_migrate_prepare2_ret *ret)
+{
+    char *cookie = NULL;
+    int cookielen = 0;
+    char *uri_in;
+    char **uri_out;
+    char *dname;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
+    dname = args->dname == NULL ? NULL : *args->dname;
+
+    /* Wacky world of XDR ... */
+    if (VIR_ALLOC(uri_out) < 0)
+        goto cleanup;
+
+    if (virDomainMigratePrepare2(priv->conn, &cookie, &cookielen,
+                                 uri_in, uri_out,
+                                 args->flags, dname, args->resource,
+                                 args->dom_xml) < 0)
+        goto cleanup;
+
+    /* remoteDispatchClientRequest will free cookie, uri_out and
+     * the string if there is one.
+     */
+    ret->cookie.cookie_len = cookielen;
+    ret->cookie.cookie_val = cookie;
+    ret->uri_out = *uri_out == NULL ? NULL : uri_out;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(uri_out);
+    }
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                        virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                        virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                        virNetMessageErrorPtr rerr,
+                                        remote_domain_get_memory_parameters_args *args,
+                                        remote_domain_get_memory_parameters_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetMemoryParameters(dom, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                args->flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetNumaParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                      virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                      virNetMessageErrorPtr rerr,
+                                      remote_domain_get_numa_parameters_args *args,
+                                      remote_domain_get_numa_parameters_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_DOMAIN_NUMA_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetNumaParameters(dom, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetBlkioParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                       virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                       virNetMessageErrorPtr rerr,
+                                       remote_domain_get_blkio_parameters_args *args,
+                                       remote_domain_get_blkio_parameters_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_DOMAIN_BLKIO_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetBlkioParameters(dom, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                args->flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchNodeGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
+                              virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                              virNetMessageErrorPtr rerr,
+                              remote_node_get_cpu_stats_args *args,
+                              remote_node_get_cpu_stats_ret *ret)
+{
+    virNodeCPUStatsPtr params = NULL;
+    size_t i;
+    int cpuNum = args->cpuNum;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_NODE_CPU_STATS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (virNodeGetCPUStats(priv->conn, cpuNum, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of stats
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    /* Serialise the memory parameters. */
+    ret->params.params_len = nparams;
+    if (VIR_ALLOC_N(ret->params.params_val, nparams) < 0)
+        goto cleanup;
+
+    for (i = 0; i < nparams; ++i) {
+        /* remoteDispatchClientRequest will free this: */
+        if (VIR_STRDUP(ret->params.params_val[i].field, params[i].field) < 0)
+            goto cleanup;
+
+        ret->params.params_val[i].value = params[i].value;
+    }
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        if (ret->params.params_val) {
+            for (i = 0; i < nparams; i++)
+                VIR_FREE(ret->params.params_val[i].field);
+            VIR_FREE(ret->params.params_val);
+        }
+    }
+    VIR_FREE(params);
+    return rv;
+}
+
+static int
+remoteDispatchNodeGetMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                 virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                 virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                 virNetMessageErrorPtr rerr,
+                                 remote_node_get_memory_stats_args *args,
+                                 remote_node_get_memory_stats_ret *ret)
+{
+    virNodeMemoryStatsPtr params = NULL;
+    size_t i;
+    int cellNum = args->cellNum;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_NODE_MEMORY_STATS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (virNodeGetMemoryStats(priv->conn, cellNum, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    /* Serialise the memory parameters. */
+    ret->params.params_len = nparams;
+    if (VIR_ALLOC_N(ret->params.params_val, nparams) < 0)
+        goto cleanup;
+
+    for (i = 0; i < nparams; ++i) {
+        /* remoteDispatchClientRequest will free this: */
+        if (VIR_STRDUP(ret->params.params_val[i].field, params[i].field) < 0)
+            goto cleanup;
+
+        ret->params.params_val[i].value = params[i].value;
+    }
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        if (ret->params.params_val) {
+            for (i = 0; i < nparams; i++)
+                VIR_FREE(ret->params.params_val[i].field);
+            VIR_FREE(ret->params.params_val);
+        }
+    }
+    VIR_FREE(params);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetPerfEvents(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                  virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                  virNetMessageErrorPtr rerr,
+                                  remote_domain_get_perf_events_args *args,
+                                  remote_domain_get_perf_events_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetPerfEvents(dom, &params, &nparams, args->flags) < 0)
+        goto cleanup;
+
+    if (nparams > REMOTE_DOMAIN_PERF_EVENTS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                0) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetBlockJobInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                    virNetMessageErrorPtr rerr,
+                                    remote_domain_get_block_job_info_args *args,
+                                    remote_domain_get_block_job_info_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virDomainBlockJobInfo tmp;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    rv = virDomainGetBlockJobInfo(dom, args->path, &tmp, args->flags);
+    if (rv <= 0)
+        goto cleanup;
+
+    ret->type = tmp.type;
+    ret->bandwidth = tmp.bandwidth;
+    ret->cur = tmp.cur;
+    ret->end = tmp.end;
+    ret->found = 1;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetBlockIoTune(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                   virNetMessagePtr hdr ATTRIBUTE_UNUSED,
+                                   virNetMessageErrorPtr rerr,
+                                   remote_domain_get_block_io_tune_args *args,
+                                   remote_domain_get_block_io_tune_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    int rv = -1;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->nparams > REMOTE_DOMAIN_BLOCK_IO_TUNE_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetBlockIoTune(dom, args->disk ? *args->disk : NULL,
+                                params, &nparams, args->flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    /* Serialize the block I/O tuning parameters. */
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                args->flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+/*-------------------------------------------------------------*/
+
+static int
+remoteDispatchAuthList(virNetServerPtr server,
+                       virNetServerClientPtr client,
+                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                       virNetMessageErrorPtr rerr,
+                       remote_auth_list_ret *ret)
+{
+    int rv = -1;
+    int auth = virNetServerClientGetAuth(client);
+    uid_t callerUid;
+    gid_t callerGid;
+    pid_t callerPid;
+    unsigned long long timestamp;
+
+    /* If the client is root then we want to bypass the
+     * policykit auth to avoid root being denied if
+     * some piece of polkit isn't present/running
+     */
+    if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
+        if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
+                                              &callerPid, &timestamp) < 0) {
+            /* Don't do anything on error - it'll be validated at next
+             * phase of auth anyway */
+            virResetLastError();
+        } else if (callerUid == 0) {
+            char *ident;
+            if (virAsprintf(&ident, "pid:%lld,uid:%d",
+                            (long long) callerPid, (int) callerUid) < 0)
+                goto cleanup;
+            VIR_INFO("Bypass polkit auth for privileged client %s", ident);
+            virNetServerSetClientAuthenticated(server, client);
+            auth = VIR_NET_SERVER_SERVICE_AUTH_NONE;
+            VIR_FREE(ident);
+        }
+    }
+
+    ret->types.types_len = 1;
+    if (VIR_ALLOC_N(ret->types.types_val, ret->types.types_len) < 0)
+        goto cleanup;
+
+    switch ((virNetServerServiceAuthMethods) auth) {
+    case VIR_NET_SERVER_SERVICE_AUTH_NONE:
+        ret->types.types_val[0] = REMOTE_AUTH_NONE;
+        break;
+    case VIR_NET_SERVER_SERVICE_AUTH_POLKIT:
+        ret->types.types_val[0] = REMOTE_AUTH_POLKIT;
+        break;
+    case VIR_NET_SERVER_SERVICE_AUTH_SASL:
+        ret->types.types_val[0] = REMOTE_AUTH_SASL;
+        break;
+    }
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+
+#ifdef WITH_SASL
+/*
+ * Initializes the SASL session in prepare for authentication
+ * and gives the client a list of allowed mechanisms to choose
+ */
+static int
+remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED,
+                           virNetServerClientPtr client,
+                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                           virNetMessageErrorPtr rerr,
+                           remote_auth_sasl_init_ret *ret)
+{
+    virNetSASLSessionPtr sasl = NULL;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    virMutexLock(&priv->lock);
+
+    VIR_DEBUG("Initialize SASL auth %d", virNetServerClientGetFD(client));
+    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
+        priv->sasl != NULL) {
+        VIR_ERROR(_("client tried invalid SASL init request"));
+        goto authfail;
+    }
+
+    sasl = virNetSASLSessionNewServer(saslCtxt,
+                                      "libvirt",
+                                      virNetServerClientLocalAddrStringSASL(client),
+                                      virNetServerClientRemoteAddrStringSASL(client));
+    if (!sasl)
+        goto authfail;
+
+# if WITH_GNUTLS
+    /* Inform SASL that we've got an external SSF layer from TLS */
+    if (virNetServerClientHasTLSSession(client)) {
+        int ssf;
+
+        if ((ssf = virNetServerClientGetTLSKeySize(client)) < 0)
+            goto authfail;
+
+        ssf *= 8; /* key size is bytes, sasl wants bits */
+
+        VIR_DEBUG("Setting external SSF %d", ssf);
+        if (virNetSASLSessionExtKeySize(sasl, ssf) < 0)
+            goto authfail;
+    }
+# endif
+
+    if (virNetServerClientIsSecure(client))
+        /* If we've got TLS or UNIX domain sock, we don't care about SSF */
+        virNetSASLSessionSecProps(sasl, 0, 0, true);
+    else
+        /* Plain TCP, better get an SSF layer */
+        virNetSASLSessionSecProps(sasl,
+                                  56,  /* Good enough to require kerberos */
+                                  100000,  /* Arbitrary big number */
+                                  false); /* No anonymous */
+
+    if (!(ret->mechlist = virNetSASLSessionListMechanisms(sasl)))
+        goto authfail;
+    VIR_DEBUG("Available mechanisms for client: '%s'", ret->mechlist);
+
+    priv->sasl = sasl;
+    virMutexUnlock(&priv->lock);
+    return 0;
+
+ authfail:
+    virResetLastError();
+    virReportError(VIR_ERR_AUTH_FAILED, "%s",
+                   _("authentication failed"));
+    virNetMessageSaveError(rerr);
+    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
+          "client=%p auth=%d",
+          client, REMOTE_AUTH_SASL);
+    virObjectUnref(sasl);
+    virMutexUnlock(&priv->lock);
+    return -1;
+}
+
+/*
+ * Returns 0 if ok, -1 on error, -2 if rejected
+ */
+static int
+remoteSASLFinish(virNetServerPtr server,
+                 virNetServerClientPtr client)
+{
+    virIdentityPtr clnt_identity = NULL;
+    const char *identity;
+    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
+    int ssf;
+
+    /* TLS or UNIX domain sockets trivially OK */
+    if (!virNetServerClientIsSecure(client)) {
+        if ((ssf = virNetSASLSessionGetKeySize(priv->sasl)) < 0)
+            goto error;
+
+        VIR_DEBUG("negotiated an SSF of %d", ssf);
+        if (ssf < 56) { /* 56 is good for Kerberos */
+            VIR_ERROR(_("negotiated SSF %d was not strong enough"), ssf);
+            return -2;
+        }
+    }
+
+    if (!(identity = virNetSASLSessionGetIdentity(priv->sasl)))
+        return -2;
+
+    if (!virNetSASLContextCheckIdentity(saslCtxt, identity))
+        return -2;
+
+    if (!(clnt_identity = virNetServerClientGetIdentity(client)))
+        goto error;
+
+    virNetServerSetClientAuthenticated(server, client);
+    virNetServerClientSetSASLSession(client, priv->sasl);
+    virIdentitySetSASLUserName(clnt_identity, identity);
+
+    VIR_DEBUG("Authentication successful %d", virNetServerClientGetFD(client));
+
+    PROBE(RPC_SERVER_CLIENT_AUTH_ALLOW,
+          "client=%p auth=%d identity=%s",
+          client, REMOTE_AUTH_SASL, identity);
+
+    virObjectUnref(clnt_identity);
+    virObjectUnref(priv->sasl);
+    priv->sasl = NULL;
+
+    return 0;
+
+ error:
+    return -1;
+}
+
+/*
+ * This starts the SASL authentication negotiation.
+ */
+static int
+remoteDispatchAuthSaslStart(virNetServerPtr server,
+                            virNetServerClientPtr client,
+                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                            virNetMessageErrorPtr rerr,
+                            remote_auth_sasl_start_args *args,
+                            remote_auth_sasl_start_ret *ret)
+{
+    const char *serverout;
+    size_t serveroutlen;
+    int err;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    const char *identity;
+
+    virMutexLock(&priv->lock);
+
+    VIR_DEBUG("Start SASL auth %d", virNetServerClientGetFD(client));
+    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
+        priv->sasl == NULL) {
+        VIR_ERROR(_("client tried invalid SASL start request"));
+        goto authfail;
+    }
+
+    VIR_DEBUG("Using SASL mechanism %s. Data %d bytes, nil: %d",
+              args->mech, args->data.data_len, args->nil);
+    err = virNetSASLSessionServerStart(priv->sasl,
+                                       args->mech,
+                                       /* NB, distinction of NULL vs "" is *critical* in SASL */
+                                       args->nil ? NULL : args->data.data_val,
+                                       args->data.data_len,
+                                       &serverout,
+                                       &serveroutlen);
+    if (err != VIR_NET_SASL_COMPLETE &&
+        err != VIR_NET_SASL_CONTINUE)
+        goto authfail;
+
+    if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
+        VIR_ERROR(_("sasl start reply data too long %d"), (int)serveroutlen);
+        goto authfail;
+    }
+
+    /* NB, distinction of NULL vs "" is *critical* in SASL */
+    if (serverout) {
+        if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0)
+            goto authfail;
+        memcpy(ret->data.data_val, serverout, serveroutlen);
+    } else {
+        ret->data.data_val = NULL;
+    }
+    ret->nil = serverout ? 0 : 1;
+    ret->data.data_len = serveroutlen;
+
+    VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil);
+    if (err == VIR_NET_SASL_CONTINUE) {
+        ret->complete = 0;
+    } else {
+        /* Check username whitelist ACL */
+        if ((err = remoteSASLFinish(server, client)) < 0) {
+            if (err == -2)
+                goto authdeny;
+            else
+                goto authfail;
+        }
+
+        ret->complete = 1;
+    }
+
+    virMutexUnlock(&priv->lock);
+    return 0;
+
+ authfail:
+    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
+          "client=%p auth=%d",
+          client, REMOTE_AUTH_SASL);
+    goto error;
+
+ authdeny:
+    identity = virNetSASLSessionGetIdentity(priv->sasl);
+    PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
+          "client=%p auth=%d identity=%s",
+          client, REMOTE_AUTH_SASL, identity);
+    goto error;
+
+ error:
+    virObjectUnref(priv->sasl);
+    priv->sasl = NULL;
+    virResetLastError();
+    virReportError(VIR_ERR_AUTH_FAILED, "%s",
+                   _("authentication failed"));
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return -1;
+}
+
+
+static int
+remoteDispatchAuthSaslStep(virNetServerPtr server,
+                           virNetServerClientPtr client,
+                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                           virNetMessageErrorPtr rerr,
+                           remote_auth_sasl_step_args *args,
+                           remote_auth_sasl_step_ret *ret)
+{
+    const char *serverout;
+    size_t serveroutlen;
+    int err;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    const char *identity;
+
+    virMutexLock(&priv->lock);
+
+    VIR_DEBUG("Step SASL auth %d", virNetServerClientGetFD(client));
+    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
+        priv->sasl == NULL) {
+        VIR_ERROR(_("client tried invalid SASL start request"));
+        goto authfail;
+    }
+
+    VIR_DEBUG("Step using SASL Data %d bytes, nil: %d",
+              args->data.data_len, args->nil);
+    err = virNetSASLSessionServerStep(priv->sasl,
+                                      /* NB, distinction of NULL vs "" is *critical* in SASL */
+                                      args->nil ? NULL : args->data.data_val,
+                                      args->data.data_len,
+                                      &serverout,
+                                      &serveroutlen);
+    if (err != VIR_NET_SASL_COMPLETE &&
+        err != VIR_NET_SASL_CONTINUE)
+        goto authfail;
+
+    if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
+        VIR_ERROR(_("sasl step reply data too long %d"),
+                  (int)serveroutlen);
+        goto authfail;
+    }
+
+    /* NB, distinction of NULL vs "" is *critical* in SASL */
+    if (serverout) {
+        if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0)
+            goto authfail;
+        memcpy(ret->data.data_val, serverout, serveroutlen);
+    } else {
+        ret->data.data_val = NULL;
+    }
+    ret->nil = serverout ? 0 : 1;
+    ret->data.data_len = serveroutlen;
+
+    VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil);
+    if (err == VIR_NET_SASL_CONTINUE) {
+        ret->complete = 0;
+    } else {
+        /* Check username whitelist ACL */
+        if ((err = remoteSASLFinish(server, client)) < 0) {
+            if (err == -2)
+                goto authdeny;
+            else
+                goto authfail;
+        }
+
+        ret->complete = 1;
+    }
+
+    virMutexUnlock(&priv->lock);
+    return 0;
+
+ authfail:
+    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
+          "client=%p auth=%d",
+          client, REMOTE_AUTH_SASL);
+    goto error;
+
+ authdeny:
+    identity = virNetSASLSessionGetIdentity(priv->sasl);
+    PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
+          "client=%p auth=%d identity=%s",
+          client, REMOTE_AUTH_SASL, identity);
+    goto error;
+
+ error:
+    virObjectUnref(priv->sasl);
+    priv->sasl = NULL;
+    virResetLastError();
+    virReportError(VIR_ERR_AUTH_FAILED, "%s",
+                   _("authentication failed"));
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return -1;
+}
+#else
+static int
+remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED,
+                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                           virNetMessageErrorPtr rerr,
+                           remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED)
+{
+    VIR_WARN("Client tried unsupported SASL auth");
+    virReportError(VIR_ERR_AUTH_FAILED, "%s",
+                   _("authentication failed"));
+    virNetMessageSaveError(rerr);
+    return -1;
+}
+static int
+remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED,
+                            virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                            virNetMessageErrorPtr rerr,
+                            remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED,
+                            remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED)
+{
+    VIR_WARN("Client tried unsupported SASL auth");
+    virReportError(VIR_ERR_AUTH_FAILED, "%s",
+                   _("authentication failed"));
+    virNetMessageSaveError(rerr);
+    return -1;
+}
+static int
+remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED,
+                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                           virNetMessageErrorPtr rerr,
+                           remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED,
+                           remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED)
+{
+    VIR_WARN("Client tried unsupported SASL auth");
+    virReportError(VIR_ERR_AUTH_FAILED, "%s",
+                   _("authentication failed"));
+    virNetMessageSaveError(rerr);
+    return -1;
+}
+#endif
+
+
+
+static int
+remoteDispatchAuthPolkit(virNetServerPtr server,
+                         virNetServerClientPtr client,
+                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                         virNetMessageErrorPtr rerr,
+                         remote_auth_polkit_ret *ret)
+{
+    pid_t callerPid = -1;
+    gid_t callerGid = -1;
+    uid_t callerUid = -1;
+    unsigned long long timestamp;
+    const char *action;
+    char *ident = NULL;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    int rv;
+
+    virMutexLock(&priv->lock);
+    action = virNetServerClientGetReadonly(client) ?
+        "org.libvirt.unix.monitor" :
+        "org.libvirt.unix.manage";
+
+    VIR_DEBUG("Start PolicyKit auth %d", virNetServerClientGetFD(client));
+    if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
+        VIR_ERROR(_("client tried invalid PolicyKit init request"));
+        goto authfail;
+    }
+
+    if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
+                                          &callerPid, &timestamp) < 0) {
+        goto authfail;
+    }
+
+    if (timestamp == 0) {
+        VIR_WARN("Failing polkit auth due to missing client (pid=%lld) start time",
+                 (long long)callerPid);
+        goto authfail;
+    }
+
+    VIR_INFO("Checking PID %lld running as %d",
+             (long long) callerPid, callerUid);
+
+    rv = virPolkitCheckAuth(action,
+                            callerPid,
+                            timestamp,
+                            callerUid,
+                            NULL,
+                            true);
+    if (rv == -1)
+        goto authfail;
+    else if (rv == -2)
+        goto authdeny;
+
+    PROBE(RPC_SERVER_CLIENT_AUTH_ALLOW,
+          "client=%p auth=%d identity=%s",
+          client, REMOTE_AUTH_POLKIT, ident);
+    VIR_INFO("Policy allowed action %s from pid %lld, uid %d",
+             action, (long long) callerPid, callerUid);
+    ret->complete = 1;
+
+    virNetServerSetClientAuthenticated(server, client);
+    virMutexUnlock(&priv->lock);
+
+    return 0;
+
+ error:
+    virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return -1;
+
+ authfail:
+    PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
+          "client=%p auth=%d",
+          client, REMOTE_AUTH_POLKIT);
+    goto error;
+
+ authdeny:
+    PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
+          "client=%p auth=%d identity=%s",
+          client, REMOTE_AUTH_POLKIT, ident);
+    goto error;
+}
+
+
+static int
+remoteDispatchNodeDeviceGetParent(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                  virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                  virNetMessageErrorPtr rerr,
+                                  remote_node_device_get_parent_args *args,
+                                  remote_node_device_get_parent_ret *ret)
+{
+    virNodeDevicePtr dev = NULL;
+    const char *parent = NULL;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dev = virNodeDeviceLookupByName(priv->conn, args->name)))
+        goto cleanup;
+
+    parent = virNodeDeviceGetParent(dev);
+
+    if (parent == NULL) {
+        ret->parent = NULL;
+    } else {
+        /* remoteDispatchClientRequest will free this. */
+        char **parent_p;
+        if (VIR_ALLOC(parent_p) < 0)
+            goto cleanup;
+        if (VIR_STRDUP(*parent_p, parent) < 0) {
+            VIR_FREE(parent_p);
+            goto cleanup;
+        }
+        ret->parent = parent_p;
+    }
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dev);
+    return rv;
+}
+
+static int
+remoteDispatchConnectRegisterCloseCallback(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                           virNetServerClientPtr client,
+                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                           virNetMessageErrorPtr rerr)
+{
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    virMutexLock(&priv->lock);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (virConnectRegisterCloseCallback(priv->conn,
+                                        remoteRelayConnectionClosedEvent,
+                                        client, NULL) < 0)
+        goto cleanup;
+
+    priv->closeRegistered = true;
+    rv = 0;
+
+ cleanup:
+    virMutexUnlock(&priv->lock);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+static int
+remoteDispatchConnectUnregisterCloseCallback(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                             virNetServerClientPtr client,
+                                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                             virNetMessageErrorPtr rerr)
+{
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    virMutexLock(&priv->lock);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (virConnectUnregisterCloseCallback(priv->conn,
+                                          remoteRelayConnectionClosedEvent) < 0)
+        goto cleanup;
+
+    priv->closeRegistered = false;
+    rv = 0;
+
+ cleanup:
+    virMutexUnlock(&priv->lock);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+static int
+remoteDispatchConnectDomainEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                         virNetServerClientPtr client,
+                                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                         virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                         remote_connect_domain_event_register_ret *ret ATTRIBUTE_UNUSED)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; or you can use VIR_APPEND_ELEMENT_COPY to avoid
+     * clearing 'callback' and having to juggle the pointer
+     * between 'ref' and 'callback'.
+     */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->eventID = VIR_DOMAIN_EVENT_ID_LIFECYCLE;
+    callback->callbackID = -1;
+    callback->legacy = true;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->domainEventCallbacks,
+                           priv->ndomainEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
+                                                       NULL,
+                                                       VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+                                                       VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
+                                                       ref,
+                                                       remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->domainEventCallbacks,
+                     priv->ndomainEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectDomainEventDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                           virNetServerClientPtr client,
+                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                           virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                           remote_connect_domain_event_deregister_ret *ret ATTRIBUTE_UNUSED)
+{
+    int callbackID = -1;
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    for (i = 0; i < priv->ndomainEventCallbacks; i++) {
+        if (priv->domainEventCallbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE) {
+            callbackID = priv->domainEventCallbacks[i]->callbackID;
+            break;
+        }
+    }
+
+    if (callbackID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("domain event %d not registered"),
+                       VIR_DOMAIN_EVENT_ID_LIFECYCLE);
+        goto cleanup;
+    }
+
+    if (virConnectDomainEventDeregisterAny(priv->conn, callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->domainEventCallbacks, i,
+                       priv->ndomainEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static void
+remoteDispatchObjectEventSend(virNetServerClientPtr client,
+                              virNetServerProgramPtr program,
+                              int procnr,
+                              xdrproc_t proc,
+                              void *data)
+{
+    virNetMessagePtr msg;
+
+    if (!(msg = virNetMessageNew(false)))
+        goto cleanup;
+
+    msg->header.prog = virNetServerProgramGetID(program);
+    msg->header.vers = virNetServerProgramGetVersion(program);
+    msg->header.proc = procnr;
+    msg->header.type = VIR_NET_MESSAGE;
+    msg->header.serial = 1;
+    msg->header.status = VIR_NET_OK;
+
+    if (virNetMessageEncodeHeader(msg) < 0)
+        goto cleanup;
+
+    if (virNetMessageEncodePayload(msg, proc, data) < 0)
+        goto cleanup;
+
+    VIR_DEBUG("Queue event %d %zu", procnr, msg->bufferLength);
+    virNetServerClientSendMessage(client, msg);
+
+    xdr_free(proc, data);
+    return;
+
+ cleanup:
+    virNetMessageFree(msg);
+    xdr_free(proc, data);
+}
+
+static int
+remoteDispatchSecretGetValue(virNetServerPtr server ATTRIBUTE_UNUSED,
+                             virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                             virNetMessageErrorPtr rerr,
+                             remote_secret_get_value_args *args,
+                             remote_secret_get_value_ret *ret)
+{
+    virSecretPtr secret = NULL;
+    size_t value_size;
+    unsigned char *value;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(secret = get_nonnull_secret(priv->conn, args->secret)))
+        goto cleanup;
+
+    if (!(value = virSecretGetValue(secret, &value_size, args->flags)))
+        goto cleanup;
+
+    ret->value.value_len = value_size;
+    ret->value.value_val = (char *)value;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(secret);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetState(virNetServerPtr server ATTRIBUTE_UNUSED,
+                             virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                             virNetMessageErrorPtr rerr,
+                             remote_domain_get_state_args *args,
+                             remote_domain_get_state_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetState(dom, &ret->state, &ret->reason, args->flags) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+/* Due to back-compat reasons, two RPC calls map to the same libvirt
+ * API of virConnectDomainEventRegisterAny.  A client should only use
+ * the new call if they have probed
+ * VIR_DRV_SUPPORTS_FEATURE(VIR_DRV_FEATURE_REMOTE_EVENT_CALLBACK),
+ * and must not mix the two styles.  */
+static int
+remoteDispatchConnectDomainEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                            virNetServerClientPtr client,
+                                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                            virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                            remote_connect_domain_event_register_any_args *args)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    /* We intentionally do not use VIR_DOMAIN_EVENT_ID_LAST here; any
+     * new domain events added after this point should only use the
+     * modern callback style of RPC.  */
+    if (args->eventID > VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED ||
+        args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"),
+                       args->eventID);
+        goto cleanup;
+    }
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+     * success, we use 'ref' to save a copy of the pointer.  */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->eventID = args->eventID;
+    callback->callbackID = -1;
+    callback->legacy = true;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->domainEventCallbacks,
+                           priv->ndomainEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
+                                                       NULL,
+                                                       args->eventID,
+                                                       domainEventCallbacks[args->eventID],
+                                                       ref,
+                                                       remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->domainEventCallbacks,
+                     priv->ndomainEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectDomainEventCallbackRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                    virNetServerClientPtr client,
+                                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                    virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                    remote_connect_domain_event_callback_register_any_args *args,
+                                                    remote_connect_domain_event_callback_register_any_ret *ret)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virDomainPtr dom = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->dom &&
+        !(dom = get_nonnull_domain(priv->conn, *args->dom)))
+        goto cleanup;
+
+    if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST || args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"),
+                       args->eventID);
+        goto cleanup;
+    }
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+     * success, we use 'ref' to save a copy of the pointer.  */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->eventID = args->eventID;
+    callback->callbackID = -1;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->domainEventCallbacks,
+                           priv->ndomainEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
+                                                       dom,
+                                                       args->eventID,
+                                                       domainEventCallbacks[args->eventID],
+                                                       ref,
+                                                       remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->domainEventCallbacks,
+                     priv->ndomainEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+    ret->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectDomainEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                              virNetServerClientPtr client,
+                                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                              virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                              remote_connect_domain_event_deregister_any_args *args)
+{
+    int callbackID = -1;
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    /* We intentionally do not use VIR_DOMAIN_EVENT_ID_LAST here; any
+     * new domain events added after this point should only use the
+     * modern callback style of RPC.  */
+    if (args->eventID > VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED ||
+        args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"),
+                       args->eventID);
+        goto cleanup;
+    }
+
+    for (i = 0; i < priv->ndomainEventCallbacks; i++) {
+        if (priv->domainEventCallbacks[i]->eventID == args->eventID) {
+            callbackID = priv->domainEventCallbacks[i]->callbackID;
+            break;
+        }
+    }
+    if (callbackID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("domain event %d not registered"), args->eventID);
+        goto cleanup;
+    }
+
+    if (virConnectDomainEventDeregisterAny(priv->conn, callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->domainEventCallbacks, i,
+                       priv->ndomainEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectDomainEventCallbackDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                      virNetServerClientPtr client,
+                                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                      virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                      remote_connect_domain_event_callback_deregister_any_args *args)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    for (i = 0; i < priv->ndomainEventCallbacks; i++) {
+        if (priv->domainEventCallbacks[i]->callbackID == args->callbackID)
+            break;
+    }
+    if (i == priv->ndomainEventCallbacks) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("domain event callback %d not registered"),
+                       args->callbackID);
+        goto cleanup;
+    }
+
+    if (virConnectDomainEventDeregisterAny(priv->conn, args->callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->domainEventCallbacks, i,
+                       priv->ndomainEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+
+static int
+qemuDispatchDomainMonitorCommand(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                 virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                 virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                 virNetMessageErrorPtr rerr,
+                                 qemu_domain_monitor_command_args *args,
+                                 qemu_domain_monitor_command_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainQemuMonitorCommand(dom, args->cmd, &ret->result,
+                                    args->flags) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigrateBegin3(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                  virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                  virNetMessageErrorPtr rerr,
+                                  remote_domain_migrate_begin3_args *args,
+                                  remote_domain_migrate_begin3_ret *ret)
+{
+    char *xml = NULL;
+    virDomainPtr dom = NULL;
+    char *dname;
+    char *xmlin;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    xmlin = args->xmlin == NULL ? NULL : *args->xmlin;
+    dname = args->dname == NULL ? NULL : *args->dname;
+
+    if (!(xml = virDomainMigrateBegin3(dom, xmlin,
+                                       &cookieout, &cookieoutlen,
+                                       args->flags, dname, args->resource)))
+        goto cleanup;
+
+    /* remoteDispatchClientRequest will free cookie and
+     * the xml string if there is one.
+     */
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+    ret->xml = xml;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigratePrepare3(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                    virNetMessageErrorPtr rerr,
+                                    remote_domain_migrate_prepare3_args *args,
+                                    remote_domain_migrate_prepare3_ret *ret)
+{
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    char *uri_in;
+    char **uri_out;
+    char *dname;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
+    dname = args->dname == NULL ? NULL : *args->dname;
+
+    /* Wacky world of XDR ... */
+    if (VIR_ALLOC(uri_out) < 0)
+        goto cleanup;
+
+    if (virDomainMigratePrepare3(priv->conn,
+                                 args->cookie_in.cookie_in_val,
+                                 args->cookie_in.cookie_in_len,
+                                 &cookieout, &cookieoutlen,
+                                 uri_in, uri_out,
+                                 args->flags, dname, args->resource,
+                                 args->dom_xml) < 0)
+        goto cleanup;
+
+    /* remoteDispatchClientRequest will free cookie, uri_out and
+     * the string if there is one.
+     */
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+    ret->uri_out = *uri_out == NULL ? NULL : uri_out;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(uri_out);
+    }
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigratePerform3(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                    virNetMessageErrorPtr rerr,
+                                    remote_domain_migrate_perform3_args *args,
+                                    remote_domain_migrate_perform3_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    char *xmlin;
+    char *dname;
+    char *uri;
+    char *dconnuri;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    xmlin = args->xmlin == NULL ? NULL : *args->xmlin;
+    dname = args->dname == NULL ? NULL : *args->dname;
+    uri = args->uri == NULL ? NULL : *args->uri;
+    dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
+
+    if (virDomainMigratePerform3(dom, xmlin,
+                                 args->cookie_in.cookie_in_val,
+                                 args->cookie_in.cookie_in_len,
+                                 &cookieout, &cookieoutlen,
+                                 dconnuri, uri,
+                                 args->flags, dname, args->resource) < 0)
+        goto cleanup;
+
+    /* remoteDispatchClientRequest will free cookie
+     */
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigrateFinish3(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                   virNetMessageErrorPtr rerr,
+                                   remote_domain_migrate_finish3_args *args,
+                                   remote_domain_migrate_finish3_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    char *uri;
+    char *dconnuri;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    uri = args->uri == NULL ? NULL : *args->uri;
+    dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
+
+    if (!(dom = virDomainMigrateFinish3(priv->conn, args->dname,
+                                        args->cookie_in.cookie_in_val,
+                                        args->cookie_in.cookie_in_len,
+                                        &cookieout, &cookieoutlen,
+                                        dconnuri, uri,
+                                        args->flags,
+                                        args->cancelled)))
+        goto cleanup;
+
+    make_nonnull_domain(&ret->dom, dom);
+
+    /* remoteDispatchClientRequest will free cookie
+     */
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(cookieout);
+    }
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigrateConfirm3(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                    virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                    virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                    virNetMessageErrorPtr rerr,
+                                    remote_domain_migrate_confirm3_args *args)
+{
+    virDomainPtr dom = NULL;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainMigrateConfirm3(dom,
+                                 args->cookie_in.cookie_in_val,
+                                 args->cookie_in.cookie_in_len,
+                                 args->flags, args->cancelled) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int remoteDispatchConnectSupportsFeature(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                virNetServerClientPtr client,
+                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                virNetMessageErrorPtr rerr,
+                                                remote_connect_supports_feature_args *args,
+                                                remote_connect_supports_feature_ret *ret)
+{
+    int rv = -1;
+    int supported;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    /* This feature is checked before opening the connection, thus we must
+     * check it first.
+     */
+    if (args->feature == VIR_DRV_FEATURE_PROGRAM_KEEPALIVE) {
+        if (virNetServerClientStartKeepAlive(client) < 0)
+            goto cleanup;
+        supported = 1;
+        goto done;
+    }
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    switch (args->feature) {
+    case VIR_DRV_FEATURE_FD_PASSING:
+    case VIR_DRV_FEATURE_REMOTE_EVENT_CALLBACK:
+    case VIR_DRV_FEATURE_REMOTE_CLOSE_CALLBACK:
+        supported = 1;
+        break;
+
+    default:
+        if ((supported = virConnectSupportsFeature(priv->conn, args->feature)) < 0)
+            goto cleanup;
+        break;
+    }
+
+ done:
+    ret->supported = supported;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainOpenGraphics(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                 virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                 virNetMessagePtr msg,
+                                 virNetMessageErrorPtr rerr,
+                                 remote_domain_open_graphics_args *args)
+{
+    virDomainPtr dom = NULL;
+    int rv = -1;
+    int fd = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if ((fd = virNetMessageDupFD(msg, 0)) < 0)
+        goto cleanup;
+
+    if (virDomainOpenGraphics(dom,
+                              args->idx,
+                              fd,
+                              args->flags) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    VIR_FORCE_CLOSE(fd);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainOpenGraphicsFd(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                   virNetMessagePtr msg,
+                                   virNetMessageErrorPtr rerr,
+                                   remote_domain_open_graphics_fd_args *args)
+{
+    virDomainPtr dom = NULL;
+    int rv = -1;
+    int fd = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if ((fd = virDomainOpenGraphicsFD(dom,
+                                      args->idx,
+                                      args->flags)) < 0)
+        goto cleanup;
+
+    if (virNetMessageAddFD(msg, fd) < 0)
+        goto cleanup;
+
+    /* return 1 here to let virNetServerProgramDispatchCall know
+     * we are passing a FD */
+    rv = 1;
+
+ cleanup:
+    VIR_FORCE_CLOSE(fd);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainGetInterfaceParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                           virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                           virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                           virNetMessageErrorPtr rerr,
+                                           remote_domain_get_interface_parameters_args *args,
+                                           remote_domain_get_interface_parameters_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    const char *device = args->device;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                virNetMessagePtr hdr ATTRIBUTE_UNUSED,
+                                virNetMessageErrorPtr rerr,
+                                remote_domain_get_cpu_stats_args *args,
+                                remote_domain_get_cpu_stats_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    struct daemonClientPrivate *priv;
+    virTypedParameterPtr params = NULL;
+    int rv = -1;
+    int percpu_len = 0;
+
+    priv = virNetServerClientGetPrivateData(client);
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->nparams > REMOTE_NODE_CPU_STATS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->ncpus > REMOTE_DOMAIN_GET_CPU_STATS_NCPUS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("ncpus too large"));
+        goto cleanup;
+    }
+
+    if (args->nparams > 0 &&
+        VIR_ALLOC_N(params, args->ncpus * args->nparams) < 0)
+        goto cleanup;
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    percpu_len = virDomainGetCPUStats(dom, params, args->nparams,
+                                      args->start_cpu, args->ncpus,
+                                      args->flags);
+    if (percpu_len < 0)
+        goto cleanup;
+    /* If nparams == 0, the function returns a single value */
+    if (args->nparams == 0)
+        goto success;
+
+    if (virTypedParamsSerialize(params, args->nparams * args->ncpus,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                args->flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+    ret->nparams = percpu_len;
+    if (args->nparams && !(args->flags & VIR_TYPED_PARAM_STRING_OKAY)) {
+        size_t i;
+
+        for (i = 0; i < percpu_len; i++) {
+            if (params[i].type == VIR_TYPED_PARAM_STRING)
+                ret->nparams--;
+        }
+    }
+
+ cleanup:
+    if (rv < 0)
+         virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, args->ncpus * args->nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetDiskErrors(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                  virNetServerClientPtr client,
+                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                  virNetMessageErrorPtr rerr,
+                                  remote_domain_get_disk_errors_args *args,
+                                  remote_domain_get_disk_errors_ret *ret)
+{
+    int rv = -1;
+    virDomainPtr dom = NULL;
+    virDomainDiskErrorPtr errors = NULL;
+    int len = 0;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (args->maxerrors > REMOTE_DOMAIN_DISK_ERRORS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("maxerrors too large"));
+        goto cleanup;
+    }
+
+    if (args->maxerrors &&
+        VIR_ALLOC_N(errors, args->maxerrors) < 0)
+        goto cleanup;
+
+    if ((len = virDomainGetDiskErrors(dom, errors,
+                                      args->maxerrors,
+                                      args->flags)) < 0)
+        goto cleanup;
+
+    ret->nerrors = len;
+    if (errors &&
+        remoteSerializeDomainDiskErrors(errors, len,
+                                        &ret->errors.errors_val,
+                                        &ret->errors.errors_len) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    if (errors && len > 0) {
+        size_t i;
+        for (i = 0; i < len; i++)
+            VIR_FREE(errors[i].disk);
+    }
+    VIR_FREE(errors);
+    return rv;
+}
+
+
+static int
+remoteDispatchNodeGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                      virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                      virNetMessageErrorPtr rerr,
+                                      remote_node_get_memory_parameters_args *args,
+                                      remote_node_get_memory_parameters_ret *ret)
+{
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    unsigned int flags;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    if (args->nparams > REMOTE_NODE_MEMORY_PARAMETERS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
+        goto cleanup;
+    }
+    if (args->nparams && VIR_ALLOC_N(params, args->nparams) < 0)
+        goto cleanup;
+    nparams = args->nparams;
+
+    if (virNodeGetMemoryParameters(priv->conn, params, &nparams, flags) < 0)
+        goto cleanup;
+
+    /* In this case, we need to send back the number of parameters
+     * supported
+     */
+    if (args->nparams == 0) {
+        ret->nparams = nparams;
+        goto success;
+    }
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                args->flags) < 0)
+        goto cleanup;
+
+ success:
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    return rv;
+}
+
+static int
+remoteDispatchNodeGetCPUMap(virNetServerPtr server ATTRIBUTE_UNUSED,
+                            virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                            virNetMessageErrorPtr rerr,
+                            remote_node_get_cpu_map_args *args,
+                            remote_node_get_cpu_map_ret *ret)
+{
+    unsigned char *cpumap = NULL;
+    unsigned int online = 0;
+    unsigned int flags;
+    int cpunum;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    flags = args->flags;
+
+    cpunum = virNodeGetCPUMap(priv->conn, args->need_map ? &cpumap : NULL,
+                              args->need_online ? &online : NULL, flags);
+    if (cpunum < 0)
+        goto cleanup;
+
+    /* 'serialize' return cpumap */
+    if (args->need_map) {
+        ret->cpumap.cpumap_len = VIR_CPU_MAPLEN(cpunum);
+        ret->cpumap.cpumap_val = (char *) cpumap;
+        cpumap = NULL;
+    }
+
+    ret->online = online;
+    ret->ret = cpunum;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    VIR_FREE(cpumap);
+    return rv;
+}
+
+static int
+lxcDispatchDomainOpenNamespace(virNetServerPtr server ATTRIBUTE_UNUSED,
+                               virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                               virNetMessageErrorPtr rerr,
+                               lxc_domain_open_namespace_args *args)
+{
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    int *fdlist = NULL;
+    int ret;
+    virDomainPtr dom = NULL;
+    size_t i;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    ret = virDomainLxcOpenNamespace(dom,
+                                    &fdlist,
+                                    args->flags);
+    if (ret < 0)
+        goto cleanup;
+
+    /* We shouldn't have received any from the client,
+     * but in case they're playing games with us, prevent
+     * a resource leak
+     */
+    for (i = 0; i < msg->nfds; i++)
+        VIR_FORCE_CLOSE(msg->fds[i]);
+    VIR_FREE(msg->fds);
+    msg->nfds = 0;
+
+    msg->fds = fdlist;
+    msg->nfds = ret;
+
+    rv = 1;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetJobStats(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                virNetServerClientPtr client,
+                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                virNetMessageErrorPtr rerr,
+                                remote_domain_get_job_stats_args *args,
+                                remote_domain_get_job_stats_ret *ret)
+{
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetJobStats(dom, &ret->type, &params,
+                             &nparams, args->flags) < 0)
+        goto cleanup;
+
+    if (nparams > REMOTE_DOMAIN_JOB_STATS_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many job stats '%d' for limit '%d'"),
+                       nparams, REMOTE_DOMAIN_JOB_STATS_MAX);
+        goto cleanup;
+    }
+
+    if (virTypedParamsSerialize(params, nparams,
+                                (virTypedParameterRemotePtr *) &ret->params.params_val,
+                                &ret->params.params_len,
+                                0) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virTypedParamsFree(params, nparams);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainMigrateBegin3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                        virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                        virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                        virNetMessageErrorPtr rerr,
+                                        remote_domain_migrate_begin3_params_args *args,
+                                        remote_domain_migrate_begin3_params_ret *ret)
+{
+    char *xml = NULL;
+    virDomainPtr dom = NULL;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many migration parameters '%d' for limit '%d'"),
+                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
+                                  args->params.params_len,
+                                  0, &params, &nparams) < 0)
+        goto cleanup;
+
+    if (!(xml = virDomainMigrateBegin3Params(dom, params, nparams,
+                                             &cookieout, &cookieoutlen,
+                                             args->flags)))
+        goto cleanup;
+
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+    ret->xml = xml;
+
+    rv = 0;
+
+ cleanup:
+    virTypedParamsFree(params, nparams);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+static int
+remoteDispatchDomainMigratePrepare3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                          virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                          virNetMessageErrorPtr rerr,
+                                          remote_domain_migrate_prepare3_params_args *args,
+                                          remote_domain_migrate_prepare3_params_ret *ret)
+{
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    char **uri_out;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many migration parameters '%d' for limit '%d'"),
+                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
+        goto cleanup;
+    }
+
+    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
+                                  args->params.params_len,
+                                  0, &params, &nparams) < 0)
+        goto cleanup;
+
+    /* Wacky world of XDR ... */
+    if (VIR_ALLOC(uri_out) < 0)
+        goto cleanup;
+
+    if (virDomainMigratePrepare3Params(priv->conn, params, nparams,
+                                       args->cookie_in.cookie_in_val,
+                                       args->cookie_in.cookie_in_len,
+                                       &cookieout, &cookieoutlen,
+                                       uri_out, args->flags) < 0)
+        goto cleanup;
+
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+    ret->uri_out = !*uri_out ? NULL : uri_out;
+
+    rv = 0;
+
+ cleanup:
+    virTypedParamsFree(params, nparams);
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(uri_out);
+    }
+    return rv;
+}
+
+static int
+remoteDispatchDomainMigratePrepareTunnel3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                virNetServerClientPtr client,
+                                                virNetMessagePtr msg,
+                                                virNetMessageErrorPtr rerr,
+                                                remote_domain_migrate_prepare_tunnel3_params_args *args,
+                                                remote_domain_migrate_prepare_tunnel3_params_ret *ret)
+{
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virStreamPtr st = NULL;
+    daemonClientStreamPtr stream = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many migration parameters '%d' for limit '%d'"),
+                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
+        goto cleanup;
+    }
+
+    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
+                                  args->params.params_len,
+                                  0, &params, &nparams) < 0)
+        goto cleanup;
+
+    if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)) ||
+        !(stream = daemonCreateClientStream(client, st, remoteProgram,
+                                            &msg->header, false)))
+        goto cleanup;
+
+    if (virDomainMigratePrepareTunnel3Params(priv->conn, st, params, nparams,
+                                             args->cookie_in.cookie_in_val,
+                                             args->cookie_in.cookie_in_len,
+                                             &cookieout, &cookieoutlen,
+                                             args->flags) < 0)
+        goto cleanup;
+
+    if (daemonAddClientStream(client, stream, false) < 0)
+        goto cleanup;
+
+    ret->cookie_out.cookie_out_val = cookieout;
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    rv = 0;
+
+ cleanup:
+    virTypedParamsFree(params, nparams);
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(cookieout);
+        if (stream) {
+            virStreamAbort(st);
+            daemonFreeClientStream(client, stream);
+        } else {
+            virObjectUnref(st);
+        }
+    }
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigratePerform3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                          virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                          virNetMessageErrorPtr rerr,
+                                          remote_domain_migrate_perform3_params_args *args,
+                                          remote_domain_migrate_perform3_params_ret *ret)
+{
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    virDomainPtr dom = NULL;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    char *dconnuri;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many migration parameters '%d' for limit '%d'"),
+                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
+                                  args->params.params_len,
+                                  0, &params, &nparams) < 0)
+        goto cleanup;
+
+    dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
+
+    if (virDomainMigratePerform3Params(dom, dconnuri, params, nparams,
+                                       args->cookie_in.cookie_in_val,
+                                       args->cookie_in.cookie_in_len,
+                                       &cookieout, &cookieoutlen,
+                                       args->flags) < 0)
+        goto cleanup;
+
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+
+    rv = 0;
+
+ cleanup:
+    virTypedParamsFree(params, nparams);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigrateFinish3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                         virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                         virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                         virNetMessageErrorPtr rerr,
+                                         remote_domain_migrate_finish3_params_args *args,
+                                         remote_domain_migrate_finish3_params_ret *ret)
+{
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    virDomainPtr dom = NULL;
+    char *cookieout = NULL;
+    int cookieoutlen = 0;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many migration parameters '%d' for limit '%d'"),
+                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
+        goto cleanup;
+    }
+
+    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
+                                  args->params.params_len,
+                                  0, &params, &nparams) < 0)
+        goto cleanup;
+
+    dom = virDomainMigrateFinish3Params(priv->conn, params, nparams,
+                                        args->cookie_in.cookie_in_val,
+                                        args->cookie_in.cookie_in_len,
+                                        &cookieout, &cookieoutlen,
+                                        args->flags, args->cancelled);
+    if (!dom)
+        goto cleanup;
+
+    make_nonnull_domain(&ret->dom, dom);
+
+    ret->cookie_out.cookie_out_len = cookieoutlen;
+    ret->cookie_out.cookie_out_val = cookieout;
+
+    rv = 0;
+
+ cleanup:
+    virTypedParamsFree(params, nparams);
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(cookieout);
+    }
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainMigrateConfirm3Params(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                          virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                          virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                          virNetMessageErrorPtr rerr,
+                                          remote_domain_migrate_confirm3_params_args *args)
+{
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    virDomainPtr dom = NULL;
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->params.params_len > REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many migration parameters '%d' for limit '%d'"),
+                       args->params.params_len, REMOTE_DOMAIN_MIGRATE_PARAM_LIST_MAX);
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virTypedParamsDeserialize((virTypedParameterRemotePtr) args->params.params_val,
+                                  args->params.params_len,
+                                  0, &params, &nparams) < 0)
+        goto cleanup;
+
+    if (virDomainMigrateConfirm3Params(dom, params, nparams,
+                                       args->cookie_in.cookie_in_val,
+                                       args->cookie_in.cookie_in_len,
+                                       args->flags, args->cancelled) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    virTypedParamsFree(params, nparams);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectGetCPUModelNames(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                      virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                      virNetMessageErrorPtr rerr,
+                                      remote_connect_get_cpu_model_names_args *args,
+                                      remote_connect_get_cpu_model_names_ret *ret)
+{
+    int len, rv = -1;
+    char **models = NULL;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    len = virConnectGetCPUModelNames(priv->conn, args->arch,
+                                     args->need_results ? &models : NULL,
+                                     args->flags);
+    if (len < 0)
+        goto cleanup;
+
+    if (len > REMOTE_CONNECT_CPU_MODELS_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many CPU models '%d' for limit '%d'"),
+                       len, REMOTE_CONNECT_CPU_MODELS_MAX);
+        goto cleanup;
+    }
+
+    if (len && models) {
+        ret->models.models_val = models;
+        ret->models.models_len = len;
+        models = NULL;
+    } else {
+        ret->models.models_val = NULL;
+        ret->models.models_len = 0;
+    }
+
+    ret->ret = len;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virStringListFree(models);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainCreateXMLWithFiles(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                       virNetServerClientPtr client,
+                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                       virNetMessageErrorPtr rerr,
+                                       remote_domain_create_xml_with_files_args *args,
+                                       remote_domain_create_xml_with_files_ret *ret)
+{
+    int rv = -1;
+    virDomainPtr dom = NULL;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    int *files = NULL;
+    unsigned int nfiles = 0;
+    size_t i;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (VIR_ALLOC_N(files, msg->nfds) < 0)
+        goto cleanup;
+    for (i = 0; i < msg->nfds; i++) {
+        if ((files[i] = virNetMessageDupFD(msg, i)) < 0)
+            goto cleanup;
+        nfiles++;
+    }
+
+    if ((dom = virDomainCreateXMLWithFiles(priv->conn, args->xml_desc,
+                                           nfiles, files,
+                                           args->flags)) == NULL)
+        goto cleanup;
+
+    make_nonnull_domain(&ret->dom, dom);
+    rv = 0;
+
+ cleanup:
+    for (i = 0; i < nfiles; i++)
+        VIR_FORCE_CLOSE(files[i]);
+    VIR_FREE(files);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int remoteDispatchDomainCreateWithFiles(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                               virNetServerClientPtr client,
+                                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                               virNetMessageErrorPtr rerr,
+                                               remote_domain_create_with_files_args *args,
+                                               remote_domain_create_with_files_ret *ret)
+{
+    int rv = -1;
+    virDomainPtr dom = NULL;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    int *files = NULL;
+    unsigned int nfiles = 0;
+    size_t i;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (VIR_ALLOC_N(files, msg->nfds) < 0)
+        goto cleanup;
+    for (i = 0; i < msg->nfds; i++) {
+        if ((files[i] = virNetMessageDupFD(msg, i)) < 0)
+            goto cleanup;
+        nfiles++;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainCreateWithFiles(dom,
+                                 nfiles, files,
+                                 args->flags) < 0)
+        goto cleanup;
+
+    make_nonnull_domain(&ret->dom, dom);
+    rv = 0;
+
+ cleanup:
+    for (i = 0; i < nfiles; i++)
+        VIR_FORCE_CLOSE(files[i]);
+    VIR_FREE(files);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                             virNetServerClientPtr client,
+                                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                             virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                             remote_connect_network_event_register_any_args *args,
+                                             remote_connect_network_event_register_any_ret *ret)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virNetworkPtr net = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->net &&
+        !(net = get_nonnull_network(priv->conn, *args->net)))
+        goto cleanup;
+
+    if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST || args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported network event ID %d"), args->eventID);
+        goto cleanup;
+    }
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+     * success, we use 'ref' to save a copy of the pointer.  */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->eventID = args->eventID;
+    callback->callbackID = -1;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->networkEventCallbacks,
+                           priv->nnetworkEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectNetworkEventRegisterAny(priv->conn,
+                                                        net,
+                                                        args->eventID,
+                                                        networkEventCallbacks[args->eventID],
+                                                        ref,
+                                                        remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->networkEventCallbacks,
+                     priv->nnetworkEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+    ret->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(net);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                               virNetServerClientPtr client,
+                                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                               virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                               remote_connect_network_event_deregister_any_args *args)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    for (i = 0; i < priv->nnetworkEventCallbacks; i++) {
+        if (priv->networkEventCallbacks[i]->callbackID == args->callbackID)
+            break;
+    }
+    if (i == priv->nnetworkEventCallbacks) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("network event callback %d not registered"),
+                       args->callbackID);
+        goto cleanup;
+    }
+
+    if (virConnectNetworkEventDeregisterAny(priv->conn, args->callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->networkEventCallbacks, i,
+                       priv->nnetworkEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectStoragePoolEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                 virNetServerClientPtr client,
+                                                 virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                 virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                 remote_connect_storage_pool_event_register_any_args *args,
+                                                 remote_connect_storage_pool_event_register_any_ret *ret)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virStoragePoolPtr  pool = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->pool &&
+        !(pool = get_nonnull_storage_pool(priv->conn, *args->pool)))
+        goto cleanup;
+
+    if (args->eventID >= VIR_STORAGE_POOL_EVENT_ID_LAST || args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported storage pool event ID %d"), args->eventID);
+        goto cleanup;
+    }
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+     * success, we use 'ref' to save a copy of the pointer.  */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->eventID = args->eventID;
+    callback->callbackID = -1;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->storageEventCallbacks,
+                           priv->nstorageEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectStoragePoolEventRegisterAny(priv->conn,
+                                                            pool,
+                                                            args->eventID,
+                                                            storageEventCallbacks[args->eventID],
+                                                            ref,
+                                                            remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->storageEventCallbacks,
+                     priv->nstorageEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+    ret->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(pool);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectStoragePoolEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                               virNetServerClientPtr client,
+                                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                               virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                               remote_connect_storage_pool_event_deregister_any_args *args)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    for (i = 0; i < priv->nstorageEventCallbacks; i++) {
+        if (priv->storageEventCallbacks[i]->callbackID == args->callbackID)
+            break;
+    }
+    if (i == priv->nstorageEventCallbacks) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("storage pool event callback %d not registered"),
+                       args->callbackID);
+        goto cleanup;
+    }
+
+    if (virConnectStoragePoolEventDeregisterAny(priv->conn, args->callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->storageEventCallbacks, i,
+                       priv->nstorageEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectNodeDeviceEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                virNetServerClientPtr client,
+                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                remote_connect_node_device_event_register_any_args *args,
+                                                remote_connect_node_device_event_register_any_ret *ret)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virNodeDevicePtr  dev = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->dev &&
+        !(dev = get_nonnull_node_device(priv->conn, *args->dev)))
+        goto cleanup;
+
+    if (args->eventID >= VIR_NODE_DEVICE_EVENT_ID_LAST || args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported node device event ID %d"), args->eventID);
+        goto cleanup;
+    }
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+     * success, we use 'ref' to save a copy of the pointer.  */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->eventID = args->eventID;
+    callback->callbackID = -1;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->nodeDeviceEventCallbacks,
+                           priv->nnodeDeviceEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectNodeDeviceEventRegisterAny(priv->conn,
+                                                           dev,
+                                                           args->eventID,
+                                                           nodeDeviceEventCallbacks[args->eventID],
+                                                           ref,
+                                                           remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->nodeDeviceEventCallbacks,
+                     priv->nnodeDeviceEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+    ret->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dev);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectNodeDeviceEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                  virNetServerClientPtr client,
+                                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                  virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                  remote_connect_node_device_event_deregister_any_args *args)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    for (i = 0; i < priv->nnodeDeviceEventCallbacks; i++) {
+        if (priv->nodeDeviceEventCallbacks[i]->callbackID == args->callbackID)
+            break;
+    }
+    if (i == priv->nnodeDeviceEventCallbacks) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("node device event callback %d not registered"),
+                       args->callbackID);
+        goto cleanup;
+    }
+
+    if (virConnectNodeDeviceEventDeregisterAny(priv->conn, args->callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->nodeDeviceEventCallbacks, i,
+                       priv->nnodeDeviceEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectSecretEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                            virNetServerClientPtr client,
+                                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                            virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                            remote_connect_secret_event_register_any_args *args,
+                                            remote_connect_secret_event_register_any_ret *ret)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virSecretPtr secret = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->secret &&
+        !(secret = get_nonnull_secret(priv->conn, *args->secret)))
+        goto cleanup;
+
+    if (args->eventID >= VIR_SECRET_EVENT_ID_LAST || args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported secret event ID %d"), args->eventID);
+        goto cleanup;
+    }
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+     * success, we use 'ref' to save a copy of the pointer.  */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->eventID = args->eventID;
+    callback->callbackID = -1;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->secretEventCallbacks,
+                           priv->nsecretEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectSecretEventRegisterAny(priv->conn,
+                                                       secret,
+                                                       args->eventID,
+                                                       secretEventCallbacks[args->eventID],
+                                                       ref,
+                                                       remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->secretEventCallbacks,
+                     priv->nsecretEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+    ret->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(secret);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectSecretEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                  virNetServerClientPtr client,
+                                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                  virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                  remote_connect_secret_event_deregister_any_args *args)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    for (i = 0; i < priv->nsecretEventCallbacks; i++) {
+        if (priv->secretEventCallbacks[i]->callbackID == args->callbackID)
+            break;
+    }
+    if (i == priv->nsecretEventCallbacks) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("node device event callback %d not registered"),
+                       args->callbackID);
+        goto cleanup;
+    }
+
+    if (virConnectSecretEventDeregisterAny(priv->conn, args->callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->secretEventCallbacks, i,
+                       priv->nsecretEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                              virNetServerClientPtr client,
+                                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                              virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                              qemu_connect_domain_monitor_event_register_args *args,
+                                              qemu_connect_domain_monitor_event_register_ret *ret)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virDomainPtr dom = NULL;
+    const char *event = args->event ? *args->event : NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->dom &&
+        !(dom = get_nonnull_domain(priv->conn, *args->dom)))
+        goto cleanup;
+
+    /* If we call register first, we could append a complete callback
+     * to our array, but on OOM append failure, we'd have to then hope
+     * deregister works to undo our register.  So instead we append an
+     * incomplete callback to our array, then register, then fix up
+     * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+     * success, we use 'ref' to save a copy of the pointer.  */
+    if (VIR_ALLOC(callback) < 0)
+        goto cleanup;
+    callback->client = virObjectRef(client);
+    callback->callbackID = -1;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->qemuEventCallbacks,
+                           priv->nqemuEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectDomainQemuMonitorEventRegister(priv->conn,
+                                                               dom,
+                                                               event,
+                                                               remoteRelayDomainQemuMonitorEvent,
+                                                               ref,
+                                                               remoteEventCallbackFree,
+                                                               args->flags)) < 0) {
+        VIR_SHRINK_N(priv->qemuEventCallbacks,
+                     priv->nqemuEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+    ret->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    remoteEventCallbackFree(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+
+static int
+qemuDispatchConnectDomainMonitorEventDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                virNetServerClientPtr client,
+                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                qemu_connect_domain_monitor_event_deregister_args *args)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    for (i = 0; i < priv->nqemuEventCallbacks; i++) {
+        if (priv->qemuEventCallbacks[i]->callbackID == args->callbackID)
+            break;
+    }
+    if (i == priv->nqemuEventCallbacks) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("qemu monitor event callback %d not registered"),
+                       args->callbackID);
+        goto cleanup;
+    }
+
+    if (virConnectDomainQemuMonitorEventDeregister(priv->conn,
+                                                   args->callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->qemuEventCallbacks, i,
+                       priv->nqemuEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchDomainGetTime(virNetServerPtr server ATTRIBUTE_UNUSED,
+                            virNetServerClientPtr client,
+                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                            virNetMessageErrorPtr rerr,
+                            remote_domain_get_time_args *args,
+                            remote_domain_get_time_ret *ret)
+{
+    int rv = -1;
+    virDomainPtr dom = NULL;
+    long long seconds;
+    unsigned int nseconds;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if (virDomainGetTime(dom, &seconds, &nseconds, args->flags) < 0)
+        goto cleanup;
+
+    ret->seconds = seconds;
+    ret->nseconds = nseconds;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dom);
+    return rv;
+}
+
+
+static int
+remoteDispatchNodeGetFreePages(virNetServerPtr server ATTRIBUTE_UNUSED,
+                               virNetServerClientPtr client,
+                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                               virNetMessageErrorPtr rerr,
+                               remote_node_get_free_pages_args *args,
+                               remote_node_get_free_pages_ret *ret)
+{
+    int rv = -1;
+    int len;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->pages.pages_len * args->cellCount > REMOTE_NODE_MAX_CELLS) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("the result won't fit into REMOTE_NODE_MAX_CELLS"));
+        goto cleanup;
+    }
+
+    /* Allocate return buffer. */
+    if (VIR_ALLOC_N(ret->counts.counts_val,
+                    args->pages.pages_len * args->cellCount) < 0)
+        goto cleanup;
+
+    if ((len = virNodeGetFreePages(priv->conn,
+                                   args->pages.pages_len,
+                                   args->pages.pages_val,
+                                   args->startCell,
+                                   args->cellCount,
+                                   (unsigned long long *) ret->counts.counts_val,
+                                   args->flags)) <= 0)
+        goto cleanup;
+
+    ret->counts.counts_len = len;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+        VIR_FREE(ret->counts.counts_val);
+    }
+    return rv;
+}
+
+/* Copy contents of virNetworkDHCPLeasePtr to remote_network_dhcp_lease */
+static int
+remoteSerializeDHCPLease(remote_network_dhcp_lease *lease_dst, virNetworkDHCPLeasePtr lease_src)
+{
+    char **mac_tmp = NULL;
+    char **iaid_tmp = NULL;
+    char **hostname_tmp = NULL;
+    char **clientid_tmp = NULL;
+
+    lease_dst->expirytime = lease_src->expirytime;
+    lease_dst->type = lease_src->type;
+    lease_dst->prefix = lease_src->prefix;
+
+    if (VIR_STRDUP(lease_dst->iface, lease_src->iface) < 0 ||
+        VIR_STRDUP(lease_dst->ipaddr, lease_src->ipaddr) < 0)
+        goto error;
+
+    if (lease_src->mac) {
+        if (VIR_ALLOC(mac_tmp) < 0 ||
+            VIR_STRDUP(*mac_tmp, lease_src->mac) < 0)
+            goto error;
+    }
+    if (lease_src->iaid) {
+        if (VIR_ALLOC(iaid_tmp) < 0 ||
+            VIR_STRDUP(*iaid_tmp, lease_src->iaid) < 0)
+            goto error;
+    }
+    if (lease_src->hostname) {
+        if (VIR_ALLOC(hostname_tmp) < 0 ||
+            VIR_STRDUP(*hostname_tmp, lease_src->hostname) < 0)
+            goto error;
+    }
+    if (lease_src->clientid) {
+        if (VIR_ALLOC(clientid_tmp) < 0 ||
+            VIR_STRDUP(*clientid_tmp, lease_src->clientid) < 0)
+            goto error;
+    }
+
+    lease_dst->mac = mac_tmp;
+    lease_dst->iaid = iaid_tmp;
+    lease_dst->hostname = hostname_tmp;
+    lease_dst->clientid = clientid_tmp;
+
+    return 0;
+
+ error:
+    if (mac_tmp)
+        VIR_FREE(*mac_tmp);
+    if (iaid_tmp)
+        VIR_FREE(*iaid_tmp);
+    if (hostname_tmp)
+        VIR_FREE(*hostname_tmp);
+    if (clientid_tmp)
+        VIR_FREE(*clientid_tmp);
+    VIR_FREE(mac_tmp);
+    VIR_FREE(iaid_tmp);
+    VIR_FREE(hostname_tmp);
+    VIR_FREE(clientid_tmp);
+    VIR_FREE(lease_dst->ipaddr);
+    VIR_FREE(lease_dst->iface);
+    return -1;
+}
+
+
+static int
+remoteDispatchNetworkGetDHCPLeases(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client,
+                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                   virNetMessageErrorPtr rerr,
+                                   remote_network_get_dhcp_leases_args *args,
+                                   remote_network_get_dhcp_leases_ret *ret)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
+    virNetworkDHCPLeasePtr *leases = NULL;
+    virNetworkPtr net = NULL;
+    int nleases = 0;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(net = get_nonnull_network(priv->conn, args->net)))
+        goto cleanup;
+
+    if ((nleases = virNetworkGetDHCPLeases(net,
+                                           args->mac ? *args->mac : NULL,
+                                           args->need_results ? &leases : NULL,
+                                           args->flags)) < 0)
+        goto cleanup;
+
+    if (nleases > REMOTE_NETWORK_DHCP_LEASES_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Number of leases is %d, which exceeds max limit: %d"),
+                       nleases, REMOTE_NETWORK_DHCP_LEASES_MAX);
+        goto cleanup;
+    }
+
+    if (leases && nleases) {
+        if (VIR_ALLOC_N(ret->leases.leases_val, nleases) < 0)
+            goto cleanup;
+
+        ret->leases.leases_len = nleases;
+
+        for (i = 0; i < nleases; i++) {
+            if (remoteSerializeDHCPLease(ret->leases.leases_val + i, leases[i]) < 0)
+                goto cleanup;
+        }
+
+    } else {
+        ret->leases.leases_len = 0;
+        ret->leases.leases_val = NULL;
+    }
+
+    ret->ret = nleases;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    if (leases && nleases > 0)
+        for (i = 0; i < nleases; i++)
+            virNetworkDHCPLeaseFree(leases[i]);
+    VIR_FREE(leases);
+    virObjectUnref(net);
+    return rv;
+}
+
+
+static int
+remoteDispatchConnectGetAllDomainStats(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                       virNetServerClientPtr client,
+                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                       virNetMessageErrorPtr rerr,
+                                       remote_connect_get_all_domain_stats_args *args,
+                                       remote_connect_get_all_domain_stats_ret *ret)
+{
+    int rv = -1;
+    size_t i;
+    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
+    virDomainStatsRecordPtr *retStats = NULL;
+    int nrecords = 0;
+    virDomainPtr *doms = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (args->doms.doms_len) {
+        if (VIR_ALLOC_N(doms, args->doms.doms_len + 1) < 0)
+            goto cleanup;
+
+        for (i = 0; i < args->doms.doms_len; i++) {
+            if (!(doms[i] = get_nonnull_domain(priv->conn, args->doms.doms_val[i])))
+                goto cleanup;
+        }
+
+        if ((nrecords = virDomainListGetStats(doms,
+                                              args->stats,
+                                              &retStats,
+                                              args->flags)) < 0)
+            goto cleanup;
+    } else {
+        if ((nrecords = virConnectGetAllDomainStats(priv->conn,
+                                                    args->stats,
+                                                    &retStats,
+                                                    args->flags)) < 0)
+            goto cleanup;
+    }
+
+    if (nrecords > REMOTE_CONNECT_GET_ALL_DOMAIN_STATS_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Number of domain stats records is %d, "
+                         "which exceeds max limit: %d"),
+                       nrecords, REMOTE_CONNECT_GET_ALL_DOMAIN_STATS_MAX);
+        goto cleanup;
+    }
+
+    if (nrecords) {
+        if (VIR_ALLOC_N(ret->retStats.retStats_val, nrecords) < 0)
+            goto cleanup;
+
+        ret->retStats.retStats_len = nrecords;
+
+        for (i = 0; i < nrecords; i++) {
+            remote_domain_stats_record *dst = ret->retStats.retStats_val + i;
+
+            make_nonnull_domain(&dst->dom, retStats[i]->dom);
+
+            if (virTypedParamsSerialize(retStats[i]->params,
+                                        retStats[i]->nparams,
+                                        (virTypedParameterRemotePtr *) &dst->params.params_val,
+                                        &dst->params.params_len,
+                                        VIR_TYPED_PARAM_STRING_OKAY) < 0)
+                goto cleanup;
+        }
+    } else {
+        ret->retStats.retStats_len = 0;
+        ret->retStats.retStats_val = NULL;
+    }
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+
+    virDomainStatsRecordListFree(retStats);
+    virObjectListFree(doms);
+
+    return rv;
+}
+
+
+static int
+remoteDispatchNodeAllocPages(virNetServerPtr server ATTRIBUTE_UNUSED,
+                             virNetServerClientPtr client,
+                             virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                             virNetMessageErrorPtr rerr,
+                             remote_node_alloc_pages_args *args,
+                             remote_node_alloc_pages_ret *ret)
+{
+    int rv = -1;
+    int len;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if ((len = virNodeAllocPages(priv->conn,
+                                 args->pageSizes.pageSizes_len,
+                                 args->pageSizes.pageSizes_val,
+                                 (unsigned long long *) args->pageCounts.pageCounts_val,
+                                 args->startCell,
+                                 args->cellCount,
+                                 args->flags)) < 0)
+        goto cleanup;
+
+    ret->ret = len;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+
+static int
+remoteDispatchDomainGetFSInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
+                              virNetServerClientPtr client,
+                              virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                              virNetMessageErrorPtr rerr,
+                              remote_domain_get_fsinfo_args *args,
+                              remote_domain_get_fsinfo_ret *ret)
+{
+    int rv = -1;
+    size_t i, j;
+    struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
+    virDomainFSInfoPtr *info = NULL;
+    virDomainPtr dom = NULL;
+    remote_domain_fsinfo *dst;
+    int ninfo = 0;
+    size_t ndisk;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if ((ninfo = virDomainGetFSInfo(dom, &info, args->flags)) < 0)
+        goto cleanup;
+
+    if (ninfo > REMOTE_DOMAIN_FSINFO_MAX) {
+        virReportError(VIR_ERR_RPC,
+                       _("Too many mountpoints in fsinfo: %d for limit %d"),
+                       ninfo, REMOTE_DOMAIN_FSINFO_MAX);
+        goto cleanup;
+    }
+
+    if (ninfo) {
+        if (VIR_ALLOC_N(ret->info.info_val, ninfo) < 0)
+            goto cleanup;
+
+        ret->info.info_len = ninfo;
+
+        for (i = 0; i < ninfo; i++) {
+            dst = &ret->info.info_val[i];
+            if (VIR_STRDUP(dst->mountpoint, info[i]->mountpoint) < 0)
+                goto cleanup;
+
+            if (VIR_STRDUP(dst->name, info[i]->name) < 0)
+                goto cleanup;
+
+            if (VIR_STRDUP(dst->fstype, info[i]->fstype) < 0)
+                goto cleanup;
+
+            ndisk = info[i]->ndevAlias;
+            if (ndisk > REMOTE_DOMAIN_FSINFO_DISKS_MAX) {
+                virReportError(VIR_ERR_RPC,
+                               _("Too many disks in fsinfo: %zd for limit %d"),
+                               ndisk, REMOTE_DOMAIN_FSINFO_DISKS_MAX);
+                goto cleanup;
+            }
+
+            if (ndisk > 0) {
+                if (VIR_ALLOC_N(dst->dev_aliases.dev_aliases_val, ndisk) < 0)
+                    goto cleanup;
+
+                for (j = 0; j < ndisk; j++) {
+                    if (VIR_STRDUP(dst->dev_aliases.dev_aliases_val[j],
+                                   info[i]->devAlias[j]) < 0)
+                        goto cleanup;
+                }
+
+                dst->dev_aliases.dev_aliases_len = ndisk;
+            } else {
+                dst->dev_aliases.dev_aliases_val = NULL;
+                dst->dev_aliases.dev_aliases_len = 0;
+            }
+        }
+
+    } else {
+        ret->info.info_len = 0;
+        ret->info.info_val = NULL;
+    }
+
+    ret->ret = ninfo;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0) {
+        virNetMessageSaveError(rerr);
+
+        if (ret->info.info_val && ninfo > 0) {
+            for (i = 0; i < ninfo; i++) {
+                dst = &ret->info.info_val[i];
+                VIR_FREE(dst->mountpoint);
+                if (dst->dev_aliases.dev_aliases_val) {
+                    for (j = 0; j < dst->dev_aliases.dev_aliases_len; j++)
+                        VIR_FREE(dst->dev_aliases.dev_aliases_val[j]);
+                    VIR_FREE(dst->dev_aliases.dev_aliases_val);
+                }
+            }
+            VIR_FREE(ret->info.info_val);
+        }
+    }
+    virObjectUnref(dom);
+    if (ninfo >= 0)
+        for (i = 0; i < ninfo; i++)
+            virDomainFSInfoFree(info[i]);
+    VIR_FREE(info);
+
+    return rv;
+}
+
+
+static int
+remoteSerializeDomainInterface(virDomainInterfacePtr *ifaces,
+                               unsigned int ifaces_count,
+                               remote_domain_interface_addresses_ret *ret)
+{
+    size_t i, j;
+
+    if (ifaces_count > REMOTE_DOMAIN_INTERFACE_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Number of interfaces, %d exceeds the max limit: %d"),
+                       ifaces_count, REMOTE_DOMAIN_INTERFACE_MAX);
+        return -1;
+    }
+
+    if (VIR_ALLOC_N(ret->ifaces.ifaces_val, ifaces_count) < 0)
+        return -1;
+
+    ret->ifaces.ifaces_len = ifaces_count;
+
+    for (i = 0; i < ifaces_count; i++) {
+        virDomainInterfacePtr iface = ifaces[i];
+        remote_domain_interface *iface_ret = &(ret->ifaces.ifaces_val[i]);
+
+        if ((VIR_STRDUP(iface_ret->name, iface->name)) < 0)
+            goto cleanup;
+
+        if (iface->hwaddr &&
+            (VIR_ALLOC(iface_ret->hwaddr) < 0 ||
+             VIR_STRDUP(*iface_ret->hwaddr, iface->hwaddr) < 0))
+            goto cleanup;
+
+        if (iface->naddrs > REMOTE_DOMAIN_IP_ADDR_MAX) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Number of interfaces, %d exceeds the max limit: %d"),
+                           iface->naddrs, REMOTE_DOMAIN_IP_ADDR_MAX);
+            goto cleanup;
+        }
+
+        if (VIR_ALLOC_N(iface_ret->addrs.addrs_val,
+                        iface->naddrs) < 0)
+            goto cleanup;
+
+        iface_ret->addrs.addrs_len = iface->naddrs;
+
+        for (j = 0; j < iface->naddrs; j++) {
+            virDomainIPAddressPtr ip_addr = &(iface->addrs[j]);
+            remote_domain_ip_addr *ip_addr_ret =
+                &(iface_ret->addrs.addrs_val[j]);
+
+            if (VIR_STRDUP(ip_addr_ret->addr, ip_addr->addr) < 0)
+                goto cleanup;
+
+            ip_addr_ret->prefix = ip_addr->prefix;
+            ip_addr_ret->type = ip_addr->type;
+        }
+    }
+
+    return 0;
+
+ cleanup:
+    if (ret->ifaces.ifaces_val) {
+        for (i = 0; i < ifaces_count; i++) {
+            remote_domain_interface *iface_ret = &(ret->ifaces.ifaces_val[i]);
+            VIR_FREE(iface_ret->name);
+            if (iface_ret->hwaddr) {
+                VIR_FREE(*iface_ret->hwaddr);
+                VIR_FREE(iface_ret->hwaddr);
+            }
+            for (j = 0; j < iface_ret->addrs.addrs_len; j++) {
+                remote_domain_ip_addr *ip_addr =
+                    &(iface_ret->addrs.addrs_val[j]);
+                VIR_FREE(ip_addr->addr);
+            }
+        }
+        VIR_FREE(ret->ifaces.ifaces_val);
+    }
+
+    return -1;
+}
+
+
+static int
+remoteDispatchDomainInterfaceAddresses(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                       virNetServerClientPtr client,
+                                       virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                       virNetMessageErrorPtr rerr,
+                                       remote_domain_interface_addresses_args *args,
+                                       remote_domain_interface_addresses_ret *ret)
+{
+    size_t i;
+    int rv = -1;
+    virDomainPtr dom = NULL;
+    virDomainInterfacePtr *ifaces = NULL;
+    int ifaces_count = 0;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    if ((ifaces_count = virDomainInterfaceAddresses(dom, &ifaces, args->source, args->flags)) < 0)
+        goto cleanup;
+
+    if (remoteSerializeDomainInterface(ifaces, ifaces_count, ret) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+
+    virObjectUnref(dom);
+
+    if (ifaces && ifaces_count > 0) {
+        for (i = 0; i < ifaces_count; i++)
+            virDomainInterfaceFree(ifaces[i]);
+    }
+    VIR_FREE(ifaces);
+
+    return rv;
+}
+
+
+static int
+remoteDispatchStorageVolGetInfoFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                     virNetServerClientPtr client,
+                                     virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                     virNetMessageErrorPtr rerr,
+                                     remote_storage_vol_get_info_flags_args *args,
+                                     remote_storage_vol_get_info_flags_ret *ret)
+{
+    int rv = -1;
+    virStorageVolPtr vol = NULL;
+    virStorageVolInfo tmp;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(vol = get_nonnull_storage_vol(priv->conn, args->vol)))
+        goto cleanup;
+
+    if (virStorageVolGetInfoFlags(vol, &tmp, args->flags) < 0)
+        goto cleanup;
+
+    ret->type = tmp.type;
+    ret->capacity = tmp.capacity;
+    ret->allocation = tmp.allocation;
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(vol);
+    return rv;
+}
+
+
+/*----- Helpers. -----*/
+
+/* 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)
+{
+    /* Should we believe the domain.id sent by the client?  Maybe
+     * this should be a check rather than an assignment? XXX
+     */
+    return virGetDomain(conn, domain.name, BAD_CAST domain.uuid, domain.id);
+}
+
+static virNetworkPtr
+get_nonnull_network(virConnectPtr conn, remote_nonnull_network network)
+{
+    return virGetNetwork(conn, network.name, BAD_CAST network.uuid);
+}
+
+static virInterfacePtr
+get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface)
+{
+    return virGetInterface(conn, iface.name, iface.mac);
+}
+
+static virStoragePoolPtr
+get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool)
+{
+    return virGetStoragePool(conn, pool.name, BAD_CAST pool.uuid,
+                             NULL, NULL);
+}
+
+static virStorageVolPtr
+get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol)
+{
+    virStorageVolPtr ret;
+    ret = virGetStorageVol(conn, vol.pool, vol.name, vol.key,
+                           NULL, NULL);
+    return ret;
+}
+
+static virSecretPtr
+get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret)
+{
+    return virGetSecret(conn, BAD_CAST secret.uuid, secret.usageType, secret.usageID);
+}
+
+static virNWFilterPtr
+get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter)
+{
+    return virGetNWFilter(conn, nwfilter.name, BAD_CAST nwfilter.uuid);
+}
+
+static virDomainSnapshotPtr
+get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot)
+{
+    return virGetDomainSnapshot(dom, snapshot.name);
+}
+
+static virNodeDevicePtr
+get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev)
+{
+    return virGetNodeDevice(conn, dev.name);
+}
+
+/* Make remote_nonnull_domain and remote_nonnull_network. */
+static void
+make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
+{
+    dom_dst->id = dom_src->id;
+    ignore_value(VIR_STRDUP_QUIET(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)
+{
+    ignore_value(VIR_STRDUP_QUIET(net_dst->name, net_src->name));
+    memcpy(net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN);
+}
+
+static void
+make_nonnull_interface(remote_nonnull_interface *interface_dst,
+                       virInterfacePtr interface_src)
+{
+    ignore_value(VIR_STRDUP_QUIET(interface_dst->name, interface_src->name));
+    ignore_value(VIR_STRDUP_QUIET(interface_dst->mac, interface_src->mac));
+}
+
+static void
+make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src)
+{
+    ignore_value(VIR_STRDUP_QUIET(pool_dst->name, pool_src->name));
+    memcpy(pool_dst->uuid, pool_src->uuid, VIR_UUID_BUFLEN);
+}
+
+static void
+make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src)
+{
+    ignore_value(VIR_STRDUP_QUIET(vol_dst->pool, vol_src->pool));
+    ignore_value(VIR_STRDUP_QUIET(vol_dst->name, vol_src->name));
+    ignore_value(VIR_STRDUP_QUIET(vol_dst->key, vol_src->key));
+}
+
+static void
+make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src)
+{
+    ignore_value(VIR_STRDUP_QUIET(dev_dst->name, dev_src->name));
+}
+
+static void
+make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src)
+{
+    memcpy(secret_dst->uuid, secret_src->uuid, VIR_UUID_BUFLEN);
+    secret_dst->usageType = secret_src->usageType;
+    ignore_value(VIR_STRDUP_QUIET(secret_dst->usageID, secret_src->usageID));
+}
+
+static void
+make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src)
+{
+    ignore_value(VIR_STRDUP_QUIET(nwfilter_dst->name, nwfilter_src->name));
+    memcpy(nwfilter_dst->uuid, nwfilter_src->uuid, VIR_UUID_BUFLEN);
+}
+
+static void
+make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src)
+{
+    ignore_value(VIR_STRDUP_QUIET(snapshot_dst->name, snapshot_src->name));
+    make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain);
+}
+
+static int
+remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors,
+                                int nerrors,
+                                remote_domain_disk_error **ret_errors_val,
+                                u_int *ret_errors_len)
+{
+    remote_domain_disk_error *val = NULL;
+    size_t i = 0;
+
+    if (VIR_ALLOC_N(val, nerrors) < 0)
+        goto error;
+
+    for (i = 0; i < nerrors; i++) {
+        if (VIR_STRDUP(val[i].disk, errors[i].disk) < 0)
+            goto error;
+        val[i].error = errors[i].error;
+    }
+
+    *ret_errors_len = nerrors;
+    *ret_errors_val = val;
+
+    return 0;
+
+ error:
+    if (val) {
+        size_t j;
+        for (j = 0; j < i; j++)
+            VIR_FREE(val[j].disk);
+        VIR_FREE(val);
+    }
+    return -1;
+}
diff --git a/src/remote/remote_daemon_dispatch.h b/src/remote/remote_daemon_dispatch.h
new file mode 100644 (file)
index 0000000..015ab97
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * remote_daemon_dispatch.h: handlers for RPC method calls
+ *
+ * Copyright (C) 2007-2018 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Richard W.M. Jones <rjones@redhat.com>
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#ifndef __REMOTE_DAEMON_DISPATCH_H__
+# define __REMOTE_DAEMON_DISPATCH_H__
+
+# include "remote_protocol.h"
+# include "rpc/virnetserverprogram.h"
+# include "rpc/virnetserverclient.h"
+
+
+extern virNetServerProgramProc remoteProcs[];
+extern size_t remoteNProcs;
+
+extern virNetServerProgramProc lxcProcs[];
+extern size_t lxcNProcs;
+
+extern virNetServerProgramProc qemuProcs[];
+extern size_t qemuNProcs;
+
+void remoteClientFree(void *data);
+void *remoteClientNew(virNetServerClientPtr client,
+                      void *opaque);
+
+#endif /* __REMOTE_DAEMON_DISPATCH_H__ */
diff --git a/src/remote/remote_daemon_stream.c b/src/remote/remote_daemon_stream.c
new file mode 100644 (file)
index 0000000..4dd3af9
--- /dev/null
@@ -0,0 +1,925 @@
+/*
+ * remote_daemon_stream.c: APIs for managing client streams
+ *
+ * Copyright (C) 2009-2018 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#include <config.h>
+
+#include "remote_daemon_stream.h"
+#include "remote_daemon_dispatch.h"
+#include "viralloc.h"
+#include "virlog.h"
+#include "virnetserverclient.h"
+#include "virerror.h"
+#include "libvirt_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_STREAMS
+
+VIR_LOG_INIT("daemon.stream");
+
+struct daemonClientStream {
+    daemonClientPrivatePtr priv;
+    int refs;
+
+    virNetServerProgramPtr prog;
+
+    virStreamPtr st;
+    int procedure;
+    unsigned int serial;
+
+    bool recvEOF;
+    bool closed;
+
+    int filterID;
+
+    virNetMessagePtr rx;
+    bool tx;
+
+    bool allowSkip;
+    size_t dataLen; /* How much data is there remaining until we see a hole */
+
+    daemonClientStreamPtr next;
+};
+
+static int
+daemonStreamHandleWrite(virNetServerClientPtr client,
+                        daemonClientStream *stream);
+static int
+daemonStreamHandleRead(virNetServerClientPtr client,
+                       daemonClientStream *stream);
+static int
+daemonStreamHandleFinish(virNetServerClientPtr client,
+                         daemonClientStream *stream,
+                         virNetMessagePtr msg);
+static int
+daemonStreamHandleAbort(virNetServerClientPtr client,
+                        daemonClientStream *stream,
+                        virNetMessagePtr msg);
+
+
+
+static void
+daemonStreamUpdateEvents(daemonClientStream *stream)
+{
+    int newEvents = 0;
+    if (stream->closed)
+        return;
+    if (stream->rx)
+        newEvents |= VIR_STREAM_EVENT_WRITABLE;
+    if (stream->tx && !stream->recvEOF)
+        newEvents |= VIR_STREAM_EVENT_READABLE;
+
+    virStreamEventUpdateCallback(stream->st, newEvents);
+}
+
+/*
+ * Invoked when an outgoing data packet message has been fully sent.
+ * This simply re-enables TX of further data.
+ *
+ * The idea is to stop the daemon growing without bound due to
+ * fast stream, but slow client
+ */
+static void
+daemonStreamMessageFinished(virNetMessagePtr msg,
+                            void *opaque)
+{
+    daemonClientStream *stream = opaque;
+    VIR_DEBUG("stream=%p proc=%d serial=%u",
+              stream, msg->header.proc, msg->header.serial);
+
+    stream->tx = true;
+    daemonStreamUpdateEvents(stream);
+
+    daemonFreeClientStream(NULL, stream);
+}
+
+
+/*
+ * Callback that gets invoked when a stream becomes writable/readable
+ */
+static void
+daemonStreamEvent(virStreamPtr st, int events, void *opaque)
+{
+    virNetServerClientPtr client = opaque;
+    daemonClientStream *stream;
+    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
+
+    virMutexLock(&priv->lock);
+
+    stream = priv->streams;
+    while (stream) {
+        if (stream->st == st)
+            break;
+        stream = stream->next;
+    }
+
+    if (!stream) {
+        VIR_WARN("event for client=%p stream st=%p, but missing stream state", client, st);
+        virStreamEventRemoveCallback(st);
+        goto cleanup;
+    }
+
+    VIR_DEBUG("st=%p events=%d EOF=%d closed=%d", st, events, stream->recvEOF, stream->closed);
+
+    if (!stream->closed &&
+        (events & VIR_STREAM_EVENT_WRITABLE)) {
+        if (daemonStreamHandleWrite(client, stream) < 0) {
+            daemonRemoveClientStream(client, stream);
+            virNetServerClientClose(client);
+            goto cleanup;
+        }
+    }
+
+    if (!stream->closed && !stream->recvEOF &&
+        (events & (VIR_STREAM_EVENT_READABLE))) {
+        events = events & ~(VIR_STREAM_EVENT_READABLE);
+        if (daemonStreamHandleRead(client, stream) < 0) {
+            daemonRemoveClientStream(client, stream);
+            virNetServerClientClose(client);
+            goto cleanup;
+        }
+        /* If we detected EOF during read processing,
+         * then clear hangup/error conditions, since
+         * we want the client to see the EOF message
+         * we just sent them
+         */
+        if (stream->recvEOF)
+            events = events & ~(VIR_STREAM_EVENT_HANGUP |
+                                VIR_STREAM_EVENT_ERROR);
+    }
+
+    /* If we have a completion/abort message, always process it */
+    if (stream->rx) {
+        virNetMessagePtr msg = stream->rx;
+        switch (msg->header.status) {
+        case VIR_NET_CONTINUE:
+            /* nada */
+            break;
+        case VIR_NET_OK:
+            virNetMessageQueueServe(&stream->rx);
+            if (daemonStreamHandleFinish(client, stream, msg) < 0) {
+                virNetMessageFree(msg);
+                daemonRemoveClientStream(client, stream);
+                virNetServerClientClose(client);
+                goto cleanup;
+            }
+            break;
+        case VIR_NET_ERROR:
+        default:
+            virNetMessageQueueServe(&stream->rx);
+            if (daemonStreamHandleAbort(client, stream, msg) < 0) {
+                virNetMessageFree(msg);
+                daemonRemoveClientStream(client, stream);
+                virNetServerClientClose(client);
+                goto cleanup;
+            }
+            break;
+        }
+    }
+
+
+    /* If we got HANGUP, we need to only send an empty
+     * packet so the client sees an EOF and cleans up
+     */
+    if (!stream->closed && !stream->recvEOF &&
+        (events & VIR_STREAM_EVENT_HANGUP)) {
+        virNetMessagePtr msg;
+        events &= ~(VIR_STREAM_EVENT_HANGUP);
+        stream->tx = false;
+        stream->recvEOF = true;
+        if (!(msg = virNetMessageNew(false))) {
+            daemonRemoveClientStream(client, stream);
+            virNetServerClientClose(client);
+            goto cleanup;
+        }
+        msg->cb = daemonStreamMessageFinished;
+        msg->opaque = stream;
+        stream->refs++;
+        if (virNetServerProgramSendStreamData(remoteProgram,
+                                              client,
+                                              msg,
+                                              stream->procedure,
+                                              stream->serial,
+                                              "", 0) < 0) {
+            virNetMessageFree(msg);
+            daemonRemoveClientStream(client, stream);
+            virNetServerClientClose(client);
+            goto cleanup;
+        }
+    }
+
+    if (!stream->closed &&
+        (events & (VIR_STREAM_EVENT_ERROR | VIR_STREAM_EVENT_HANGUP))) {
+        int ret;
+        virNetMessagePtr msg;
+        virNetMessageError rerr;
+        virErrorPtr origErr = virSaveLastError();
+
+        memset(&rerr, 0, sizeof(rerr));
+        stream->closed = true;
+        virStreamEventRemoveCallback(stream->st);
+        virStreamAbort(stream->st);
+        if (origErr && origErr->code != VIR_ERR_OK) {
+            virSetError(origErr);
+        } else {
+            if (events & VIR_STREAM_EVENT_HANGUP)
+                virReportError(VIR_ERR_RPC,
+                               "%s", _("stream had unexpected termination"));
+            else
+                virReportError(VIR_ERR_RPC,
+                               "%s", _("stream had I/O failure"));
+        }
+        virFreeError(origErr);
+
+        msg = virNetMessageNew(false);
+        if (!msg) {
+            ret = -1;
+        } else {
+            ret = virNetServerProgramSendStreamError(remoteProgram,
+                                                     client,
+                                                     msg,
+                                                     &rerr,
+                                                     stream->procedure,
+                                                     stream->serial);
+        }
+        daemonRemoveClientStream(client, stream);
+        if (ret < 0)
+            virNetServerClientClose(client);
+        goto cleanup;
+    }
+
+    if (stream->closed) {
+        daemonRemoveClientStream(client, stream);
+    } else {
+        daemonStreamUpdateEvents(stream);
+    }
+
+ cleanup:
+    virMutexUnlock(&priv->lock);
+}
+
+
+/*
+ * @client: a locked client object
+ *
+ * Invoked by the main loop when filtering incoming messages.
+ *
+ * Returns 1 if the message was processed, 0 if skipped,
+ * -1 on fatal client error
+ */
+static int
+daemonStreamFilter(virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                   virNetMessagePtr msg,
+                   void *opaque)
+{
+    daemonClientStream *stream = opaque;
+    int ret = 0;
+
+    virMutexLock(&stream->priv->lock);
+
+    if (msg->header.type != VIR_NET_STREAM &&
+        msg->header.type != VIR_NET_STREAM_HOLE)
+        goto cleanup;
+
+    if (!virNetServerProgramMatches(stream->prog, msg))
+        goto cleanup;
+
+    if (msg->header.proc != stream->procedure ||
+        msg->header.serial != stream->serial)
+        goto cleanup;
+
+    VIR_DEBUG("Incoming client=%p, rx=%p, serial=%u, proc=%d, status=%d",
+              client, stream->rx, msg->header.proc,
+              msg->header.serial, msg->header.status);
+
+    virNetMessageQueuePush(&stream->rx, msg);
+    daemonStreamUpdateEvents(stream);
+    ret = 1;
+
+ cleanup:
+    virMutexUnlock(&stream->priv->lock);
+    return ret;
+}
+
+
+/*
+ * @conn: a connection object to associate the stream with
+ * @header: the method call to associate with the stream
+ *
+ * Creates a new stream for this conn
+ *
+ * Returns a new stream object, or NULL upon OOM
+ */
+daemonClientStream *
+daemonCreateClientStream(virNetServerClientPtr client,
+                         virStreamPtr st,
+                         virNetServerProgramPtr prog,
+                         virNetMessageHeaderPtr header,
+                         bool allowSkip)
+{
+    daemonClientStream *stream;
+    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
+
+    VIR_DEBUG("client=%p, proc=%d, serial=%u, st=%p",
+              client, header->proc, header->serial, st);
+
+    if (VIR_ALLOC(stream) < 0)
+        return NULL;
+
+    stream->refs = 1;
+    stream->priv = priv;
+    stream->prog = virObjectRef(prog);
+    stream->procedure = header->proc;
+    stream->serial = header->serial;
+    stream->filterID = -1;
+    stream->st = st;
+    stream->allowSkip = allowSkip;
+
+    return stream;
+}
+
+/*
+ * @stream: an unused client stream
+ *
+ * Frees the memory associated with this inactive client
+ * stream
+ */
+int daemonFreeClientStream(virNetServerClientPtr client,
+                           daemonClientStream *stream)
+{
+    virNetMessagePtr msg;
+    int ret = 0;
+
+    if (!stream)
+        return 0;
+
+    stream->refs--;
+    if (stream->refs)
+        return 0;
+
+    VIR_DEBUG("client=%p, proc=%d, serial=%u",
+              client, stream->procedure, stream->serial);
+
+    virObjectUnref(stream->prog);
+
+    msg = stream->rx;
+    while (msg) {
+        virNetMessagePtr tmp = msg->next;
+        if (client) {
+            /* Send a dummy reply to free up 'msg' & unblock client rx */
+            virNetMessageClear(msg);
+            msg->header.type = VIR_NET_REPLY;
+            if (virNetServerClientSendMessage(client, msg) < 0) {
+                virNetServerClientImmediateClose(client);
+                virNetMessageFree(msg);
+                ret = -1;
+            }
+        } else {
+            virNetMessageFree(msg);
+        }
+        msg = tmp;
+    }
+
+    virObjectUnref(stream->st);
+    VIR_FREE(stream);
+
+    return ret;
+}
+
+
+/*
+ * @client: a locked client to add the stream to
+ * @stream: a stream to add
+ */
+int daemonAddClientStream(virNetServerClientPtr client,
+                          daemonClientStream *stream,
+                          bool transmit)
+{
+    VIR_DEBUG("client=%p, proc=%d, serial=%u, st=%p, transmit=%d",
+              client, stream->procedure, stream->serial, stream->st, transmit);
+    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
+
+    if (stream->filterID != -1) {
+        VIR_WARN("Filter already added to client %p", client);
+        return -1;
+    }
+
+    if (virStreamEventAddCallback(stream->st, 0,
+                                  daemonStreamEvent, client,
+                                  virObjectFreeCallback) < 0)
+        return -1;
+
+    virObjectRef(client);
+
+    if ((stream->filterID = virNetServerClientAddFilter(client,
+                                                        daemonStreamFilter,
+                                                        stream)) < 0) {
+        virStreamEventRemoveCallback(stream->st);
+        return -1;
+    }
+
+    if (transmit)
+        stream->tx = true;
+
+    virMutexLock(&priv->lock);
+    stream->next = priv->streams;
+    priv->streams = stream;
+
+    daemonStreamUpdateEvents(stream);
+
+    virMutexUnlock(&priv->lock);
+
+    return 0;
+}
+
+
+/*
+ * @client: a locked client object
+ * @stream: an inactive, closed stream object
+ *
+ * Removes a stream from the list of active streams for the client
+ *
+ * Returns 0 if the stream was removed, -1 if it doesn't exist
+ */
+int
+daemonRemoveClientStream(virNetServerClientPtr client,
+                         daemonClientStream *stream)
+{
+    VIR_DEBUG("client=%p, proc=%d, serial=%u, st=%p",
+              client, stream->procedure, stream->serial, stream->st);
+    daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client);
+    daemonClientStream *curr = priv->streams;
+    daemonClientStream *prev = NULL;
+
+    if (stream->filterID != -1) {
+        virNetServerClientRemoveFilter(client,
+                                       stream->filterID);
+        stream->filterID = -1;
+    }
+
+    if (!stream->closed) {
+        stream->closed = true;
+        virStreamEventRemoveCallback(stream->st);
+        virStreamAbort(stream->st);
+    }
+
+    while (curr) {
+        if (curr == stream) {
+            if (prev)
+                prev->next = curr->next;
+            else
+                priv->streams = curr->next;
+            return daemonFreeClientStream(client, stream);
+        }
+        prev = curr;
+        curr = curr->next;
+    }
+    return -1;
+}
+
+
+void
+daemonRemoveAllClientStreams(daemonClientStream *stream)
+{
+    daemonClientStream *tmp;
+
+    VIR_DEBUG("stream=%p", stream);
+
+    while (stream) {
+        tmp = stream->next;
+
+        if (!stream->closed) {
+            stream->closed = true;
+            virStreamEventRemoveCallback(stream->st);
+            virStreamAbort(stream->st);
+        }
+
+        daemonFreeClientStream(NULL, stream);
+
+        VIR_DEBUG("next stream=%p", tmp);
+        stream = tmp;
+    }
+}
+
+/*
+ * Returns:
+ *   -1  if fatal error occurred
+ *    0  if message was fully processed
+ *    1  if message is still being processed
+ */
+static int
+daemonStreamHandleWriteData(virNetServerClientPtr client,
+                            daemonClientStream *stream,
+                            virNetMessagePtr msg)
+{
+    int ret;
+
+    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u, len=%zu, offset=%zu",
+              client, stream, msg->header.proc, msg->header.serial,
+              msg->bufferLength, msg->bufferOffset);
+
+    ret = virStreamSend(stream->st,
+                        msg->buffer + msg->bufferOffset,
+                        msg->bufferLength - msg->bufferOffset);
+
+    if (ret > 0) {
+        msg->bufferOffset += ret;
+
+        /* Partial write, so indicate we have more todo later */
+        if (msg->bufferOffset < msg->bufferLength)
+            return 1;
+    } else if (ret == -2) {
+        /* Blocking, so indicate we have more todo later */
+        return 1;
+    } else {
+        virNetMessageError rerr;
+
+        memset(&rerr, 0, sizeof(rerr));
+
+        VIR_INFO("Stream send failed");
+        stream->closed = true;
+        virStreamEventRemoveCallback(stream->st);
+        virStreamAbort(stream->st);
+
+        return virNetServerProgramSendReplyError(stream->prog,
+                                                 client,
+                                                 msg,
+                                                 &rerr,
+                                                 &msg->header);
+    }
+
+    return 0;
+}
+
+
+/*
+ * Process a finish handshake from the client.
+ *
+ * Returns a VIR_NET_OK confirmation if successful, or a VIR_NET_ERROR
+ * if there was a stream error
+ *
+ * Returns 0 if successfully sent RPC reply, -1 upon fatal error
+ */
+static int
+daemonStreamHandleFinish(virNetServerClientPtr client,
+                         daemonClientStream *stream,
+                         virNetMessagePtr msg)
+{
+    int ret;
+
+    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u",
+              client, stream, msg->header.proc, msg->header.serial);
+
+    stream->closed = true;
+    virStreamEventRemoveCallback(stream->st);
+    ret = virStreamFinish(stream->st);
+
+    if (ret < 0) {
+        virNetMessageError rerr;
+        memset(&rerr, 0, sizeof(rerr));
+        return virNetServerProgramSendReplyError(stream->prog,
+                                                 client,
+                                                 msg,
+                                                 &rerr,
+                                                 &msg->header);
+    } else {
+        /* Send zero-length confirm */
+        return virNetServerProgramSendStreamData(stream->prog,
+                                                 client,
+                                                 msg,
+                                                 stream->procedure,
+                                                 stream->serial,
+                                                 NULL, 0);
+    }
+}
+
+
+/*
+ * Process an abort request from the client.
+ *
+ * Returns 0 if successfully aborted, -1 upon error
+ */
+static int
+daemonStreamHandleAbort(virNetServerClientPtr client,
+                        daemonClientStream *stream,
+                        virNetMessagePtr msg)
+{
+    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u",
+              client, stream, msg->header.proc, msg->header.serial);
+    int ret;
+    bool raise_error = false;
+
+    stream->closed = true;
+    virStreamEventRemoveCallback(stream->st);
+    ret = virStreamAbort(stream->st);
+
+    if (msg->header.status == VIR_NET_ERROR) {
+        VIR_INFO("stream aborted at client request");
+        raise_error = (ret < 0);
+    } else {
+        virReportError(VIR_ERR_RPC,
+                       _("stream aborted with unexpected status %d"),
+                       msg->header.status);
+        raise_error = true;
+    }
+
+    if (raise_error) {
+        virNetMessageError rerr;
+        memset(&rerr, 0, sizeof(rerr));
+        return virNetServerProgramSendReplyError(remoteProgram,
+                                                 client,
+                                                 msg,
+                                                 &rerr,
+                                                 &msg->header);
+    } else {
+        /* Send zero-length confirm */
+        return virNetServerProgramSendStreamData(stream->prog,
+                                                 client,
+                                                 msg,
+                                                 stream->procedure,
+                                                 stream->serial,
+                                                 NULL, 0);
+    }
+}
+
+
+static int
+daemonStreamHandleHole(virNetServerClientPtr client,
+                       daemonClientStream *stream,
+                       virNetMessagePtr msg)
+{
+    int ret;
+    virNetStreamHole data;
+
+    VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%u",
+              client, stream, msg->header.proc, msg->header.serial);
+
+    /* Let's check if client plays nicely and advertised usage of
+     * sparse stream upfront. */
+    if (!stream->allowSkip) {
+        virReportError(VIR_ERR_RPC, "%s",
+                       _("Unexpected stream hole"));
+        return -1;
+    }
+
+    if (virNetMessageDecodePayload(msg,
+                                   (xdrproc_t) xdr_virNetStreamHole,
+                                   &data) < 0)
+        return -1;
+
+    ret = virStreamSendHole(stream->st, data.length, data.flags);
+
+    if (ret < 0) {
+        virNetMessageError rerr;
+
+        memset(&rerr, 0, sizeof(rerr));
+
+        VIR_INFO("Stream send hole failed");
+        stream->closed = true;
+        virStreamEventRemoveCallback(stream->st);
+        virStreamAbort(stream->st);
+
+        return virNetServerProgramSendReplyError(stream->prog,
+                                                 client,
+                                                 msg,
+                                                 &rerr,
+                                                 &msg->header);
+    }
+
+    return 0;
+}
+
+
+/*
+ * Called when the stream is signalled has being able to accept
+ * data writes. Will process all pending incoming messages
+ * until they're all gone, or I/O blocks
+ *
+ * Returns 0 on success, or -1 upon fatal error
+ */
+static int
+daemonStreamHandleWrite(virNetServerClientPtr client,
+                        daemonClientStream *stream)
+{
+    VIR_DEBUG("client=%p, stream=%p", client, stream);
+
+    while (stream->rx && !stream->closed) {
+        virNetMessagePtr msg = stream->rx;
+        int ret;
+
+        if (msg->header.type == VIR_NET_STREAM_HOLE) {
+            /* Handle special case when the client sent us a hole.
+             * Otherwise just carry on with processing stream
+             * data. */
+            ret = daemonStreamHandleHole(client, stream, msg);
+        } else if (msg->header.type == VIR_NET_STREAM) {
+            switch (msg->header.status) {
+            case VIR_NET_OK:
+                ret = daemonStreamHandleFinish(client, stream, msg);
+                break;
+
+            case VIR_NET_CONTINUE:
+                ret = daemonStreamHandleWriteData(client, stream, msg);
+                break;
+
+            case VIR_NET_ERROR:
+            default:
+                ret = daemonStreamHandleAbort(client, stream, msg);
+                break;
+            }
+        } else {
+            virReportError(VIR_ERR_RPC,
+                           _("Unexpected message type: %d"),
+                           msg->header.type);
+            ret = -1;
+        }
+
+        if (ret > 0)
+            break;  /* still processing data from msg */
+
+        virNetMessageQueueServe(&stream->rx);
+        if (ret < 0) {
+            virNetMessageFree(msg);
+            virNetServerClientImmediateClose(client);
+            return -1;
+        }
+
+        /* 'CONTINUE' messages don't send a reply (unless error
+         * occurred), so to release the 'msg' object we need to
+         * send a fake zero-length reply. Nothing actually gets
+         * onto the wire, but this causes the client to reset
+         * its active request count / throttling
+         */
+        if (msg->header.status == VIR_NET_CONTINUE) {
+            virNetMessageClear(msg);
+            msg->header.type = VIR_NET_REPLY;
+            if (virNetServerClientSendMessage(client, msg) < 0) {
+                virNetMessageFree(msg);
+                virNetServerClientImmediateClose(client);
+                return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+
+/*
+ * Invoked when a stream is signalled as having data
+ * available to read. This reads up to one message
+ * worth of data, and then queues that for transmission
+ * to the client.
+ *
+ * Returns 0 if data was queued for TX, or an error RPC
+ * was sent, or -1 on fatal error, indicating client should
+ * be killed
+ */
+static int
+daemonStreamHandleRead(virNetServerClientPtr client,
+                       daemonClientStream *stream)
+{
+    virNetMessagePtr msg = NULL;
+    virNetMessageError rerr;
+    char *buffer;
+    size_t bufferLen = VIR_NET_MESSAGE_LEGACY_PAYLOAD_MAX;
+    int ret = -1;
+    int rv;
+    int inData = 0;
+    long long length = 0;
+
+    VIR_DEBUG("client=%p, stream=%p tx=%d closed=%d",
+              client, stream, stream->tx, stream->closed);
+
+    /* We might have had an event pending before we shut
+     * down the stream, so if we're marked as closed,
+     * then do nothing
+     */
+    if (stream->closed)
+        return 0;
+
+    /* Shouldn't ever be called unless we're marked able to
+     * transmit, but doesn't hurt to check */
+    if (!stream->tx)
+        return 0;
+
+    memset(&rerr, 0, sizeof(rerr));
+
+    if (VIR_ALLOC_N(buffer, bufferLen) < 0)
+        return -1;
+
+    if (!(msg = virNetMessageNew(false)))
+        goto cleanup;
+
+    if (stream->allowSkip && stream->dataLen == 0) {
+        /* Handle skip. We want to send some data to the client. But we might
+         * be in a hole. Seek to next data. But if we are in data already, just
+         * carry on. */
+
+        rv = virStreamInData(stream->st, &inData, &length);
+        VIR_DEBUG("rv=%d inData=%d length=%lld", rv, inData, length);
+
+        if (rv < 0) {
+            if (virNetServerProgramSendStreamError(remoteProgram,
+                                                   client,
+                                                   msg,
+                                                   &rerr,
+                                                   stream->procedure,
+                                                   stream->serial) < 0)
+                goto cleanup;
+            msg = NULL;
+
+            /* We're done with this call */
+            goto done;
+        } else {
+            if (!inData && length) {
+                stream->tx = false;
+                msg->cb = daemonStreamMessageFinished;
+                msg->opaque = stream;
+                stream->refs++;
+                if (virNetServerProgramSendStreamHole(remoteProgram,
+                                                      client,
+                                                      msg,
+                                                      stream->procedure,
+                                                      stream->serial,
+                                                      length,
+                                                      0) < 0)
+                    goto cleanup;
+
+                msg = NULL;
+
+                /* We have successfully sent stream skip to the other side.
+                 * To keep streams in sync seek locally too. */
+                virStreamSendHole(stream->st, length, 0);
+                /* We're done with this call */
+                goto done;
+            }
+        }
+
+        stream->dataLen = length;
+    }
+
+    if (stream->allowSkip &&
+        bufferLen > stream->dataLen)
+        bufferLen = stream->dataLen;
+
+    rv = virStreamRecv(stream->st, buffer, bufferLen);
+    if (rv == -2) {
+        /* Should never get this, since we're only called when we know
+         * we're readable, but hey things change... */
+    } else if (rv < 0) {
+        if (virNetServerProgramSendStreamError(remoteProgram,
+                                               client,
+                                               msg,
+                                               &rerr,
+                                               stream->procedure,
+                                               stream->serial) < 0)
+            goto cleanup;
+        msg = NULL;
+    } else {
+        if (stream->allowSkip)
+            stream->dataLen -= rv;
+
+        stream->tx = false;
+        if (rv == 0)
+            stream->recvEOF = true;
+
+        msg->cb = daemonStreamMessageFinished;
+        msg->opaque = stream;
+        stream->refs++;
+        if (virNetServerProgramSendStreamData(remoteProgram,
+                                              client,
+                                              msg,
+                                              stream->procedure,
+                                              stream->serial,
+                                              buffer, rv) < 0)
+            goto cleanup;
+        msg = NULL;
+    }
+
+ done:
+    ret = 0;
+ cleanup:
+    VIR_FREE(buffer);
+    virNetMessageFree(msg);
+    return ret;
+}
diff --git a/src/remote/remote_daemon_stream.h b/src/remote/remote_daemon_stream.h
new file mode 100644 (file)
index 0000000..832e1f7
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * remote_daemon_stream.h: APIs for managing client streams
+ *
+ * Copyright (C) 2009-2018 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#ifndef __REMOTE_DAEMON_STREAM_H__
+# define __REMOTE_DAEMON_STREAM_H__
+
+# include "remote_daemon.h"
+
+daemonClientStream *
+daemonCreateClientStream(virNetServerClientPtr client,
+                         virStreamPtr st,
+                         virNetServerProgramPtr prog,
+                         virNetMessageHeaderPtr hdr,
+                         bool allowSkip);
+
+int daemonFreeClientStream(virNetServerClientPtr client,
+                           daemonClientStream *stream);
+
+int daemonAddClientStream(virNetServerClientPtr client,
+                          daemonClientStream *stream,
+                          bool transmit);
+
+int
+daemonRemoveClientStream(virNetServerClientPtr client,
+                         daemonClientStream *stream);
+
+void
+daemonRemoveAllClientStreams(daemonClientStream *stream);
+
+#endif /* __REMOTE_DAEMON_STREAM_H__ */