]> xenbits.xensource.com Git - people/jgross/xen.git/commitdiff
tools/libxl: move libxenlight to tools/libs/light
authorJuergen Gross <jgross@suse.com>
Wed, 23 Sep 2020 04:57:20 +0000 (06:57 +0200)
committerJuergen Gross <jgross@suse.com>
Thu, 1 Oct 2020 11:26:09 +0000 (13:26 +0200)
Carve out all libxenlight related sources and move them to
tools/libs/light in order to use the generic library build environment.

The closely related sources for libxl-save-helper and the libxl test
environment are being moved, too.

Signed-off-by: Juergen Gross <jgross@suse.com>
Acked-by: Wei Liu <wl@xen.org>
211 files changed:
.gitignore
tools/Rules.mk
tools/configure
tools/configure.ac
tools/golang/xenlight/Makefile
tools/libs/Makefile
tools/libs/light/CODING_STYLE [new file with mode: 0644]
tools/libs/light/Makefile [new file with mode: 0644]
tools/libs/light/check-libxl-api-rules [new file with mode: 0755]
tools/libs/light/flexarray.c [new file with mode: 0644]
tools/libs/light/flexarray.h [new file with mode: 0644]
tools/libs/light/gentest.py [new file with mode: 0644]
tools/libs/light/gentypes.py [new file with mode: 0644]
tools/libs/light/idl.py [new file with mode: 0644]
tools/libs/light/idl.txt [new file with mode: 0644]
tools/libs/light/include/libxl.h [new file with mode: 0644]
tools/libs/light/include/libxl_event.h [new file with mode: 0644]
tools/libs/light/include/libxl_json.h [new file with mode: 0644]
tools/libs/light/include/libxl_utils.h [new file with mode: 0644]
tools/libs/light/include/libxl_uuid.h [new file with mode: 0644]
tools/libs/light/libxl.c [new file with mode: 0644]
tools/libs/light/libxl_9pfs.c [new file with mode: 0644]
tools/libs/light/libxl_aoutils.c [new file with mode: 0644]
tools/libs/light/libxl_arch.h [new file with mode: 0644]
tools/libs/light/libxl_arm.c [new file with mode: 0644]
tools/libs/light/libxl_arm.h [new file with mode: 0644]
tools/libs/light/libxl_arm_acpi.c [new file with mode: 0644]
tools/libs/light/libxl_arm_no_acpi.c [new file with mode: 0644]
tools/libs/light/libxl_bootloader.c [new file with mode: 0644]
tools/libs/light/libxl_checkpoint_device.c [new file with mode: 0644]
tools/libs/light/libxl_colo.h [new file with mode: 0644]
tools/libs/light/libxl_colo_nic.c [new file with mode: 0644]
tools/libs/light/libxl_colo_proxy.c [new file with mode: 0644]
tools/libs/light/libxl_colo_qdisk.c [new file with mode: 0644]
tools/libs/light/libxl_colo_restore.c [new file with mode: 0644]
tools/libs/light/libxl_colo_save.c [new file with mode: 0644]
tools/libs/light/libxl_console.c [new file with mode: 0644]
tools/libs/light/libxl_convert_callout.c [new file with mode: 0644]
tools/libs/light/libxl_cpuid.c [new file with mode: 0644]
tools/libs/light/libxl_cpupool.c [new file with mode: 0644]
tools/libs/light/libxl_create.c [new file with mode: 0644]
tools/libs/light/libxl_device.c [new file with mode: 0644]
tools/libs/light/libxl_disk.c [new file with mode: 0644]
tools/libs/light/libxl_dm.c [new file with mode: 0644]
tools/libs/light/libxl_dom.c [new file with mode: 0644]
tools/libs/light/libxl_dom_save.c [new file with mode: 0644]
tools/libs/light/libxl_dom_suspend.c [new file with mode: 0644]
tools/libs/light/libxl_domain.c [new file with mode: 0644]
tools/libs/light/libxl_event.c [new file with mode: 0644]
tools/libs/light/libxl_exec.c [new file with mode: 0644]
tools/libs/light/libxl_flask.c [new file with mode: 0644]
tools/libs/light/libxl_fork.c [new file with mode: 0644]
tools/libs/light/libxl_freebsd.c [new file with mode: 0644]
tools/libs/light/libxl_genid.c [new file with mode: 0644]
tools/libs/light/libxl_internal.c [new file with mode: 0644]
tools/libs/light/libxl_internal.h [new file with mode: 0644]
tools/libs/light/libxl_json.c [new file with mode: 0644]
tools/libs/light/libxl_libfdt_compat.c [new file with mode: 0644]
tools/libs/light/libxl_libfdt_compat.h [new file with mode: 0644]
tools/libs/light/libxl_linux.c [new file with mode: 0644]
tools/libs/light/libxl_mem.c [new file with mode: 0644]
tools/libs/light/libxl_netbsd.c [new file with mode: 0644]
tools/libs/light/libxl_netbuffer.c [new file with mode: 0644]
tools/libs/light/libxl_nic.c [new file with mode: 0644]
tools/libs/light/libxl_no_colo.c [new file with mode: 0644]
tools/libs/light/libxl_no_convert_callout.c [new file with mode: 0644]
tools/libs/light/libxl_nocpuid.c [new file with mode: 0644]
tools/libs/light/libxl_nonetbuffer.c [new file with mode: 0644]
tools/libs/light/libxl_numa.c [new file with mode: 0644]
tools/libs/light/libxl_osdeps.h [new file with mode: 0644]
tools/libs/light/libxl_paths.c [new file with mode: 0644]
tools/libs/light/libxl_pci.c [new file with mode: 0644]
tools/libs/light/libxl_psr.c [new file with mode: 0644]
tools/libs/light/libxl_pvcalls.c [new file with mode: 0644]
tools/libs/light/libxl_qmp.c [new file with mode: 0644]
tools/libs/light/libxl_remus.c [new file with mode: 0644]
tools/libs/light/libxl_remus_disk_drbd.c [new file with mode: 0644]
tools/libs/light/libxl_save_callout.c [new file with mode: 0644]
tools/libs/light/libxl_save_helper.c [new file with mode: 0644]
tools/libs/light/libxl_save_msgs_gen.pl [new file with mode: 0755]
tools/libs/light/libxl_sched.c [new file with mode: 0644]
tools/libs/light/libxl_sr_stream_format.h [new file with mode: 0644]
tools/libs/light/libxl_stream_read.c [new file with mode: 0644]
tools/libs/light/libxl_stream_write.c [new file with mode: 0644]
tools/libs/light/libxl_test_fdevent.c [new file with mode: 0644]
tools/libs/light/libxl_test_fdevent.h [new file with mode: 0644]
tools/libs/light/libxl_test_timedereg.c [new file with mode: 0644]
tools/libs/light/libxl_test_timedereg.h [new file with mode: 0644]
tools/libs/light/libxl_tmem.c [new file with mode: 0644]
tools/libs/light/libxl_types.idl [new file with mode: 0644]
tools/libs/light/libxl_types_internal.idl [new file with mode: 0644]
tools/libs/light/libxl_usb.c [new file with mode: 0644]
tools/libs/light/libxl_utils.c [new file with mode: 0644]
tools/libs/light/libxl_uuid.c [new file with mode: 0644]
tools/libs/light/libxl_vdispl.c [new file with mode: 0644]
tools/libs/light/libxl_vkb.c [new file with mode: 0644]
tools/libs/light/libxl_vnuma.c [new file with mode: 0644]
tools/libs/light/libxl_vsnd.c [new file with mode: 0644]
tools/libs/light/libxl_vtpm.c [new file with mode: 0644]
tools/libs/light/libxl_x86.c [new file with mode: 0644]
tools/libs/light/libxl_x86_acpi.c [new file with mode: 0644]
tools/libs/light/libxl_x86_acpi.h [new file with mode: 0644]
tools/libs/light/libxl_xshelp.c [new file with mode: 0644]
tools/libs/light/osdeps.c [new file with mode: 0644]
tools/libs/light/test_common.c [new file with mode: 0644]
tools/libs/light/test_common.h [new file with mode: 0644]
tools/libs/light/test_fdderegrace.c [new file with mode: 0644]
tools/libs/light/test_timedereg.c [new file with mode: 0644]
tools/libs/uselibs.mk
tools/libxl/Makefile
tools/libxl/check-libxl-api-rules [deleted file]
tools/libxl/flexarray.c [deleted file]
tools/libxl/flexarray.h [deleted file]
tools/libxl/gentest.py [deleted file]
tools/libxl/gentypes.py [deleted file]
tools/libxl/idl.py [deleted file]
tools/libxl/idl.txt [deleted file]
tools/libxl/libxl.c [deleted file]
tools/libxl/libxl.h [deleted file]
tools/libxl/libxl_9pfs.c [deleted file]
tools/libxl/libxl_aoutils.c [deleted file]
tools/libxl/libxl_arch.h [deleted file]
tools/libxl/libxl_arm.c [deleted file]
tools/libxl/libxl_arm.h [deleted file]
tools/libxl/libxl_arm_acpi.c [deleted file]
tools/libxl/libxl_arm_no_acpi.c [deleted file]
tools/libxl/libxl_bootloader.c [deleted file]
tools/libxl/libxl_checkpoint_device.c [deleted file]
tools/libxl/libxl_colo.h [deleted file]
tools/libxl/libxl_colo_nic.c [deleted file]
tools/libxl/libxl_colo_proxy.c [deleted file]
tools/libxl/libxl_colo_qdisk.c [deleted file]
tools/libxl/libxl_colo_restore.c [deleted file]
tools/libxl/libxl_colo_save.c [deleted file]
tools/libxl/libxl_console.c [deleted file]
tools/libxl/libxl_convert_callout.c [deleted file]
tools/libxl/libxl_cpuid.c [deleted file]
tools/libxl/libxl_cpupool.c [deleted file]
tools/libxl/libxl_create.c [deleted file]
tools/libxl/libxl_device.c [deleted file]
tools/libxl/libxl_disk.c [deleted file]
tools/libxl/libxl_dm.c [deleted file]
tools/libxl/libxl_dom.c [deleted file]
tools/libxl/libxl_dom_save.c [deleted file]
tools/libxl/libxl_dom_suspend.c [deleted file]
tools/libxl/libxl_domain.c [deleted file]
tools/libxl/libxl_event.c [deleted file]
tools/libxl/libxl_event.h [deleted file]
tools/libxl/libxl_exec.c [deleted file]
tools/libxl/libxl_flask.c [deleted file]
tools/libxl/libxl_fork.c [deleted file]
tools/libxl/libxl_freebsd.c [deleted file]
tools/libxl/libxl_genid.c [deleted file]
tools/libxl/libxl_internal.c [deleted file]
tools/libxl/libxl_internal.h [deleted file]
tools/libxl/libxl_json.c [deleted file]
tools/libxl/libxl_json.h [deleted file]
tools/libxl/libxl_libfdt_compat.c [deleted file]
tools/libxl/libxl_libfdt_compat.h [deleted file]
tools/libxl/libxl_linux.c [deleted file]
tools/libxl/libxl_mem.c [deleted file]
tools/libxl/libxl_netbsd.c [deleted file]
tools/libxl/libxl_netbuffer.c [deleted file]
tools/libxl/libxl_nic.c [deleted file]
tools/libxl/libxl_no_colo.c [deleted file]
tools/libxl/libxl_no_convert_callout.c [deleted file]
tools/libxl/libxl_nocpuid.c [deleted file]
tools/libxl/libxl_nonetbuffer.c [deleted file]
tools/libxl/libxl_numa.c [deleted file]
tools/libxl/libxl_osdeps.h [deleted file]
tools/libxl/libxl_paths.c [deleted file]
tools/libxl/libxl_pci.c [deleted file]
tools/libxl/libxl_psr.c [deleted file]
tools/libxl/libxl_pvcalls.c [deleted file]
tools/libxl/libxl_qmp.c [deleted file]
tools/libxl/libxl_remus.c [deleted file]
tools/libxl/libxl_remus_disk_drbd.c [deleted file]
tools/libxl/libxl_save_callout.c [deleted file]
tools/libxl/libxl_save_helper.c [deleted file]
tools/libxl/libxl_save_msgs_gen.pl [deleted file]
tools/libxl/libxl_sched.c [deleted file]
tools/libxl/libxl_sr_stream_format.h [deleted file]
tools/libxl/libxl_stream_read.c [deleted file]
tools/libxl/libxl_stream_write.c [deleted file]
tools/libxl/libxl_test_fdevent.c [deleted file]
tools/libxl/libxl_test_fdevent.h [deleted file]
tools/libxl/libxl_test_timedereg.c [deleted file]
tools/libxl/libxl_test_timedereg.h [deleted file]
tools/libxl/libxl_tmem.c [deleted file]
tools/libxl/libxl_types.idl [deleted file]
tools/libxl/libxl_types_internal.idl [deleted file]
tools/libxl/libxl_usb.c [deleted file]
tools/libxl/libxl_utils.c [deleted file]
tools/libxl/libxl_utils.h [deleted file]
tools/libxl/libxl_uuid.c [deleted file]
tools/libxl/libxl_uuid.h [deleted file]
tools/libxl/libxl_vdispl.c [deleted file]
tools/libxl/libxl_vkb.c [deleted file]
tools/libxl/libxl_vnuma.c [deleted file]
tools/libxl/libxl_vsnd.c [deleted file]
tools/libxl/libxl_vtpm.c [deleted file]
tools/libxl/libxl_x86.c [deleted file]
tools/libxl/libxl_x86_acpi.c [deleted file]
tools/libxl/libxl_x86_acpi.h [deleted file]
tools/libxl/libxl_xshelp.c [deleted file]
tools/libxl/osdeps.c [deleted file]
tools/libxl/test_common.c [deleted file]
tools/libxl/test_common.h [deleted file]
tools/libxl/test_fdderegrace.c [deleted file]
tools/libxl/test_timedereg.c [deleted file]
tools/ocaml/libs/xl/Makefile

index 5e8c47e2db2dc68a606e97331ef0752355e6bd35..f30550255f5dc166d694e458fde52dfceb80fde6 100644 (file)
@@ -128,6 +128,22 @@ tools/libs/guest/xc_core.h
 tools/libs/guest/xc_core_arm.h
 tools/libs/guest/xc_core_x86.h
 tools/libs/guest/xc_private.h
+tools/libs/light/_*.[ch]
+tools/libs/light/*.pyc
+tools/libs/light/_libxl.api-for-check
+tools/libs/light/*.api-ok
+tools/libs/light/libxenlight.map
+tools/libs/light/libxl-save-helper
+tools/libs/light/dsdt*
+tools/libs/light/mk_dsdt
+tools/libs/light/ssdt*
+tools/libs/light/testidl
+tools/libs/light/testidl.c
+tools/libs/light/test_timedereg
+tools/libs/light/test_fdderegrace
+tools/libs/light/tmp.*
+tools/libs/light/xenlight.pc
+tools/libs/light/include/_*.h
 tools/libs/stat/_paths.h
 tools/libs/stat/headers.chk
 tools/libs/stat/libxenstat.map
@@ -216,16 +232,8 @@ tools/include/xen/*
 tools/include/xen-xsm/*
 tools/include/xen-foreign/*.(c|h|size)
 tools/include/xen-foreign/checker
-tools/libxl/_libxl.api-for-check
-tools/libxl/*.api-ok
 tools/libxl/*.pc
-tools/libxl/dsdt*
 tools/libxl/libxlu_cfg_y.output
-tools/libxl/mk_dsdt
-tools/libxl/ssdt*
-tools/libxl/testenum
-tools/libxl/testenum.c
-tools/libxl/tmp.*
 tools/misc/cpuperf/cpuperf-perfcntr
 tools/misc/cpuperf/cpuperf-xen
 tools/misc/xc_shadow
@@ -380,13 +388,6 @@ tools/include/xen-foreign/arm64.h
 tools/misc/xen-hptool
 tools/misc/xen-mfndump
 tools/libs/toolcore/include/_*.h
-tools/libxl/_*.[ch]
-tools/libxl/testidl
-tools/libxl/testidl.c
-tools/libxl/*.pyc
-tools/libxl/libxl-save-helper
-tools/libxl/test_timedereg
-tools/libxl/test_fdderegrace
 tools/firmware/etherboot/eb-roms.h
 tools/firmware/etherboot/gpxe-git-snapshot.tar.gz
 tools/misc/xenhypfs
index a71abb2e4f97a62655ac532a890365ce81747764..a68dbb9de8c98fa3440f39d6b5ff4341fa3f5676 100644 (file)
@@ -15,9 +15,7 @@ XEN_INCLUDE        = $(XEN_ROOT)/tools/include
 
 include $(XEN_ROOT)/tools/libs/uselibs.mk
 
-XEN_libxenlight    = $(XEN_ROOT)/tools/libxl
-# Currently libxlutil lives in the same directory as libxenlight
-XEN_libxlutil      = $(XEN_libxenlight)
+XEN_libxlutil      = $(XEN_ROOT)/tools/libxl
 
 CFLAGS_xeninclude = -I$(XEN_INCLUDE)
 
@@ -107,6 +105,8 @@ ifeq ($(CONFIG_Linux),y)
 LDLIBS_libxenstore += -ldl
 endif
 
+CFLAGS_libxenlight += $(CFLAGS_libxenctrl)
+
 ifeq ($(debug),y)
 # Disable optimizations
 CFLAGS += -O0 -fno-omit-frame-pointer
@@ -116,11 +116,6 @@ else
 CFLAGS += -O2 -fomit-frame-pointer
 endif
 
-CFLAGS_libxenlight = -I$(XEN_libxenlight) $(CFLAGS_libxenctrl) $(CFLAGS_xeninclude)
-SHDEPS_libxenlight = $(SHLIB_libxenctrl) $(SHLIB_libxenstore) $(SHLIB_libxenhypfs) $(SHLIB_libxenguest)
-LDLIBS_libxenlight = $(SHDEPS_libxenlight) $(XEN_libxenlight)/libxenlight$(libextension)
-SHLIB_libxenlight  = $(SHDEPS_libxenlight) -Wl,-rpath-link=$(XEN_libxenlight)
-
 CFLAGS_libxlutil = -I$(XEN_libxlutil)
 SHDEPS_libxlutil = $(SHLIB_libxenlight)
 LDLIBS_libxlutil = $(SHDEPS_libxlutil) $(XEN_libxlutil)/libxlutil$(libextension)
index edcdcf4f73f98e2fd12ccfaecbaf6123a9a7b781..8a708e9baa85a5ff8336a4dacb897453cb43c358 100755 (executable)
@@ -585,7 +585,7 @@ PACKAGE_STRING='Xen Hypervisor Tools 4.15'
 PACKAGE_BUGREPORT='xen-devel@lists.xen.org'
 PACKAGE_URL='https://www.xen.org/'
 
-ac_unique_file="libxl/libxl.c"
+ac_unique_file="libs/light/libxl.c"
 # Factoring default headers for most tests.
 ac_includes_default="\
 #include <stdio.h>
index 6614a4f130ff6a11d7b23889f1a31dabf2feccaa..ee8ba5ff24acc4c4e26bae18cc0e7a25ae9e868d 100644 (file)
@@ -4,7 +4,7 @@
 AC_PREREQ([2.67])
 AC_INIT([Xen Hypervisor Tools], m4_esyscmd([../version.sh ../xen/Makefile]),
     [xen-devel@lists.xen.org], [xen], [https://www.xen.org/])
-AC_CONFIG_SRCDIR([libxl/libxl.c])
+AC_CONFIG_SRCDIR([libs/light/libxl.c])
 AC_CONFIG_FILES([
 ../config/Tools.mk
 hotplug/FreeBSD/rc.d/xencommons
index b17095e64bc9d63adb343d56e58aedba8dc02c23..fd8e4893dbdbe31ef5231f318e837ba890404644 100644 (file)
@@ -8,7 +8,7 @@ GOXL_INSTALL_DIR = $(GOCODE_DIR)/src/$(XEN_GOCODE_URL)/xenlight/
 
 GO ?= go
 
-LIBXL_SRC_DIR = ../../libxl
+LIBXL_SRC_DIR = $(XEN_ROOT)/tools/libs/light
 
 .PHONY: all
 all: build
index e8fcd592148384cf7e234b385693e1591a75cab8..c41455c604f1202679d73692766892180fccd7c7 100644 (file)
@@ -15,6 +15,7 @@ SUBDIRS-y += hypfs
 SUBDIRS-y += store
 SUBDIRS-y += stat
 SUBDIRS-$(CONFIG_Linux) += vchan
+SUBDIRS-y += light
 
 ifeq ($(CONFIG_RUMP),y)
 SUBDIRS-y := toolcore
diff --git a/tools/libs/light/CODING_STYLE b/tools/libs/light/CODING_STYLE
new file mode 100644 (file)
index 0000000..3d572f6
--- /dev/null
@@ -0,0 +1,330 @@
+LIBXENLIGHT CODING STYLE
+========================
+
+
+AN APOLOGY AND WARNING
+----------------------
+
+Much of the code in libxl does not yet follow this coding style
+document in every respect.  However, new code is expected to conform.
+
+Patches to improve the style of existing code are welcome.  Please
+separate these out from functional changes.
+
+If it is not feasible to conform fully to the style while patching old
+code, without doing substantial style reengineering first, we may
+accept patches which contain nonconformant elements, provided that
+they don't make the coding style problem worse overall.
+
+In this case, the new code should conform to the prevailing style in
+the area being touched.
+
+
+MEMORY ALLOCATION
+-----------------
+
+Memory allocation for libxl-internal purposes should normally be done
+with the provided gc mechanisms; there is then no need to free.  See
+"libxl memory management" in libxl.h.
+
+
+CONVENTIONAL VARIABLE NAMES
+---------------------------
+
+The following local variable names should be used where applicable:
+
+  int rc;    /* a libxl error code - and not anything else */
+  int r;     /* the return value from a system call (or libxc call) */
+  bool ok;   /* the success return value from a boolean function */
+
+  uint32_t domid;
+  libxl__gc *gc;
+  libxl__egc *egc;
+  libxl__ao *ao;
+
+  libxl_foo_bar_state *fbs;    /* local variable */
+  libxl_foo_bar_state foo_bar; /* inside another state struct */
+
+
+CONVENIENCE MACROS
+------------------
+
+There are a number of convenience macros which shorten the program and
+avoid opportunity for mistakes.  In some cases non-use of the macros
+produces functional bugs or incorrect error handling.  Use the macros
+whenever they are applicable.  For example:
+
+ Usually, don't use:     Instead, use (see libxl_internal.h):
+  libxl__log[v]           LOG, LOGE, LOGEV
+  libxl__sprintf          GCSPRINTF
+  libxl__*alloc et al.    GCNEW, GCNEW_ARRAY, GCREALLOC_ARRAY
+  isalnum etc. directly   CTYPE
+  libxl__ctx_[un]lock     CTX_LOCK, CTX_UNLOCK
+  gc=...; ao=...;         EGC_GC, AO_GC, STATE_AO_GC
+  explicit gc creation    GC_INIT, GC_FREE
+  memset(..,0,sizeof..)   FILLZERO
+
+Instead of malloc et al one should (as an exception to the above) use
+libxl__{zalloc,calloc,realloc} etc but passing NOGC.
+
+ERROR HANDLING
+--------------
+
+Unless, there are good reasons to do otherwise, the following error
+handling and cleanup paradigm should be used:
+
+  * All local variables referring to resources which might need
+    cleaning up are declared at the top of the function, and
+    initialised to a sentinel value indicating "nothing allocated".
+    For example,
+            libxl_evgen_disk_eject *evg = NULL;
+            int nullfd = -1;
+
+  * If the function is to return a libxl error value, `rc' is
+    used to contain the error code, but it is NOT initialised:
+            int rc;
+
+  * There is only one error cleanup path out of the function.  It
+    starts with a label `out:'.  That error cleanup path checks for
+    each allocated resource and frees it iff necessary.  It then
+    returns rc.  For example,
+         out:
+             if (evg) libxl__evdisable_disk_eject(gc, evg);
+             if (nullfd >= 0) close(nullfd);
+             return rc;
+
+  * Function calls which might fail (ie most function calls) are
+    handled by putting the return/status value into a variable, and
+    then checking it in a separate statement:
+            char *dompath = libxl__xs_get_dompath(gc, bl->domid);
+            if (!dompath) { rc = ERROR_FAIL; goto out; }
+
+  * If a resource is freed in the main body of the function (for
+    example, in a loop), the corresponding variable has to be reset to
+    the sentinel at the point where it's freed.
+
+Whether to use the `out' path for successful returns as well as error
+returns is a matter of taste and convenience for the specific
+function.  Not reusing the out path is fine if the duplicated function
+exit code is only `CTX_UNLOCK; GC_FREE;' (or similar).
+
+If you reuse the `out' path for successful returns, there may be
+resources which are to be returned to the caller rather than freed.
+In that case you have to reset the local variable to `nothing here',
+to avoid the resource being freed on the out path.  That resetting
+should be done immediately after the resource value is stored at the
+applicable _r function parameter (or equivalent).  Do not test `rc' in
+the out section, to discover whether to free things.
+
+The uses of the single-line formatting in the examples above are
+permitted exceptions to the usual libxl code formatting rules.
+
+
+
+IDEMPOTENT DATA STRUCTURE CONSTRUCTION/DESTRUCTION
+--------------------------------------------------
+
+Nontrivial data structures (in structs) should come with an idempotent
+_dispose function, which must free all resources associated with the
+data structure (but not free the struct itself).
+
+Such a struct should also come with an _init function which
+initialises the struct so that _dispose is a no-op.
+
+
+ASYNCHRONOUS/LONG-RUNNING OPERATIONS
+------------------------------------
+
+All long-running operations in libxl need to use the asynchronous
+operation machinery.  Consult the programmer documentation in
+libxl_internal.h for details - search for "Machinery for asynchronous
+operations".
+
+The code for asynchronous operations should be laid out in
+chronological order.  That is, where there is a chain of callback
+functions, each subsequent function should be, textually, the next
+function in the file.  This will normally involve predeclaring the
+callback functions.  Synchronous helper functions should be separated
+out into a section preceding the main callback chain.
+
+Control flow arrangements in asynchronous operations should be made as
+simple as possible, because it can otherwise be very hard to see
+through the tangle.
+
+
+When inventing a new sub-operation in asynchronous code, consider
+whether to structure it formally as a sub-operation with its own state
+structure.  (See, for example, libxl__datacopier_*.)
+
+An ao-suboperation state structure should contain, in this order:
+  * fields that the caller must fill in, and which are,
+    effectively, the parameters to the operation, including:
+      - libxl__ao *ao
+      - the callback function pointer(s), which
+        should be named callback or callback_*.
+  * shared information fields or ones used for returning information
+    to the calling operation
+  * private fields
+These sections should be clearly demarcated by comments.
+
+An asynchronous operation should normally have an idempotent stop or
+cancel function.  It should normally also have an _init function for
+its state struct, which arranges that the stop is a no-op.
+
+The permitted order of calls into your ao operation's methods must be
+documented in comments, if it is nontrivial.
+
+
+When using an ao sub-operation, you should normally:
+ * Physically include the sub-operation state struct in your
+   own state struct;
+ * Use CONTAINER_OF to find your own state struct at the start of
+   your implementations of the sub-operation callback functions;
+ * Unconditionally initialise the sub-operation's struct (with its
+   _init method) in your own _init method.
+ * Unconditionally cancel or destroy the sub-operation in your own
+   cancel or destroy method.
+
+
+FORMATTING AND NAMING
+---------------------
+
+Blatantly copied from qemu and linux with few modifications.
+
+
+1. Whitespace
+
+Of course, the most important aspect in any coding style is whitespace.
+Crusty old coders who have trouble spotting the glasses on their noses
+can tell the difference between a tab and eight spaces from a distance
+of approximately fifteen parsecs.  Many a flamewar have been fought and
+lost on this issue.
+
+Libxenlight indents are four spaces.  Tabs are never used, except in
+Makefiles where they have been irreversibly coded into the syntax.
+Spaces of course are superior to tabs because:
+
+ - You have just one way to specify whitespace, not two.  Ambiguity breeds
+   mistakes.
+ - The confusion surrounding 'use tabs to indent, spaces to justify' is gone.
+ - Tab indents push your code to the right, making your screen seriously
+   unbalanced.
+ - Tabs will be rendered incorrectly on editors who are misconfigured not
+   to use tab stops of eight positions.
+ - Tabs are rendered badly in patches, causing off-by-one errors in almost
+   every line.
+ - It is the libxenlight coding style.
+
+Do not leave whitespace dangling off the ends of lines.
+
+
+2. Line width
+
+Lines are limited to 75 characters.
+
+Rationale:
+ - Some people like to tile their 24" screens with a 6x4 matrix of 80x24
+   xterms and use vi in all of them.  The best way to punish them is to
+   let them keep doing it.
+ - In an 80 column terminal, some room needs to be left for > quoting
+   characters, +/- diff characters, and so on, in emails.
+ - Code and especially patches is much more readable if limited to a sane
+   line length.  Eighty is traditional.
+ - It is the libxenlight coding style.
+
+
+3. Naming
+
+C is a Spartan language, and so should your naming be.  Unlike Modula-2
+and Pascal programmers, C programmers do not use cute names like
+ThisVariableIsATemporaryCounter.  A C programmer would call that
+variable "tmp", which is much easier to write, and not the least more
+difficult to understand.
+
+HOWEVER, while mixed-case names are frowned upon, descriptive names for
+global variables are a must.  To call a global function "foo" is a
+shooting offense.
+
+GLOBAL variables (to be used only if you _really_ need them) need to
+have descriptive names, as do global functions.  If you have a function
+that counts the number of active users, you should call that
+"count_active_users()" or similar, you should _not_ call it "cntusr()".
+
+Encoding the type of a function into the name (so-called Hungarian
+notation) is brain damaged - the compiler knows the types anyway and can
+check those, and it only confuses the programmer.
+
+LOCAL variable names should be short, and to the point.  If you have
+some random integer loop counter, it should probably be called "i".
+Calling it "loop_counter" is non-productive, if there is no chance of it
+being mis-understood.  Similarly, "tmp" can be just about any type of
+variable that is used to hold a temporary value.
+
+Local variables used to store return values should have descriptive name
+like "rc" or "ret". Following the same reasoning the label used as exit
+path should be called "out".
+
+Function arguments which are used to return values to the caller
+should be suffixed `_r' or `_out'.
+
+Variables, type names and function names are
+lower_case_with_underscores.
+Type names and function names use the prefix libxl__ when internal to
+libxenlight and libxl_ when exported in libxl.h.
+Xl should avoid using libxl_ and libxl__ as prefix for its own function
+names.
+
+When wrapping standard library functions, use the prefix libxl_ to alert
+readers that they are seeing a wrapped version; otherwise avoid this prefix.
+
+Typedefs are used to eliminate the redundant 'struct' keyword.
+It is the libxenlight coding style.
+
+
+4. Statements
+
+Don't put multiple statements on a single line.
+Don't put multiple assignments on a single line either.
+Error code paths with an if statement and a goto or a return on the same
+line are allowed. Examples:
+
+    if (rc) goto out;
+    if (rc < 0) return;
+
+Libxenlight coding style is super simple.  Avoid tricky expressions.
+
+
+5. Block structure
+
+Every indented statement is braced, but blocks that contain just one
+statement may have the braces omitted.  To avoid confusion, either all
+the blocks in an if...else chain have braces, or none of them do.
+
+The opening brace is on the line that contains the control flow
+statement that introduces the new block; the closing brace is on the
+same line as the else keyword, or on a line by itself if there is no
+else keyword.  Examples:
+
+    if (a == 5) {
+        printf("a was 5.\n");
+    } else if (a == 6) {
+        printf("a was 6.\n");
+    } else {
+        printf("a was something else entirely.\n");
+    }
+
+    if (a == 5)
+        printf("a was 5.\n");
+
+An exception is the opening brace for a function; for reasons of tradition
+and clarity it comes on a line by itself:
+
+    void a_function(void)
+    {
+        do_something();
+    }
+
+Rationale: a consistent (except for functions...) bracing style reduces
+ambiguity and avoids needless churn when lines are added or removed.
+Furthermore, it is the libxenlight coding style.
+
diff --git a/tools/libs/light/Makefile b/tools/libs/light/Makefile
new file mode 100644 (file)
index 0000000..f58a321
--- /dev/null
@@ -0,0 +1,277 @@
+XEN_ROOT = $(CURDIR)/../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+SRCS-y += osdeps.c
+SRCS-y += libxl_paths.c
+SRCS-y += libxl_bootloader.c
+SRCS-y += flexarray.c
+ifeq ($(CONFIG_LIBNL),y)
+SRCS-y += libxl_netbuffer.c
+else
+SRCS-y += libxl_nonetbuffer.c
+endif
+ifeq ($(CONFIG_X86),y)
+SRCS-y += libxl_convert_callout.c
+else
+SRCS-y += libxl_no_convert_callout.c
+endif
+SRCS-y += libxl_remus.c
+SRCS-y += libxl_checkpoint_device.c
+SRCS-y += libxl_remus_disk_drbd.c
+ifeq ($(CONFIG_LIBNL),y)
+SRCS-y += libxl_colo_restore.c
+SRCS-y += libxl_colo_save.c
+SRCS-y += libxl_colo_qdisk.c
+SRCS-y += libxl_colo_proxy.c
+SRCS-y += libxl_colo_nic.c
+else
+SRCS-y += libxl_no_colo.c
+endif
+
+ACPI_PATH  = $(XEN_ROOT)/tools/libacpi
+DSDT_FILES-$(CONFIG_X86) = dsdt_pvh.c
+ACPI_OBJS  = $(patsubst %.c,%.o,$(DSDT_FILES-y)) build.o static_tables.o
+ACPI_PIC_OBJS = $(patsubst %.o,%.opic,$(ACPI_OBJS))
+$(DSDT_FILES-y): acpi
+vpath build.c $(ACPI_PATH)/
+vpath static_tables.c $(ACPI_PATH)/
+
+.PHONY: acpi
+acpi:
+       $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
+
+SRCS-$(CONFIG_X86) += $(ACPI_OBJS:.o=.c)
+
+CFLAGS += -Wno-format-zero-length -Wmissing-declarations \
+       -Wno-declaration-after-statement -Wformat-nonliteral
+CFLAGS += -I.
+
+SRCS-$(CONFIG_X86) += libxl_cpuid.c
+SRCS-$(CONFIG_X86) += libxl_x86.c
+SRCS-$(CONFIG_X86) += libxl_psr.c
+SRCS-$(CONFIG_X86) += libxl_x86_acpi.c
+SRCS-$(CONFIG_ARM) += libxl_nocpuid.c
+SRCS-$(CONFIG_ARM) += libxl_arm.c
+SRCS-$(CONFIG_ARM) += libxl_libfdt_compat.c
+ifeq ($(CONFIG_ARM_64),y)
+DSDT_FILES-y = dsdt_anycpu_arm.c
+SRCS-y += libxl_arm_acpi.c
+SRCS-y += $(DSDT_FILES-y)
+dsdt_anycpu_arm.c:
+       $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
+else
+SRCS-$(CONFIG_ARM) += libxl_arm_no_acpi.c
+endif
+
+SRCS-OS-$(CONFIG_NetBSD) = libxl_netbsd.c
+SRCS-OS-$(CONFIG_Linux) = libxl_linux.c
+SRCS-OS-$(CONFIG_FreeBSD) = libxl_freebsd.c
+ifeq ($(SRCS-OS-y),)
+$(error Your Operating System is not supported by libxenlight, \
+please check libxl_linux.c and libxl_netbsd.c to see how to get it ported)
+endif
+SRCS-y += $(SRCS-OS-y)
+
+SRCS-y += libxl.c
+SRCS-y += libxl_create.c
+SRCS-y += libxl_dm.c
+SRCS-y += libxl_pci.c
+SRCS-y += libxl_dom.c
+SRCS-y += libxl_exec.c
+SRCS-y += libxl_xshelp.c
+SRCS-y += libxl_device.c
+SRCS-y += libxl_internal.c
+SRCS-y += libxl_utils.c
+SRCS-y += libxl_uuid.c
+SRCS-y += libxl_json.c
+SRCS-y += libxl_aoutils.c
+SRCS-y += libxl_numa.c
+SRCS-y += libxl_vnuma.c
+SRCS-y += libxl_stream_read.c
+SRCS-y += libxl_stream_write.c
+SRCS-y += libxl_save_callout.c
+SRCS-y += _libxl_save_msgs_callout.c
+SRCS-y += libxl_qmp.c
+SRCS-y += libxl_event.c
+SRCS-y += libxl_fork.c
+SRCS-y += libxl_dom_suspend.c
+SRCS-y += libxl_dom_save.c
+SRCS-y += libxl_usb.c
+SRCS-y += libxl_vtpm.c
+SRCS-y += libxl_nic.c
+SRCS-y += libxl_disk.c
+SRCS-y += libxl_console.c
+SRCS-y += libxl_cpupool.c
+SRCS-y += libxl_mem.c
+SRCS-y += libxl_sched.c
+SRCS-y += libxl_tmem.c
+SRCS-y += libxl_9pfs.c
+SRCS-y += libxl_domain.c
+SRCS-y += libxl_vdispl.c
+SRCS-y += libxl_pvcalls.c
+SRCS-y += libxl_vsnd.c
+SRCS-y += libxl_vkb.c
+SRCS-y += libxl_genid.c
+SRCS-y += _libxl_types.c
+SRCS-y += libxl_flask.c
+SRCS-y += _libxl_types_internal.c
+
+ifeq ($(CONFIG_LIBNL),y)
+CFLAGS_LIBXL += $(LIBNL3_CFLAGS)
+endif
+CFLAGS_LIBXL += -Wshadow
+ifeq ($(debug),y)
+CFLAGS_LIBXL += -DCONFIG_DEBUG
+endif
+
+CFLAGS += $(PTHREAD_CFLAGS)
+LDFLAGS += $(PTHREAD_LDFLAGS)
+
+LIBXL_TESTS += timedereg
+LIBXL_TESTS_PROGS = $(LIBXL_TESTS) fdderegrace
+LIBXL_TESTS_INSIDE = $(LIBXL_TESTS) fdevent
+
+# Each entry FOO in LIBXL_TESTS has two main .c files:
+#   libxl_test_FOO.c  "inside libxl" code to support the test case
+#   test_FOO.c        "outside libxl" code to exercise the test case
+# Conventionally there will also be:
+#   libxl_test_FOO.h  interface between the "inside" and "outside" parts
+# The "inside libxl" file is compiled exactly like a piece of libxl, and the
+# "outside libxl" file is compiled exactly like a piece of application
+# code.  They must share information via explicit libxl entrypoints.
+# Unlike proper parts of libxl, it is permissible for libxl_test_FOO.c
+# to use private global variables for its state.  Note that all the
+# "inside" parts are compiled into a single test library, so their
+# symbol names must be unique.
+#
+# To run these tests, either use LD_PRELOAD to get libxenlight_test.so
+# loaded, or rename it to libxenlight.so so it is the target of the
+# appropriate symlinks.
+
+LIBXL_TEST_OBJS += $(foreach t, $(LIBXL_TESTS_INSIDE),libxl_test_$t.opic)
+TEST_PROG_OBJS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t.o) test_common.o
+TEST_PROGS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t)
+
+AUTOINCS = _libxl_list.h _paths.h _libxl_save_msgs_callout.h _libxl_save_msgs_helper.h
+AUTOSRCS = _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c
+
+CLIENTS = testidl libxl-save-helper
+
+SAVE_HELPER_OBJS = libxl_save_helper.o _libxl_save_msgs_helper.o
+
+LIBHEADER := libxl.h libxl_event.h libxl_json.h _libxl_types.h _libxl_types_json.h _libxl_list.h libxl_utils.h libxl_uuid.h
+
+NO_HEADERS_CHK := y
+
+include $(XEN_ROOT)/tools/libs/libs.mk
+
+$(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(CURDIR)
+$(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude)
+
+LDUSELIBS-y += $(PTYFUNCS_LIBS)
+LDUSELIBS-$(CONFIG_LIBNL) += $(LIBNL3_LIBS)
+LDUSELIBS-$(CONFIG_Linux) += -luuid
+LDUSELIBS-$(CONFIG_Linux) += -lrt
+LDUSELIBS-$(CONFIG_ARM) += -lfdt
+LDUSELIBS-y += $(PTHREAD_LIBS)
+LDUSELIBS-y += -lyajl
+LDUSELIBS += $(LDUSELIBS-y)
+
+$(LIB_OBJS) $(PIC_OBJS) $(LIBXL_TEST_OBJS): CFLAGS += $(CFLAGS_LIBXL) -include $(XEN_ROOT)/tools/config.h
+$(ACPI_OBJS) $(ACPI_PIC_OBJS): CFLAGS += -I. -DLIBACPI_STDUTILS=\"$(CURDIR)/libxl_x86_acpi.h\"
+$(TEST_PROG_OBJS) _libxl.api-for-check: CFLAGS += $(CFLAGS_libxentoollog) $(CFLAGS_libxentoolcore)
+libxl_dom.o libxl_dom.opic: CFLAGS += -I$(XEN_ROOT)/tools  # include libacpi/x86.h
+libxl_x86_acpi.o libxl_x86_acpi.opic: CFLAGS += -I$(XEN_ROOT)/tools
+$(SAVE_HELPER_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenevtchn) $(CFLAGS_libxenguest)
+
+testidl.o: CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenlight)
+testidl.c: libxl_types.idl gentest.py include/libxl.h $(AUTOINCS)
+       $(PYTHON) gentest.py libxl_types.idl testidl.c.new
+       mv testidl.c.new testidl.c
+
+build: $(CLIENTS) $(TEST_PROGS) $(AUTOSRCS) $(AUTOINCS)
+
+$(LIB_OBJS) $(PIC_OBJS) $(SAVE_HELPER_OBJS) $(LIBXL_TEST_OBJS) $(TEST_PROG_OBJS): $(AUTOINCS) libxl.api-ok
+
+genpath-target = $(call buildmakevars2header,_paths.h)
+$(eval $(genpath-target))
+
+libxl.api-ok: check-libxl-api-rules _libxl.api-for-check
+       $(PERL) $^
+       touch $@
+
+_%.api-for-check: include/%.h $(AUTOINCS)
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$*.o) -c -E $< $(APPEND_CFLAGS) \
+               -DLIBXL_EXTERNAL_CALLERS_ONLY=LIBXL_EXTERNAL_CALLERS_ONLY \
+               >$@.new
+       mv -f $@.new $@
+
+_libxl_list.h: $(XEN_INCLUDE)/xen-external/bsd-sys-queue-h-seddery $(XEN_INCLUDE)/xen-external/bsd-sys-queue.h
+       $(PERL) $^ --prefix=libxl >$@.new
+       $(call move-if-changed,$@.new,$@)
+
+_libxl_save_msgs_helper.c _libxl_save_msgs_callout.c \
+_libxl_save_msgs_helper.h _libxl_save_msgs_callout.h: \
+               libxl_save_msgs_gen.pl
+       $(PERL) -w $< $@ >$@.new
+       $(call move-if-changed,$@.new,$@)
+
+include/libxl.h: _libxl_types.h _libxl_list.h
+include/libxl_json.h: _libxl_types_json.h
+libxl_internal.h: _libxl_types_internal.h _libxl_types_private.h _libxl_types_internal_private.h _paths.h
+libxl_internal_json.h: _libxl_types_internal_json.h
+xl.h: _paths.h
+
+$(LIB_OBJS) $(PIC_OBJS) $(LIBXL_TEST_OBJS) $(TEST_PROG_OBJS) $(SAVE_HELPER_OBJS): include/libxl.h
+$(LIB_OBJS) $(PIC_OBJS) $(LIBXL_TEST_OBJS): libxl_internal.h
+
+_libxl_type%.h _libxl_type%_json.h _libxl_type%_private.h _libxl_type%.c: libxl_type%.idl gentypes.py idl.py
+       $(eval stem = $(notdir $*))
+       $(PYTHON) gentypes.py libxl_type$(stem).idl __libxl_type$(stem).h __libxl_type$(stem)_private.h \
+               __libxl_type$(stem)_json.h  __libxl_type$(stem).c
+       $(call move-if-changed,__libxl_type$(stem).h,_libxl_type$(stem).h)
+       $(call move-if-changed,__libxl_type$(stem)_private.h,_libxl_type$(stem)_private.h)
+       $(call move-if-changed,__libxl_type$(stem)_json.h,_libxl_type$(stem)_json.h)
+       $(call move-if-changed,__libxl_type$(stem).c,_libxl_type$(stem).c)
+
+include/_%.h: _%.h
+       cp $< $@
+
+libxenlight_test.so: $(PIC_OBJS) $(LIBXL_TEST_OBJS)
+       $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LDUSELIBS) $(APPEND_LDFLAGS)
+
+test_%: test_%.o test_common.o libxenlight_test.so
+       $(CC) $(LDFLAGS) -o $@ $^ $(filter-out %libxenlight.so, $(LDLIBS_libxenlight)) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) -lyajl $(APPEND_LDFLAGS)
+
+libxl-save-helper: $(SAVE_HELPER_OBJS) libxenlight.so
+       $(CC) $(LDFLAGS) -o $@ $(SAVE_HELPER_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
+
+testidl: testidl.o libxenlight.so
+       $(CC) $(LDFLAGS) -o $@ testidl.o $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
+
+install: installlocal $(LIBHEADERS)
+
+.PHONY: installlocal
+installlocal: libxl-save-helper
+       $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
+       $(INSTALL_PROG) libxl-save-helper $(DESTDIR)$(LIBEXEC_BIN)
+
+uninstall: uninstalllocal
+
+.PHONY: uninstalllocal
+uninstalllocal:
+       rm -f $(DESTDIR)$(LIBEXEC_BIN)/libxl-save-helper
+
+clean: cleanlocal
+
+.PHONY: cleanlocal
+cleanlocal:
+       $(RM) -f _*.h *.o $(CLIENTS)
+       $(RM) -f _*.c *.pyc _paths.*.tmp _*.api-for-check
+       $(RM) -f testidl.c.new testidl.c *.api-ok
+       $(RM) -f $(TEST_PROGS)
+       $(RM) -rf __pycache__
+       $(RM) -f include/_*.h
+       $(RM) -f libxenlight.map
+       $(RM) -f $(AUTOSRCS) $(AUTOINCS)
+       $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) clean
diff --git a/tools/libs/light/check-libxl-api-rules b/tools/libs/light/check-libxl-api-rules
new file mode 100755 (executable)
index 0000000..18ff39c
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/perl -w
+use strict;
+our $needed=0;
+our $speclineoffset=0;
+our $specfile;
+while (<>) {
+    if (m/^\# (\d+) \"(.*)\"$/) {
+        $speclineoffset = $1 - $. -1;
+        $specfile = $2;
+    }
+    my $file = defined($specfile) ? $specfile : $ARGV;
+    my $line = $speclineoffset + $.;
+    if (m/libxl_asyncop_how[^;]/) {
+        $needed=1;
+    }
+    if (m/LIBXL_EXTERNAL_CALLERS_ONLY/) {
+        $needed=0;
+    }
+    next unless $needed;
+    if (m/\;/) {
+        die "$file:$line:missing LIBXL_EXTERNAL_CALLERS_ONLY";
+    }
+}
diff --git a/tools/libs/light/flexarray.c b/tools/libs/light/flexarray.c
new file mode 100644 (file)
index 0000000..fe40e76
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_internal.h"
+#include <stdarg.h>
+
+/*
+ * It is safe to store gc in the struct because:
+ * - If it an actual gc, then the flexarray should not be used after the gc
+ *   have been freed.
+ * - If it is a NOGC, then this point to a structure embedded in libxl_ctx,
+ *   therefore will survive across several libxl calls.
+ */
+
+flexarray_t *flexarray_make(libxl__gc *gc, int size, int autogrow)
+{
+    flexarray_t *array;
+
+    GCNEW(array);
+    array->size = size;
+    array->autogrow = autogrow;
+    array->count = 0;
+    array->gc = gc;
+    GCNEW_ARRAY(array->data, size);
+
+    return array;
+}
+
+void flexarray_free(flexarray_t *array)
+{
+    assert(!libxl__gc_is_real(array->gc));
+    free(array->data);
+    free(array);
+}
+
+void flexarray_grow(flexarray_t *array, int extents)
+{
+    int newsize;
+    libxl__gc *gc = array->gc;
+
+    newsize = array->size + extents;
+    GCREALLOC_ARRAY(array->data, newsize);
+    array->size += extents;
+}
+
+int flexarray_set(flexarray_t *array, unsigned int idx, void *ptr)
+{
+    if (idx >= array->size) {
+        int newsize;
+        if (!array->autogrow)
+            return 1;
+        newsize = (array->size * 2 < idx) ? idx + 1 : array->size * 2;
+        flexarray_grow(array, newsize - array->size);
+    }
+    if ( idx + 1 > array->count )
+        array->count = idx + 1;
+    array->data[idx] = ptr;
+    return 0;
+}
+
+int flexarray_append(flexarray_t *array, void *ptr)
+{
+    return flexarray_set(array, array->count, ptr);
+}
+
+int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2)
+{
+    int rc = flexarray_append(array, ptr1);
+    if (!rc)
+        rc = flexarray_append(array, ptr2);
+    return rc;
+}
+
+int flexarray_vappend(flexarray_t *array, ...)
+{
+    va_list va;
+    void *ptr;
+    int ret;
+
+    va_start(va, array);
+    for(ret = 0; (ptr = va_arg(va, void *)); ret++) {
+        if ( flexarray_append(array, ptr) )
+            break;
+    }
+    va_end(va);
+    return ret;
+}
+
+int flexarray_get(flexarray_t *array, int idx, void **ptr)
+{
+    if (idx >= array->size)
+        return 1;
+    *ptr = array->data[idx];
+    return 0;
+}
+
+void **flexarray_contents(flexarray_t *array)
+{
+    void **data;
+    data = array->data;
+    if (!libxl__gc_is_real(array->gc))
+        free(array);
+    return data;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/flexarray.h b/tools/libs/light/flexarray.h
new file mode 100644 (file)
index 0000000..a1e8647
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef FLEXARRAY_H
+#define FLEXARRAY_H
+
+struct libxl__gc;
+
+typedef struct flexarray {
+    int size;
+    int autogrow;
+    unsigned int count;
+    void **data; /* array of pointer */
+    struct libxl__gc *gc;
+} flexarray_t;
+
+/*
+ * NOGC can be used with flexarrays, but flexarray_free will need to be called
+ * to free the struct. The content of the flexarray will not be freed through
+ * flexarray_free.
+ */
+_hidden flexarray_t *flexarray_make(struct libxl__gc *gc_opt,
+                                    int size, int autogrow);
+_hidden void flexarray_free(flexarray_t *array);
+_hidden void flexarray_grow(flexarray_t *array, int extents);
+_hidden int flexarray_set(flexarray_t *array, unsigned int index, void *ptr);
+_hidden int flexarray_append(flexarray_t *array, void *ptr);
+_hidden int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2);
+_hidden int flexarray_vappend(flexarray_t *array, ...);
+_hidden int flexarray_get(flexarray_t *array, int index, void **ptr);
+
+_hidden void **flexarray_contents(flexarray_t *array);
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/gentest.py b/tools/libs/light/gentest.py
new file mode 100644 (file)
index 0000000..1cc7eeb
--- /dev/null
@@ -0,0 +1,374 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+
+import os
+import sys
+import re
+import random
+
+import idl
+
+def randomize_char(c):
+    if random.random() < 0.5:
+        return str.lower(c)
+    else:
+        return str.upper(c)
+
+def randomize_case(s):
+    r = [randomize_char(c) for c in s]
+    return "".join(r)
+
+def randomize_enum(e):
+    return random.choice([v.name for v in e.values])
+
+handcoded = ["libxl_bitmap", "libxl_key_value_list",
+             "libxl_cpuid_policy_list", "libxl_string_list"]
+
+def gen_rand_init(ty, v, indent = "    ", parent = None):
+    s = ""
+    if isinstance(ty, idl.Enumeration):
+        s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), randomize_enum(ty))
+    elif isinstance(ty, idl.Array):
+        if parent is None:
+            raise Exception("Array type must have a parent")
+        s += "%s = test_rand(8);\n" % (parent + ty.lenvar.name)
+        s += "%s = calloc(%s, sizeof(*%s));\n" % \
+            (v, parent + ty.lenvar.name, v)
+        s += "assert(%s);\n" % (v, )
+        s += "{\n"
+        s += "    int i;\n"
+        s += "    for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
+        s += gen_rand_init(ty.elem_type, v+"[i]",
+                           indent + "        ", parent)
+        s += "}\n"
+    elif isinstance(ty, idl.KeyedUnion):
+        if parent is None:
+            raise Exception("KeyedUnion type must have a parent")
+        s += gen_rand_init(ty.keyvar.type, parent + ty.keyvar.name, indent, parent)
+        s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+        for f in ty.fields:
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            s += "case %s:\n" % f.enumname
+            if f.type is not None:
+                s += gen_rand_init(f.type, fexpr, indent + "    ", nparent)
+            s += "    break;\n"
+        s += "}\n"
+    elif isinstance(ty, idl.Struct) \
+     and (parent is None or ty.json_gen_fn is None):
+        for f in [f for f in ty.fields if not f.const]:
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            s += gen_rand_init(f.type, fexpr, "", nparent)
+    elif hasattr(ty, "rand_init") and ty.rand_init is not None:
+        s += "%s(%s);\n" % (ty.rand_init,
+                            ty.pass_arg(v, isref=parent is None,
+                                        passby=idl.PASS_BY_REFERENCE))
+    elif ty.typename in ["libxl_uuid", "libxl_mac", "libxl_hwcap", "libxl_ms_vm_genid"]:
+        s += "rand_bytes((uint8_t *)%s, sizeof(*%s));\n" % (v,v)
+    elif ty.typename in ["libxl_domid", "libxl_devid"] or isinstance(ty, idl.Number):
+        s += "%s = test_rand(sizeof(%s) * 8);\n" % \
+             (ty.pass_arg(v, parent is None),
+              ty.pass_arg(v, parent is None))
+    elif ty.typename in ["bool"]:
+        s += "%s = test_rand(2);\n" % v
+    elif ty.typename in ["libxl_defbool"]:
+        s += "libxl_defbool_set(%s, test_rand(2));\n" % v
+    elif ty.typename in ["char *"]:
+        s += "%s = rand_str();\n" % v
+    elif ty.private:
+        pass
+    elif ty.typename in handcoded:
+        raise Exception("Gen for handcoded %s" % ty.typename)
+    else:
+        raise Exception("Cannot randomly init %s" % ty.typename)
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+if __name__ == '__main__':
+    if len(sys.argv) < 3:
+        print("Usage: gentest.py <idl> <implementation>", file=sys.stderr)
+        sys.exit(1)
+
+    random.seed(os.getenv('LIBXL_TESTIDL_SEED'))
+
+    (builtins,types) = idl.parse(sys.argv[1])
+
+    impl = sys.argv[2]
+    f = open(impl, "w")
+    f.write("""
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "libxl.h"
+#include "libxl_utils.h"
+
+static int test_rand(unsigned max)
+{
+    /* We are not using rand() for its cryptographic properies. */
+    return rand() % max;
+}
+
+static char *rand_str(void)
+{
+    int i, sz = test_rand(32);
+    char *s = malloc(sz+1);
+    assert(s);
+    for (i=0; i<sz; i++)
+        s[i] = 'a' + test_rand(26);
+    s[i] = '\\0';
+    return s;
+}
+
+static void rand_bytes(uint8_t *p, size_t sz)
+{
+    int i;
+    for (i=0; i<sz; i++)
+        p[i] = test_rand(256);
+}
+
+static void libxl_bitmap_rand_init(libxl_bitmap *bitmap)
+{
+    int i;
+    bitmap->size = test_rand(16);
+    bitmap->map = calloc(bitmap->size, sizeof(*bitmap->map));
+    assert(bitmap->map);
+    libxl_for_each_bit(i, *bitmap) {
+        if (test_rand(2))
+            libxl_bitmap_set(bitmap, i);
+        else
+            libxl_bitmap_reset(bitmap, i);
+    }
+}
+
+static void libxl_key_value_list_rand_init(libxl_key_value_list *pkvl)
+{
+    int i, nr_kvp = test_rand(16);
+    libxl_key_value_list kvl = calloc(nr_kvp+1, 2*sizeof(char *));
+    assert(kvl);
+
+    for (i = 0; i<2*nr_kvp; i += 2) {
+        kvl[i] = rand_str();
+        if (test_rand(8))
+            kvl[i+1] = rand_str();
+        else
+            kvl[i+1] = NULL;
+    }
+    kvl[i] = NULL;
+    kvl[i+1] = NULL;
+    *pkvl = kvl;
+}
+
+static void libxl_cpuid_policy_list_rand_init(libxl_cpuid_policy_list *pp)
+{
+    int i, nr_policies = test_rand(16);
+    struct {
+        const char *n;
+        int w;
+    } options[] = {
+      /* A random selection from libxl_cpuid_parse_config */
+        {"maxleaf",     32},
+        {"family",       8},
+        {"model",        8},
+        {"stepping",     4},
+        {"localapicid",  8},
+        {"proccount",    8},
+        {"clflush",      8},
+        {"brandid",      8},
+        {"f16c",         1},
+        {"avx",          1},
+        {"osxsave",      1},
+        {"xsave",        1},
+        {"aes",          1},
+        {"popcnt",       1},
+        {"movbe",        1},
+        {"x2apic",       1},
+        {"sse4.2",       1},
+        {"sse4.1",       1},
+        {"dca",          1},
+        {"pdcm",         1},
+        {"procpkg",      6},
+    };
+    const int nr_options = sizeof(options)/sizeof(options[0]);
+    char buf[64];
+    libxl_cpuid_policy_list p = NULL;
+
+    for (i = 0; i < nr_policies; i++) {
+        int opt = test_rand(nr_options);
+        int val = test_rand(1<<options[opt].w);
+        snprintf(buf, 64, \"%s=%#x\", options[opt].n, val);
+        libxl_cpuid_parse_config(&p, buf);
+    }
+    *pp = p;
+}
+
+static void libxl_string_list_rand_init(libxl_string_list *p)
+{
+    int i, nr = test_rand(16);
+    libxl_string_list l = calloc(nr+1, sizeof(char *));
+    assert(l);
+
+    for (i = 0; i<nr; i++) {
+        l[i] = rand_str();
+    }
+    l[i] = NULL;
+    *p = l;
+}
+""")
+    for ty in builtins + types:
+        if isinstance(ty, idl.Number): continue
+        if ty.typename not in handcoded:
+            f.write("static void %s_rand_init(%s);\n" % \
+                    (ty.typename,
+                     ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+            f.write("static void %s_rand_init(%s)\n" % \
+                    (ty.typename,
+                     ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+            f.write("{\n")
+            f.write(gen_rand_init(ty, "p"))
+            f.write("}\n")
+            f.write("\n")
+        ty.rand_init = "%s_rand_init" % ty.typename
+
+    f.write("""
+int main(int argc, char **argv)
+{
+""")
+
+    for ty in types:
+        f.write("    %s %s_val, %s_val_new;\n" % \
+                (ty.typename, ty.typename, ty.typename))
+    f.write("""
+    int rc;
+    char *s, *new_s, *json_string;
+    xentoollog_logger_stdiostream *logger;
+    libxl_ctx *ctx;
+
+    logger = xtl_createlogger_stdiostream(stderr, XTL_DETAIL, 0);
+    if (!logger) exit(1);
+
+    if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)logger)) {
+        fprintf(stderr, "cannot init xl context\\n");
+        exit(1);
+    }
+""")
+    f.write("    printf(\"Testing TYPE_to/from_json()\\n\");\n")
+    f.write("    printf(\"----------------------\\n\");\n")
+    f.write("    printf(\"\\n\");\n")
+    for ty in [t for t in types if t.json_gen_fn is not None]:
+        arg = ty.typename + "_val"
+        f.write("    %s_rand_init(%s);\n" % (ty.typename, \
+            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+        if not isinstance(ty, idl.Enumeration):
+            iters = random.randrange(1,10)
+            while iters > 0:
+                f.write("    %s_init(%s_new);\n" % (ty.typename, \
+                    ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+                iters -= 1
+        f.write("    s = %s_to_json(ctx, %s);\n" % \
+                (ty.typename, ty.pass_arg(arg, isref=False)))
+        f.write("    printf(\"%%s: %%s\\n\", \"%s\", s);\n" % ty.typename)
+        f.write("    if (s == NULL) abort();\n")
+        f.write("    rc = %s_from_json(ctx, &%s_val_new, s);\n" % \
+                (ty.typename, ty.typename))
+        f.write("    if (rc) abort();\n")
+        f.write("    new_s = %s_to_json(ctx, %s_new);\n" % \
+                (ty.typename, ty.pass_arg(arg, isref=False)))
+        f.write("    if (new_s == NULL) abort();\n")
+        f.write("    if (strcmp(s, new_s)) {\n")
+        f.write("        printf(\"Huh? Regenerated string different from original string.\\n\");\n")
+        f.write("        printf(\"Regenerated string: %s\\n\", new_s);\n")
+        f.write("        abort();\n")
+        f.write("    }\n")
+        f.write("    free(s);\n")
+        f.write("    free(new_s);\n")
+        if ty.dispose_fn is not None:
+            iters = random.randrange(1,10)
+            f.write("    %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
+            while iters > 0:
+                f.write("    %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
+                iters -= 1
+        f.write("\n")
+
+    f.write("    printf(\"Testing TYPE_copy()\\n\");\n")
+    f.write("    printf(\"----------------------\\n\");\n")
+    f.write("    printf(\"\\n\");\n")
+    for ty in [t for t in types if t.copy_fn is not None]:
+        f.write("    printf(\"Testing %s_copy, \");\n" % ty.typename)
+        arg = ty.typename + "_val"
+        f.write("    %s_init(%s);\n" % (ty.typename, \
+            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+        f.write("    %s_rand_init(%s);\n" % (ty.typename, \
+            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+        f.write("    %s_init(%s_new);\n" % (ty.typename, \
+            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+        f.write("    %s_copy(ctx, %s_new, %s);\n" % (ty.typename, \
+            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE), \
+            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
+        f.write("    s = %s_to_json(ctx, %s);\n" % \
+                (ty.typename, ty.pass_arg(arg, isref=False)))
+        f.write("    if (s == NULL) abort();\n")
+        f.write("    new_s = %s_to_json(ctx, %s_new);\n" % \
+                (ty.typename, ty.pass_arg(arg, isref=False)))
+        f.write("    if (new_s == NULL) abort();\n")
+        f.write("    if (strcmp(s, new_s)) {\n")
+        f.write("        printf(\"Huh? Deep copy for %s failed. Regenerated string different from original string.\\n\");\n" \
+                % ty.typename)
+        f.write("        printf(\"Original string: %s\\n\", s);\n")
+        f.write("        printf(\"Regenerated string: %s\\n\", new_s);\n")
+        f.write("        abort();\n")
+        f.write("    }\n")
+        f.write("    free(s);\n")
+        f.write("    free(new_s);\n")
+        if ty.dispose_fn is not None:
+            f.write("    %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
+            f.write("    %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
+        f.write("    printf(\"done\\n\");\n")
+        f.write("\n")
+
+    f.write("    printf(\"\\n\");\n")
+    f.write("    printf(\"Testing Enumerations\\n\");\n")
+    f.write("    printf(\"--------------------\\n\");\n")
+    f.write("    printf(\"\\n\");\n")
+    for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
+        f.write("    printf(\"%s -- to string:\\n\");\n" % (ty.typename))
+        for v in ty.values:
+            f.write("    printf(\"\\t%s = %%d = \\\"%%s\\\"\\n\", " \
+                    "%s, %s_to_string(%s));\n" % \
+                    (v.valuename, v.name, ty.typename, v.name))
+        f.write("\n")
+
+        f.write("    printf(\"%s -- to JSON:\\n\");\n" % (ty.typename))
+        for v in ty.values:
+            f.write("    json_string = %s_to_json(ctx, %s);\n" % \
+                    (ty.typename, v.name))
+            f.write("    printf(\"\\t%s = %%d = %%s\", " \
+                    "%s, json_string);\n" %\
+                    (v.valuename, v.name))
+            f.write("    free(json_string);\n");
+            f.write("    json_string = NULL;\n");
+        f.write("\n")
+
+        f.write("    printf(\"%s -- from string:\\n\");\n" % (ty.typename))
+        for v in [v.valuename for v in ty.values] + ["AN INVALID VALUE"]:
+            n = randomize_case(v)
+            f.write("    %s_val = -1;\n" % (ty.typename))
+            f.write("    rc = %s_from_string(\"%s\", &%s_val);\n" %\
+                    (ty.typename, n, ty.typename))
+
+            f.write("    printf(\"\\t%s = \\\"%%s\\\" = %%d (rc %%d)\\n\", " \
+                    "\"%s\", %s_val, rc);\n" %\
+                    (v, n, ty.typename))
+        f.write("\n")
+
+    f.write("""
+
+    libxl_ctx_free(ctx);
+    xtl_logger_destroy((xentoollog_logger*)logger);
+
+    return 0;
+}
+""")
diff --git a/tools/libs/light/gentypes.py b/tools/libs/light/gentypes.py
new file mode 100644 (file)
index 0000000..9a45e45
--- /dev/null
@@ -0,0 +1,797 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+
+import sys
+import re
+
+import idl
+
+def libxl_C_instance_of(ty, instancename):
+    if isinstance(ty, idl.Aggregate) and ty.typename is None:
+        if instancename is None:
+            return libxl_C_type_define(ty)
+        else:
+            return libxl_C_type_define(ty) + " " + instancename
+
+    s = ""
+    if isinstance(ty, idl.Array):
+        s += libxl_C_instance_of(ty.lenvar.type, ty.lenvar.name) + ";\n"
+
+    return s + ty.typename + " " + instancename
+
+def libxl_C_type_define(ty, indent = ""):
+    s = ""
+    if isinstance(ty, idl.Enumeration):
+        if ty.typename is None:
+            s += "enum {\n"
+        else:
+            s += "typedef enum %s {\n" % ty.typename
+
+        for v in ty.values:
+            x = "%s = %d" % (v.name, v.value)
+            x = x.replace("\n", "\n    ")
+            s += "    " + x + ",\n"
+        if ty.typename is None:
+            s += "}"
+        else:
+            s += "} %s" % ty.typename
+
+    elif isinstance(ty, idl.Aggregate):
+        if isinstance(ty, idl.KeyedUnion):
+            s += libxl_C_instance_of(ty.keyvar.type, ty.keyvar.name) + ";\n"
+            
+        if ty.typename is None:
+            s += "%s {\n" % ty.kind
+        else:
+            s += "typedef %s %s {\n" % (ty.kind, ty.typename)
+
+        for f in ty.fields:
+            if isinstance(ty, idl.KeyedUnion) and f.type is None: continue
+            
+            x = libxl_C_instance_of(f.type, f.name)
+            if f.const:
+                x = "const " + x
+            x = x.replace("\n", "\n    ")
+            s += "    " + x + ";\n"
+        if ty.typename is None:
+            s += "}"
+        else:
+            s += "} %s" % ty.typename
+    else:
+        raise NotImplementedError("%s" % type(ty))
+    return s.replace("\n", "\n%s" % indent)
+
+def libxl_C_type_dispose(ty, v, indent = "    ", parent = None):
+    s = ""
+    if isinstance(ty, idl.KeyedUnion):
+        if parent is None:
+            raise Exception("KeyedUnion type must have a parent")
+        s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+        for f in ty.fields:
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            s += "case %s:\n" % f.enumname
+            if f.type is not None:
+                s += libxl_C_type_dispose(f.type, fexpr, indent + "    ", nparent)
+            s += "    break;\n"
+        s += "}\n"
+    elif isinstance(ty, idl.Array):
+        if parent is None:
+            raise Exception("Array type must have a parent")
+        if ty.elem_type.dispose_fn is not None:
+            s += "{\n"
+            s += "    int i;\n"
+            s += "    for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
+            s += libxl_C_type_dispose(ty.elem_type, v+"[i]",
+                                      indent + "        ", parent)
+        if ty.dispose_fn is not None:
+            if ty.elem_type.dispose_fn is not None:
+                s += "    "
+            s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
+        if ty.elem_type.dispose_fn is not None:
+            s += "}\n"
+    elif isinstance(ty, idl.Struct) and (parent is None or ty.dispose_fn is None):
+        for f in [f for f in ty.fields if not f.const]:
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            s += libxl_C_type_dispose(f.type, fexpr, "", nparent)
+    else:
+        if ty.dispose_fn is not None:
+            s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_copy(ty, v, w, indent = "    ", vparent = None, wparent = None):
+    s = ""
+
+    if vparent is None:
+        s += "GC_INIT(ctx);\n";
+
+    if isinstance(ty, idl.KeyedUnion):
+        if vparent is None or wparent is None:
+            raise Exception("KeyedUnion type must have a parent")
+        s += "%s = %s;\n" % ((vparent + ty.keyvar.name), (wparent + ty.keyvar.name))
+        s += "switch (%s) {\n" % (wparent + ty.keyvar.name)
+        for f in ty.fields:
+            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+            (wnparent,wfexpr) = ty.member(w, f, wparent is None)
+            s += "case %s:\n" % f.enumname
+            if f.type is not None:
+                s += libxl_C_type_copy(f.type, vfexpr, wfexpr, indent + "    ",
+                                       vnparent, wnparent)
+            s += "    break;\n"
+        s += "}\n"
+    elif isinstance(ty, idl.Array):
+        if vparent is None or wparent is None:
+            raise Exception("Array type must have a parent")
+        s += "%s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (ty.pass_arg(v, vparent is None),
+                                                               (wparent + ty.lenvar.name),
+                                                               ty.pass_arg(w, wparent is None))
+        s += "%s = %s;\n" % ((vparent + ty.lenvar.name), (wparent + ty.lenvar.name))
+        s += "{\n"
+        s += "    int i;\n"
+        s += "    for (i=0; i<%s; i++)\n" % (wparent + ty.lenvar.name)
+        s += libxl_C_type_copy(ty.elem_type, v+"[i]", w+"[i]",
+                               indent + "        ", vparent, wparent)
+        s += "}\n"
+    elif isinstance(ty, idl.Struct) and ((vparent is None and wparent is None) or ty.copy_fn is None):
+        for f in [f for f in ty.fields if not f.const and not f.type.private]:
+            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+            (wnparent,wfexpr) = ty.member(w, f, wparent is None)
+            s += libxl_C_type_copy(f.type, vfexpr, wfexpr, "", vnparent, wnparent)
+    else:
+        if ty.copy_fn is not None:
+            s += "%s(ctx, %s, %s);\n" % (ty.copy_fn,
+                                         ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_REFERENCE),
+                                         ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_REFERENCE))
+
+        else:
+            s += "%s = %s;\n" % (ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_VALUE),
+                                 ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_VALUE))
+
+    if vparent is None:
+        s += "GC_FREE;\n"
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_init_members(ty, nesting = 0):
+    """Returns a list of members of ty which require a separate init"""
+
+    if isinstance(ty, idl.Aggregate):
+        return [f for f in ty.fields if not f.const and isinstance(f.type,idl.KeyedUnion)]
+    else:
+        return []
+    
+def libxl_C_type_do_init(ty, pass_arg, need_zero=True, indent="    "):
+    s=indent
+    if ty.init_val is not None:
+        s+= "%s = %s;\n" % (pass_arg(idl.PASS_BY_VALUE), ty.init_val)
+    elif ty.init_fn is not None:
+        s+= "%s(%s);\n" % (ty.init_fn, pass_arg(idl.PASS_BY_REFERENCE))
+    elif need_zero:
+        ptr = pass_arg(idl.PASS_BY_REFERENCE)
+        s+= "memset(%s, 0, sizeof(*%s));\n" % (ptr, ptr)
+    else:
+        s=""
+    return s
+
+def _libxl_C_type_init(ty, v, indent = "    ", parent = None, subinit=False):
+    s = ""
+    if isinstance(ty, idl.KeyedUnion):
+        if parent is None:
+            raise Exception("KeyedUnion type must have a parent")
+        if subinit:
+            s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+            for f in ty.fields:
+                (nparent,fexpr) = ty.member(v, f, parent is None)
+                s += "case %s:\n" % f.enumname
+                if f.type is not None:
+                    s += _libxl_C_type_init(f.type, fexpr, "    ", nparent)
+                s += "    break;\n"
+            s += "}\n"
+        else:
+            if ty.keyvar.init_val:
+                s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.init_val)
+            elif ty.keyvar.type.init_val:
+                s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.type.init_val)
+    elif isinstance(ty, idl.Struct) and (parent is None or ty.init_fn is None):
+        for f in [f for f in ty.fields if not f.const]:
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            if f.init_val is not None:
+                s += "%s = %s;\n" % (fexpr, f.init_val)
+            else:
+                s += _libxl_C_type_init(f.type, fexpr, "", nparent)
+    else:
+        if ty.init_val is not None:
+            s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), ty.init_val)
+        elif ty.init_fn is not None:
+            s += "%s(%s);\n" % (ty.init_fn, ty.pass_arg(v, parent is None))
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_init(ty):
+    s = ""
+    s += "void %s(%s)\n" % (ty.init_fn, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))
+    s += "{\n"
+    s += "    memset(p, '\\0', sizeof(*p));\n"
+    s += _libxl_C_type_init(ty, "p")
+    s += "}\n"
+    s += "\n"
+    return s
+
+def libxl_C_type_member_init(ty, field):
+    if not isinstance(field.type, idl.KeyedUnion):
+        raise Exception("Only KeyedUnion is supported for member init")
+
+    ku = field.type
+    
+    s = ""
+    s += "void %s(%s, %s)\n" % (ty.init_fn + "_" + ku.keyvar.name,
+                                ty.make_arg("p", passby=idl.PASS_BY_REFERENCE),
+                                ku.keyvar.type.make_arg(ku.keyvar.name))
+    s += "{\n"
+    
+    if ku.keyvar.init_val is not None:
+        init_val = ku.keyvar.init_val
+    elif ku.keyvar.type.init_val is not None:
+        init_val = ku.keyvar.type.init_val
+    else:
+        init_val = None
+        
+    (nparent,fexpr) = ty.member(ty.pass_arg("p"), ku.keyvar, isref=True)
+    if init_val is not None:
+        s += "    assert(%s == %s);\n" % (fexpr, init_val)
+    else:
+        s += "    assert(!%s);\n" % (fexpr)
+    s += "    %s = %s;\n" % (fexpr, ku.keyvar.name)
+
+    (nparent,fexpr) = ty.member(ty.pass_arg("p"), field, isref=True)
+    s += _libxl_C_type_init(ku, fexpr, parent=nparent, subinit=True)
+    s += "}\n"
+    s += "\n"
+    return s
+
+def libxl_C_type_gen_map_key(f, parent, indent = ""):
+    s = ""
+    if isinstance(f.type, idl.KeyedUnion):
+        s += "switch (%s) {\n" % (parent + f.type.keyvar.name)
+        for x in f.type.fields:
+            v = f.type.keyvar.name + "." + x.name
+            s += "case %s:\n" % x.enumname
+            s += "    s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (v, v)
+            s += "    if (s != yajl_gen_status_ok)\n"
+            s += "        goto out;\n"
+            s += "    break;\n"
+        s += "}\n"
+    else:
+        s += "s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (f.name, f.name)
+        s += "if (s != yajl_gen_status_ok)\n"
+        s += "    goto out;\n"
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_copy_deprecated(field, v, indent = "    ", vparent = None):
+    s = ""
+
+    if isinstance(field.type, idl.KeyedUnion):
+        if vparent is None:
+            raise Exception("KeyedUnion type must have a parent")
+        s += "switch (%s) {\n" % (vparent + field.type.keyvar.name)
+        for f in [f for f in field.type.fields if not f.const]:
+            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+            s += "case %s:\n" % f.enumname
+            if f.type is not None:
+                s += libxl_C_type_copy_deprecated(f, vfexpr, indent, vnparent)
+            s+= "    break;\n"
+        s+="}\n";
+    elif isinstance(field.type, idl.Array) and field.deprecated_by:
+        raise Exception("Array type is not supported for deprecation")
+    elif isinstance(field.type, idl.Struct) and field.type.copy_fn is None:
+        for f in [f for f in field.type.fields if not f.const]:
+            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
+            s += libxl_C_type_copy_deprecated(f, vfexpr, "", vnparent)
+    elif field.deprecated_by is not None:
+        if field.type.check_default_fn is None:
+            raise Exception(
+"Deprecated field %s type doesn't have a default value checker" % field.name)
+        field_pass = lambda by: field.type.pass_arg(v, vparent is None,
+                                                    passby=by)
+        field_val = field_pass(idl.PASS_BY_VALUE)
+        field_ptr = field_pass(idl.PASS_BY_REFERENCE)
+        s+= "if (!%s(&p->%s) && !%s(%s))\n" % (field.type.check_default_fn,
+                                               field.deprecated_by,
+                                               field.type.check_default_fn,
+                                               field_ptr)
+        s+= "    return -EINVAL;\n"
+        s+="(void) (&p->%s == %s);\n" % (field.deprecated_by, field_ptr)
+        s+= "if (%s(&p->%s)) {\n" % (field.type.check_default_fn,
+                                     field.deprecated_by)
+        s+= "    "
+        if field.type.copy_fn is not None:
+            s+= "%s(ctx, &p->%s, %s);\n" % (field.type.copy_fn,
+                                            field.deprecated_by, field_ptr)
+        else:
+            s+= "p->%s = %s;\n" % (field.deprecated_by, field_val)
+
+        if field.type.dispose_fn is not None:
+            s+= "    %s(%s);\n" % (field.type.dispose_fn,
+                                   field.type.pass_arg(v, vparent is None))
+        s+=libxl_C_type_do_init(field.type, field_pass)
+        s+= "}\n"
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def get_init_val(f):
+    if f.init_val is not None:
+        return f.init_val
+    elif f.type.init_val is not None:
+        return f.type.init_val
+    return None
+
+def get_default_expr(f, nparent, fexpr):
+    if isinstance(f.type, idl.Aggregate):
+        return "1 /* always generate JSON output for aggregate type */"
+
+    if isinstance(f.type, idl.Array):
+        return "%s && %s" % (fexpr, nparent + f.type.lenvar.name)
+
+    init_val = get_init_val(f)
+    if init_val is not None:
+        return "%s != %s" % (fexpr, init_val)
+
+    if f.type.check_default_fn:
+        return "!%s(&%s)" % (f.type.check_default_fn, fexpr)
+
+    return "%s" % fexpr
+
+def libxl_C_type_gen_json(ty, v, indent = "    ", parent = None):
+    s = ""
+    if parent is None:
+        s += "yajl_gen_status s;\n"
+
+    if isinstance(ty, idl.Array):
+        if parent is None:
+            raise Exception("Array type must have a parent")
+        s += "{\n"
+        s += "    int i;\n"
+        s += "    s = yajl_gen_array_open(hand);\n"
+        s += "    if (s != yajl_gen_status_ok)\n"
+        s += "        goto out;\n"
+        s += "    for (i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name)
+        s += libxl_C_type_gen_json(ty.elem_type, v+"[i]",
+                                   indent + "        ", parent)
+        s += "    }\n"
+        s += "    s = yajl_gen_array_close(hand);\n"
+        s += "    if (s != yajl_gen_status_ok)\n"
+        s += "        goto out;\n"
+        s += "}\n"
+    elif isinstance(ty, idl.Enumeration):
+        s += "s = libxl__yajl_gen_enum(hand, %s_to_string(%s));\n" % (ty.typename, ty.pass_arg(v, parent is None))
+        s += "if (s != yajl_gen_status_ok)\n"
+        s += "    goto out;\n"
+    elif isinstance(ty, idl.KeyedUnion):
+        if parent is None:
+            raise Exception("KeyedUnion type must have a parent")
+        s += "switch (%s) {\n" % (parent + ty.keyvar.name)
+        for f in ty.fields:
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            s += "case %s:\n" % f.enumname
+            if f.type is not None:
+                s += libxl_C_type_gen_json(f.type, fexpr, indent + "    ", nparent)
+            else:
+                s += "    s = yajl_gen_map_open(hand);\n"
+                s += "    if (s != yajl_gen_status_ok)\n"
+                s += "        goto out;\n"
+                s += "    s = yajl_gen_map_close(hand);\n"
+                s += "    if (s != yajl_gen_status_ok)\n"
+                s += "        goto out;\n"
+            s += "    break;\n"
+        s += "}\n"
+    elif isinstance(ty, idl.Struct) and (parent is None or ty.json_gen_fn is None):
+        s += "s = yajl_gen_map_open(hand);\n"
+        s += "if (s != yajl_gen_status_ok)\n"
+        s += "    goto out;\n"
+        for f in [f for f in ty.fields if not f.const and not f.type.private]:
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            default_expr = get_default_expr(f, nparent, fexpr)
+            s += "if (%s) {\n" % default_expr
+
+            s += libxl_C_type_gen_map_key(f, nparent, "    ")
+            s += libxl_C_type_gen_json(f.type, fexpr, "    ", nparent)
+
+            s += "}\n"
+
+        s += "s = yajl_gen_map_close(hand);\n"
+        s += "if (s != yajl_gen_status_ok)\n"
+        s += "    goto out;\n"
+    else:
+        if ty.json_gen_fn is not None:
+            s += "s = %s(hand, %s);\n" % (ty.json_gen_fn, ty.pass_arg(v, parent is None))
+            s += "if (s != yajl_gen_status_ok)\n"
+            s += "    goto out;\n"
+
+    if parent is None:
+        s += "out:\n"
+        s += "return s;\n"
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_to_json(ty, v, indent = "    "):
+    s = ""
+    gen = "(libxl__gen_json_callback)&%s_gen_json" % ty.typename
+    s += "return libxl__object_to_json(ctx, \"%s\", %s, (void *)%s);\n" % (ty.typename, gen, ty.pass_arg(v, passby=idl.PASS_BY_REFERENCE))
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_parse_json(ty, w, v, indent = "    ", parent = None, discriminator = None):
+    s = ""
+    if parent is None:
+        s += "int rc = 0;\n"
+        s += "const libxl__json_object *x __attribute__((__unused__)) = o;\n"
+
+    if isinstance(ty, idl.Array):
+        if parent is None:
+            raise Exception("Array type must have a parent")
+        if discriminator is not None:
+            raise Exception("Only KeyedUnion can have discriminator")
+        lenvar = parent + ty.lenvar.name
+        s += "{\n"
+        s += "    libxl__json_object *t;\n"
+        s += "    int i;\n"
+        s += "    if (!libxl__json_object_is_array(x)) {\n"
+        s += "        rc = -1;\n"
+        s += "        goto out;\n"
+        s += "    }\n"
+        s += "    %s = x->u.array->count;\n" % lenvar
+        s += "    %s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (v, lenvar, v)
+        s += "    if (!%s && %s != 0) {\n" % (v, lenvar)
+        s += "        rc = -1;\n"
+        s += "        goto out;\n"
+        s += "    }\n"
+        s += "    for (i=0; (t=libxl__json_array_get(x,i)); i++) {\n"
+        s += libxl_C_type_do_init(ty.elem_type,
+                    lambda by: ("&" if by == idl.PASS_BY_REFERENCE else "")+
+                               ("%s[i]" % v),
+                                  need_zero=False, indent=indent+"    ")
+        s += libxl_C_type_parse_json(ty.elem_type, "t", v+"[i]",
+                                     indent + "    ", parent)
+        s += "    }\n"
+        s += "    if (i != %s) {\n" % lenvar
+        s += "        rc = -1;\n"
+        s += "        goto out;\n"
+        s += "    }\n"
+        s += "}\n"
+    elif isinstance(ty, idl.Enumeration):
+        if discriminator is not None:
+            raise Exception("Only KeyedUnion can have discriminator")
+        s += "{\n"
+        s += "    const char *enum_str;\n"
+        s += "    if (!libxl__json_object_is_string(%s)) {\n" % w
+        s += "        rc = -1;\n"
+        s += "        goto out;\n"
+        s += "    }\n"
+        s += "    enum_str = libxl__json_object_get_string(%s);\n" % w
+        s += "    rc = %s_from_string(enum_str, %s);\n" % (ty.typename, ty.pass_arg(v, parent is None, idl.PASS_BY_REFERENCE))
+        s += "    if (rc)\n"
+        s += "        goto out;\n"
+        s += "}\n"
+    elif isinstance(ty, idl.KeyedUnion):
+        if parent is None:
+            raise Exception("KeyedUnion type must have a parent")
+        if discriminator is None:
+            raise Excpetion("KeyedUnion type must have a discriminator")
+        for f in ty.fields:
+            if f.enumname != discriminator:
+                continue
+            (nparent,fexpr) = ty.member(v, f, parent is None)
+            if f.type is not None:
+                s += libxl_C_type_parse_json(f.type, w, fexpr, indent + "    ", nparent)
+    elif isinstance(ty, idl.Struct) and (parent is None or ty.json_parse_fn is None):
+        if discriminator is not None:
+            raise Exception("Only KeyedUnion can have discriminator")
+        for f in [f for f in ty.fields if not f.const and not f.type.private]:
+            saved_var_name = "saved_%s" % f.name
+            s += "{\n"
+            s += "    const libxl__json_object *%s = x;\n" % saved_var_name
+            if isinstance(f.type, idl.KeyedUnion):
+                for x in f.type.fields:
+                    s += "    x = libxl__json_map_get(\"%s\", %s, JSON_MAP);\n" % \
+                         (f.type.keyvar.name + "." + x.name, w)
+                    s += "    if (x) {\n"
+                    (nparent, fexpr) = ty.member(v, f.type.keyvar, parent is None)
+                    s += "        %s_init_%s(%s, %s);\n" % (ty.typename, f.type.keyvar.name, v, x.enumname)
+                    (nparent,fexpr) = ty.member(v, f, parent is None)
+                    s += libxl_C_type_parse_json(f.type, "x", fexpr, "  ", nparent, x.enumname)
+                    s += "    }\n"
+            else:
+                s += "    x = libxl__json_map_get(\"%s\", %s, %s);\n" % (f.name, w, f.type.json_parse_type)
+                s += "    if (x) {\n"
+                (nparent,fexpr) = ty.member(v, f, parent is None)
+                s += libxl_C_type_parse_json(f.type, "x", fexpr, "        ", nparent)
+                s += "    }\n"
+            s += "    x = %s;\n" % saved_var_name
+            s += "}\n"
+    else:
+        if discriminator is not None:
+            raise Exception("Only KeyedUnion can have discriminator")
+        if ty.json_parse_fn is not None:
+            s += "rc = %s(gc, %s, &%s);\n" % (ty.json_parse_fn, w, v)
+            s += "if (rc)\n"
+            s += "    goto out;\n"
+
+    if parent is None:
+        s += "out:\n"
+        s += "return rc;\n"
+
+    if s != "":
+        s = indent +s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_type_from_json(ty, v, w, indent = "    "):
+    s = ""
+    parse = "(libxl__json_parse_callback)&%s_parse_json" % (ty.namespace + "_" + ty.rawname)
+    s += "return libxl__object_from_json(ctx, \"%s\", %s, %s, %s);\n" % (ty.typename, parse, v, w)
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_enum_to_string(ty, e, indent = "    "):
+    s = ""
+    s += "switch(%s) {\n" % e
+    for v in ty.values:
+        s += "    case %s:\n" % (v.name)
+        s += "        return \"%s\";\n" % (v.valuename.lower())
+    s += "    default:\n "
+    s += "        return NULL;\n"
+    s += "}\n"
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_enum_strings(ty, indent=""):
+    s = ""
+    s += "libxl_enum_string_table %s_string_table[] = {\n" % (ty.typename)
+    for v in ty.values:
+        s += "    { .s = \"%s\", .v = %s },\n" % (v.valuename.lower(), v.name)
+    s += "    { NULL, -1 },\n"
+    s += "};\n"
+    s += "\n"
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+def libxl_C_enum_from_string(ty, str, e, indent = "    "):
+    s = ""
+    s += "return libxl__enum_from_string(%s_string_table,\n" % ty.typename
+    s += "                               %s, (int *)%s);\n" % (str, e)
+
+    if s != "":
+        s = indent + s
+    return s.replace("\n", "\n%s" % indent).rstrip(indent)
+
+
+if __name__ == '__main__':
+    if len(sys.argv) != 6:
+        print("Usage: gentypes.py <idl> <header> <header-private> <header-json> <implementation>", file=sys.stderr)
+        sys.exit(1)
+
+    (_, idlname, header, header_private, header_json, impl) = sys.argv
+
+    (builtins,types) = idl.parse(idlname)
+
+    print("outputting libxl type definitions to %s" % header)
+
+    f = open(header, "w")
+
+    header_define = header.upper().replace('.','_')
+    f.write("""#ifndef %s
+#define %s
+
+/*
+ * DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+""" % (header_define, header_define, " ".join(sys.argv)))
+
+    for ty in types:
+        f.write(libxl_C_type_define(ty) + ";\n")
+        if ty.dispose_fn is not None:
+            f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.dispose_fn, ty.make_arg("p")))
+        if ty.copy_deprecated_fn is not None:
+            f.write("%sint %s(libxl_ctx *ctx, %s);\n" % (ty.hidden(),
+                                                         ty.copy_deprecated_fn,
+                                                         ty.make_arg("p")))
+        if ty.copy_fn is not None:
+            f.write("%svoid %s(libxl_ctx *ctx, %s, const %s);\n" % (ty.hidden(), ty.copy_fn,
+                                              ty.make_arg("dst"), ty.make_arg("src")))
+        if ty.init_fn is not None:
+            f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.init_fn, ty.make_arg("p")))
+            for field in libxl_init_members(ty):
+                if not isinstance(field.type, idl.KeyedUnion):
+                    raise Exception("Only KeyedUnion is supported for member init")
+                ku = field.type
+                f.write("%svoid %s(%s, %s);\n" % (ty.hidden(), ty.init_fn + "_" + ku.keyvar.name,
+                                               ty.make_arg("p"),
+                                               ku.keyvar.type.make_arg(ku.keyvar.name)))
+        if ty.json_gen_fn is not None:
+            f.write("%schar *%s_to_json(libxl_ctx *ctx, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
+        if ty.json_parse_fn is not None:
+            f.write("%sint %s_from_json(libxl_ctx *ctx, %s, const char *s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+        if isinstance(ty, idl.Enumeration):
+            f.write("%sconst char *%s_to_string(%s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
+            f.write("%sint %s_from_string(const char *s, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("e", passby=idl.PASS_BY_REFERENCE)))
+            f.write("%sextern libxl_enum_string_table %s_string_table[];\n" % (ty.hidden(), ty.typename))
+        f.write("\n")
+
+    f.write("""#endif /* %s */\n""" % (header_define))
+    f.close()
+
+    print("outputting libxl JSON definitions to %s" % header_json)
+
+    f = open(header_json, "w")
+
+    header_json_define = header_json.upper().replace('.','_')
+    f.write("""#ifndef %s
+#define %s
+
+/*
+ * DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+""" % (header_json_define, header_json_define, " ".join(sys.argv)))
+
+    for ty in [ty for ty in types if ty.json_gen_fn is not None]:
+        f.write("%syajl_gen_status %s_gen_json(yajl_gen hand, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+
+    f.write("\n")
+    f.write("""#endif /* %s */\n""" % header_json_define)
+    f.close()
+
+    print("outputting libxl type internal definitions to %s" % header_private)
+
+    f = open(header_private, "w")
+
+    header_private_define = header_private.upper().replace('.','_')
+    f.write("""#ifndef %s
+#define %s
+
+/*
+ * DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+""" % (header_private_define, header_private_define, " ".join(sys.argv)))
+
+    for ty in [ty for ty in types if ty.json_parse_fn is not None]:
+        f.write("%sint %s_parse_json(libxl__gc *gc, const libxl__json_object *o, %s);\n" % \
+                (ty.hidden(), ty.namespace + "_" + ty.rawname,
+                 ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+
+    f.write("\n")
+    f.write("""#endif /* %s */\n""" % header_json_define)
+    f.close()
+
+    print("outputting libxl type implementations to %s" % impl)
+
+    f = open(impl, "w")
+    f.write("""
+/* DO NOT EDIT.
+ *
+ * This file is autogenerated by
+ * "%s"
+ */
+
+#include "libxl_osdeps.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libxl_internal.h"
+
+
+""" % " ".join(sys.argv))
+
+    for ty in [t for t in types if t.dispose_fn is not None and t.autogenerate_dispose_fn]:
+        f.write("void %s(%s)\n" % (ty.dispose_fn, ty.make_arg("p")))
+        f.write("{\n")
+        f.write("    if (!p) return;\n")
+        f.write(libxl_C_type_dispose(ty, "p"))
+        f.write("    memset(p, 0, sizeof(*p));\n")
+        f.write("}\n")
+        f.write("\n")
+
+    for ty in [t for t in types if t.copy_fn and t.autogenerate_copy_fn]:
+        f.write("void %s(libxl_ctx *ctx, %s, const %s)\n" % (ty.copy_fn,
+                                       ty.make_arg("dst", passby=idl.PASS_BY_REFERENCE),
+                                       ty.make_arg("src", passby=idl.PASS_BY_REFERENCE)))
+        f.write("{\n")
+        f.write(libxl_C_type_copy(ty, "dst", "src"))
+        f.write("}\n")
+        f.write("\n")
+        
+    for ty in [t for t in types if t.copy_deprecated_fn]:
+        f.write("int %s(libxl_ctx *ctx, %s)\n" % (ty.copy_deprecated_fn,
+            ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+        f.write("{\n")
+        for field in [field for field in ty.fields if not field.const]:
+            (vnparent,vfexpr) = ty.member("p", field, True)
+            f.write(libxl_C_type_copy_deprecated(field, vfexpr,
+                                                 vparent = vnparent))
+        f.write("    return 0;\n")
+        f.write("}\n")
+        f.write("\n")
+
+    for ty in [t for t in types if t.init_fn is not None and t.autogenerate_init_fn]:
+        f.write(libxl_C_type_init(ty))
+        for field in libxl_init_members(ty):
+            f.write(libxl_C_type_member_init(ty, field))
+
+    for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
+        f.write("const char *%s_to_string(%s e)\n" % (ty.typename, ty.typename))
+        f.write("{\n")
+        f.write(libxl_C_enum_to_string(ty, "e"))
+        f.write("}\n")
+        f.write("\n")
+
+        f.write(libxl_C_enum_strings(ty))
+
+        f.write("int %s_from_string(const char *s, %s *e)\n" % (ty.typename, ty.typename))
+        f.write("{\n")
+        f.write(libxl_C_enum_from_string(ty, "s", "e"))
+        f.write("}\n")
+        f.write("\n")
+
+    for ty in [t for t in types if t.json_gen_fn is not None]:
+        f.write("yajl_gen_status %s_gen_json(yajl_gen hand, %s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+        f.write("{\n")
+        f.write(libxl_C_type_gen_json(ty, "p"))
+        f.write("}\n")
+        f.write("\n")
+
+        f.write("char *%s_to_json(libxl_ctx *ctx, %s)\n" % (ty.typename, ty.make_arg("p")))
+        f.write("{\n")
+        f.write(libxl_C_type_to_json(ty, "p"))
+        f.write("}\n")
+        f.write("\n")
+
+    for ty in [t for t in types if t.json_parse_fn is not None]:
+        f.write("int %s_parse_json(libxl__gc *gc, const libxl__json_object *%s, %s)\n" % \
+                (ty.namespace + "_" + ty.rawname,"o",ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+        f.write("{\n")
+        f.write(libxl_C_type_parse_json(ty, "o", "p"))
+        f.write("}\n")
+        f.write("\n")
+
+        f.write("int %s_from_json(libxl_ctx *ctx, %s, const char *s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
+        f.write("{\n")
+        if not isinstance(ty, idl.Enumeration):
+            f.write("    %s_init(p);\n" % ty.typename)
+        f.write(libxl_C_type_from_json(ty, "p", "s"))
+        f.write("}\n")
+        f.write("\n")
+
+    f.close()
diff --git a/tools/libs/light/idl.py b/tools/libs/light/idl.py
new file mode 100644 (file)
index 0000000..d736750
--- /dev/null
@@ -0,0 +1,377 @@
+from __future__ import print_function
+
+import sys
+
+PASS_BY_VALUE = 1
+PASS_BY_REFERENCE = 2
+
+DIR_NONE = 0
+DIR_IN   = 1
+DIR_OUT  = 2
+DIR_BOTH = 3
+
+_default_namespace = ""
+def namespace(s):
+    if type(s) != str:
+        raise TypeError("Require a string for the default namespace.")
+    global _default_namespace
+    _default_namespace = s
+
+def _get_default_namespace():
+    global _default_namespace
+    return _default_namespace
+
+_default_hidden = False
+def hidden(b):
+    global _default_hidden
+    _default_hidden = b
+
+def _get_default_hidden():
+    global _default_hidden
+    return _default_hidden
+
+class Type(object):
+    def __init__(self, typename, **kwargs):
+        self.namespace = kwargs.setdefault('namespace',
+                _get_default_namespace())
+        self._hidden = kwargs.setdefault('hidden', _get_default_hidden())
+        self.dir = kwargs.setdefault('dir', DIR_BOTH)
+        if self.dir not in [DIR_NONE, DIR_IN, DIR_OUT, DIR_BOTH]:
+            raise ValueError
+
+        self.passby = kwargs.setdefault('passby', PASS_BY_VALUE)
+        if self.passby not in [PASS_BY_VALUE, PASS_BY_REFERENCE]:
+            raise ValueError
+
+        self.private = kwargs.setdefault('private', False)
+
+        if typename is None: # Anonymous type
+            self.typename = None
+            self.rawname = None
+        elif self.namespace is None: # e.g. system provided types
+            self.typename = typename
+            self.rawname = typename
+        else:
+            self.typename = self.namespace + typename
+            self.rawname = typename
+
+        if self.typename is not None:
+            self.dispose_fn = kwargs.setdefault('dispose_fn', self.typename + "_dispose")
+        else:
+            self.dispose_fn = kwargs.setdefault('dispose_fn', None)
+
+        self.autogenerate_dispose_fn = kwargs.setdefault('autogenerate_dispose_fn', True)
+
+        if self.typename is not None:
+            self.copy_fn = kwargs.setdefault('copy_fn', self.typename + "_copy")
+        else:
+            self.copy_fn = kwargs.setdefault('copy_fn', None)
+
+        self.autogenerate_copy_fn = kwargs.setdefault('autogenerate_copy_fn', True)
+
+        self.init_fn = kwargs.setdefault('init_fn', None)
+        self.init_val = kwargs.setdefault('init_val', None)
+        self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', False)
+
+        self.check_default_fn = kwargs.setdefault('check_default_fn', None)
+        self.copy_deprecated_fn = kwargs.setdefault('copy_deprecated_fn',
+                                                    None)
+
+        if self.typename is not None and not self.private:
+            self.json_gen_fn = kwargs.setdefault('json_gen_fn', self.typename + "_gen_json")
+            self.json_parse_type = kwargs.setdefault('json_parse_type', "JSON_ANY")
+            if self.namespace is not None:
+                self.json_parse_fn = kwargs.setdefault('json_parse_fn',
+                                                       self.namespace + "_" + self.rawname  + "_parse_json")
+            else:
+                self.json_parse_fn = kwargs.setdefault('json_parse_fn',
+                                                       self.typename + "_parse_json")
+        else:
+            self.json_gen_fn = kwargs.setdefault('json_gen_fn', None)
+            self.json_parse_type = kwargs.setdefault('json_parse_type', None)
+            self.json_parse_fn = kwargs.setdefault('json_parse_fn', None)
+
+        self.autogenerate_json = kwargs.setdefault('autogenerate_json', True)
+
+    def marshal_in(self):
+        return self.dir in [DIR_IN, DIR_BOTH]
+    def marshal_out(self):
+        return self.dir in [DIR_OUT, DIR_BOTH]
+
+    def hidden(self):
+        if self._hidden:
+            return "_hidden "
+        else:
+            return ""
+
+    def make_arg(self, n, passby=None):
+        if passby is None: passby = self.passby
+
+        if passby == PASS_BY_REFERENCE:
+            return "%s *%s" % (self.typename, n)
+        else:
+            return "%s %s" % (self.typename, n)
+
+    def pass_arg(self, n, isref=None, passby=None):
+        if passby is None: passby = self.passby
+        if isref is None: isref = self.passby == PASS_BY_REFERENCE
+
+        if passby == PASS_BY_REFERENCE:
+            if isref:
+                return "%s" % (n)
+            else:
+                return "&%s" % (n)
+        else:
+            if isref:
+                return "*%s" % (n)
+            else:
+                return "%s" % (n)
+
+class Builtin(Type):
+    """Builtin type"""
+    def __init__(self, typename, **kwargs):
+        kwargs.setdefault('dispose_fn', None)
+        kwargs.setdefault('autogenerate_dispose_fn', False)
+        kwargs.setdefault('autogenerate_json', False)
+        Type.__init__(self, typename, **kwargs)
+
+class Number(Builtin):
+    def __init__(self, ctype, **kwargs):
+        kwargs.setdefault('namespace', None)
+        kwargs.setdefault('dispose_fn', None)
+        kwargs.setdefault('copy_fn', None)
+        kwargs.setdefault('signed', False)
+        kwargs.setdefault('json_gen_fn', "yajl_gen_integer")
+        kwargs.setdefault('json_parse_type', "JSON_INTEGER")
+        # json_parse_fn might be overriden on specific type
+        kwargs.setdefault('json_parse_fn', "libxl__int_parse_json")
+        self.signed = kwargs['signed']
+        Builtin.__init__(self, ctype, **kwargs)
+
+class UInt(Number):
+    def __init__(self, w, **kwargs):
+        kwargs.setdefault('namespace', None)
+        kwargs.setdefault('dispose_fn', None)
+        kwargs.setdefault('json_parse_fn', "libxl__uint%d_parse_json" % w)
+        kwargs.setdefault('copy_fn', None)
+        Number.__init__(self, "uint%d_t" % w, **kwargs)
+
+        self.width = w
+
+class EnumerationValue(object):
+    def __init__(self, enum, value, name, **kwargs):
+        self.enum = enum
+
+        self.valuename = str.upper(name)
+        self.rawname = str.upper(enum.rawname) + "_" + self.valuename
+        self.name = str.upper(enum.value_namespace) + self.rawname
+        self.value = value
+
+class Enumeration(Type):
+    def __init__(self, typename, values, **kwargs):
+        kwargs.setdefault('dispose_fn', None)
+        kwargs.setdefault('copy_fn', None)
+        kwargs.setdefault('json_parse_type', "JSON_STRING")
+        Type.__init__(self, typename, **kwargs)
+
+        self.value_namespace = kwargs.setdefault('value_namespace',
+            self.namespace)
+
+        self.values = []
+        for v in values:
+            # (value, name)
+            (num,name) = v
+            self.values.append(EnumerationValue(self, num, name,
+                                                typename=self.rawname))
+    def lookup(self, name):
+        for v in self.values:
+            if v.valuename == str.upper(name):
+                return v
+        return ValueError
+
+class Field(object):
+    """An element of an Aggregate type"""
+    def __init__(self, type, name, **kwargs):
+        self.type = type
+        self.name = name
+        self.const = kwargs.setdefault('const', False)
+        self.enumname = kwargs.setdefault('enumname', None)
+        self.init_val = kwargs.setdefault('init_val', None)
+        self.deprecated_by = kwargs.setdefault('deprecated_by', None)
+
+class Aggregate(Type):
+    """A type containing a collection of other types"""
+    def __init__(self, kind, typename, fields, **kwargs):
+        kwargs.setdefault('json_parse_type', "JSON_MAP")
+        Type.__init__(self, typename, **kwargs)
+
+        if self.typename is not None:
+            self.init_fn = kwargs.setdefault('init_fn', self.typename + "_init")
+        else:
+            self.init_fn = kwargs.setdefault('init_fn', None)
+
+        self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', True)
+
+        self.kind = kind
+
+        self.fields = []
+        for f in fields:
+            # (name, type[, {kw args}])
+            if len(f) == 2:
+                n,t = f
+                kw = {}
+            elif len(f) == 3:
+                n,t,kw = f
+            else:
+                raise ValueError
+            if n is None:
+                raise ValueError
+            self.fields.append(Field(t,n,**kw))
+
+    # Returns a tuple (stem, field-expr)
+    #
+    # field-expr is a C expression for a field "f" within the struct
+    # "v".
+    #
+    # stem is the stem common to both "f" and any other sibbling field
+    # within the "v".
+    def member(self, v, f, isref):
+        if isref:
+            deref = v + "->"
+        else:
+            deref = v + "."
+
+        if f.name is None: # Anonymous
+            return (deref, deref)
+        else:
+            return (deref, deref + f.name)
+
+class Struct(Aggregate):
+    def __init__(self, name, fields, **kwargs):
+        kwargs.setdefault('passby', PASS_BY_REFERENCE)
+        Aggregate.__init__(self, "struct", name, fields, **kwargs)
+
+    def has_fields(self):
+        return len(self.fields) != 0
+
+class Union(Aggregate):
+    def __init__(self, name, fields, **kwargs):
+        # Generally speaking some intelligence is required to free a
+        # union therefore any specific instance of this class will
+        # need to provide an explicit destructor function.
+        kwargs.setdefault('passby', PASS_BY_REFERENCE)
+        kwargs.setdefault('dispose_fn', None)
+        Aggregate.__init__(self, "union", name, fields, **kwargs)
+
+class KeyedUnion(Aggregate):
+    """A union which is keyed of another variable in the parent structure"""
+    def __init__(self, name, keyvar_type, keyvar_name, fields, **kwargs):
+        Aggregate.__init__(self, "union", name, [], **kwargs)
+
+        if not isinstance(keyvar_type, Enumeration):
+            raise ValueError
+
+        kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in kwargs.items() if x.startswith('keyvar_')])
+        
+        self.keyvar = Field(keyvar_type, keyvar_name, **kv_kwargs)
+
+        for f in fields:
+            # (name, enum, type)
+            e, ty = f
+            ev = keyvar_type.lookup(e)
+            en = ev.name
+            self.fields.append(Field(ty, e, enumname=en))
+
+#
+# Standard Types
+#
+
+void = Builtin("void *", namespace = None)
+bool = Builtin("bool", namespace = None,
+               copy_fn=None,
+               json_gen_fn = "yajl_gen_bool",
+               json_parse_type = "JSON_BOOL",
+               json_parse_fn = "libxl__bool_parse_json",
+               autogenerate_json = False)
+
+size_t = Number("size_t", namespace = None)
+
+integer = Number("int", namespace = None, signed = True)
+
+uint8 = UInt(8)
+uint16 = UInt(16)
+uint32 = UInt(32)
+uint64 = UInt(64, json_gen_fn = "libxl__uint64_gen_json")
+
+string = Builtin("char *", namespace = None, copy_fn = "libxl_string_copy", dispose_fn = "free",
+                 json_gen_fn = "libxl__string_gen_json",
+                 json_parse_type = "JSON_STRING | JSON_NULL",
+                 json_parse_fn = "libxl__string_parse_json",
+                 autogenerate_json = False,
+                 check_default_fn="libxl__string_is_default")
+
+class Array(Type):
+    """An array of the same type"""
+    def __init__(self, elem_type, lenvar_name, **kwargs):
+        kwargs.setdefault('dispose_fn', 'free')
+        kwargs.setdefault('json_parse_type', 'JSON_ARRAY')
+        Type.__init__(self, namespace=elem_type.namespace, typename=elem_type.rawname + " *", **kwargs)
+
+        lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in kwargs.items() if x.startswith('lenvar_')])
+
+        self.lenvar = Field(integer, lenvar_name, **lv_kwargs)
+        self.elem_type = elem_type
+
+class OrderedDict(dict):
+    """A dictionary which remembers insertion order.
+
+       push to back on duplicate insertion"""
+
+    def __init__(self):
+        dict.__init__(self)
+        self.__ordered = []
+
+    def __setitem__(self, key, value):
+        try:
+            self.__ordered.remove(key)
+        except ValueError:
+            pass
+
+        self.__ordered.append(key)
+        dict.__setitem__(self, key, value)
+
+    def ordered_keys(self):
+        return self.__ordered
+    def ordered_values(self):
+        return [self[x] for x in self.__ordered]
+    def ordered_items(self):
+        return [(x,self[x]) for x in self.__ordered]
+
+def parse(f):
+    print("Parsing %s" % f, file=sys.stderr)
+
+    globs = {}
+    locs = OrderedDict()
+
+    for n,t in globals().items():
+        if isinstance(t, Type):
+            globs[n] = t
+        elif isinstance(t,type(object)) and issubclass(t, Type):
+            globs[n] = t
+        elif n in ['PASS_BY_REFERENCE', 'PASS_BY_VALUE',
+                   'DIR_NONE', 'DIR_IN', 'DIR_OUT', 'DIR_BOTH',
+                   'namespace', 'hidden']:
+            globs[n] = t
+
+    try:
+        exec(compile(open(f).read(), f, 'exec'), globs, locs)
+    except SyntaxError as e:
+        raise SyntaxError("Errors were found at line %d while processing %s:\n\t%s"
+                          % (e.lineno, f, e.text))
+
+    types = [t for t in locs.ordered_values() if isinstance(t,Type)]
+
+    builtins = [t for t in types if isinstance(t,Builtin)]
+    types = [t for t in types if not isinstance(t,Builtin)]
+
+    return (builtins,types)
diff --git a/tools/libs/light/idl.txt b/tools/libs/light/idl.txt
new file mode 100644 (file)
index 0000000..7440fb3
--- /dev/null
@@ -0,0 +1,214 @@
+libxl IDL
+---------
+
+Each type in the libxl interface is represented by an object of type
+idl.Type (or a subclass thereof). Every local variable defined by the
+.idl file must be an instance of idl.Type (e.g. you may not define
+Python functions or any other construct other than defining variables)
+
+The name of the type must be passed as the first argument to the
+constructor when defining a new type. The name given should not
+contain the initial namespace element (e.g. "libxl_"). See below for
+how to specify a namespace.
+
+The Type.typename contains the C name of the type _including_ the
+namespace element while Type.rawname is always set to the 'base' name
+of the type.
+
+The idl.Type base class has several other properties which apply to
+all types. The properties are set by passing a named parameter to the
+constructor.
+
+Type.namespace: (default: "libxl_")
+
+ The namespace in which the type resides. Usually this is "libxl_" but
+ system defined and builtin types may differ.
+
+ If the typename is not None then the namespace is prepended to the
+ type.
+Type.passby: (default: idl.PASS_BY_VALUE)
+
+ Defines the manner in which a type should be passed to C
+ functions. Valid values for this fields are:
+   idl.PASS_BY_VALUE
+   idl.PASS_BY_REFERENCE
+
+Type.dispose_fn: (default: typename + "_dispose" or None if type == None)
+
+ The name of the C function which will free all dynamically allocated
+ memory contained within this type (but not the type itself).
+
+Type.autogenerate_dispose_fn: (default: True)
+
+ Indicates if the above named Type.dispose_fn should be
+ autogenerated.
+
+Type.copy_fn: (default: typename + "_copy" or None if type == None)
+
+ The name of the C function which will deep copy all fields within
+ this type.
+
+Type.autogenerate_copy_fn: (default: True)
+
+ Indicates if the above named Type.copy_fn should be
+ autogenerated.
+
+Type.autogenerate_copy_fn
+
+Type.init_val: (default: None)
+
+ C expression for the value to initialise instances of this type to.
+
+ If present takes precendence over init_fn (see below).
+
+Type.init_fn: (default: typename + "_init" if dir in [IN, BOTH] and
+                        type != None)
+
+ The name of the C function which will initialist Type.
+
+Type.autogenerate_init_fn: (default: True if dir in [IN, BOTH])
+
+ Indicates if the above named Type.init_fn should be
+ autogenerated.
+
+Type.json_gen_fn: (default: typename + "_gen_json" or None if type == None)
+
+ The name of the C function which will generate a YAJL data structure
+ representing this type.
+
+Type.json_parse_fn: (default: typename + "_parse_json" or None if type == None)
+
+ The name of the C function which will parse a libxl JSON structure
+ representing this type to C type.
+
+Type.autogenerate_json: (default: True)
+
+ Indicates if the above named Type.json_*_fn should be autogenerated.
+
+Type.check_default_fn:
+
+ If it's set then calling this function shall return true if this type
+ has been set to default value (internal libxl implementation).
+
+ If this is not set, that means we can check the type against init_val
+ (if it has one) or zero to determine whether the value is default
+ value.
+
+Other simple type-Classes
+-------------------------
+
+idl.Builtin
+
+ Instances of this class represent types which are predefined within
+ the system.
+
+idl.UInt
+
+ Instances of this class represent the standard uint<N>_t types.
+
+ The <N> for a given instance must be passed to the constructor and is
+ then available in UInt.width
+
+Complex type-Classes
+--------------------
+
+idl.Enumeration
+
+  A class representing an enumeration (named integer values).
+  This class has one property besides the ones defined for the Type
+  class:
+
+  Enumeration.value_namespace: (default: namespace)
+
+    The namespace in which the values of the Enumeration (see below) reside.
+    This prefix is prepended to the name of the value.
+
+  The values are available in the list Enumeration.values. Each
+  element in the list is of type idl.EnumerationValue.
+
+  Each EnumerationValue has the following properties:
+
+    EnumerationValue.enum       Reference to containing Enumeration
+    EnumerationValue.name       The C name of this value, including
+                                    the namespace and typename of the
+                                    containing Enumeration (e.g.
+                                    "LIBXL_FOOENUM_VALUE")
+    EnumerationValue.rawname    The C name of this value, excluding
+                                    the namespace but including the
+                                    typename of the containing
+                                    Enumeration (e.g. "FOOENUM_VALUE")
+    EnumerationValue.valuename  The name of this value, excluding the
+                                    name of the containing Enumeration
+                                    and any namespace (e.g. "VALUE")
+    EnumerationValue.value      The integer value associated with this name.
+
+idl.Aggregate
+
+ Base class for type-Classes which contain a number of other types
+ (e.g. structs and unions).
+
+ The contained types are available in the list Aggregate.fields. Each
+ element in the list is of type idl.Field representing a member of the
+ aggregate.
+
+ Each field has the following properties:
+
+  Field.type     The type of the member (a idl.Type).
+  Field.name     The name of the member (can be None for anonymous
+                 fields).
+  Field.const    Boolean, true if the member is const.
+  Field.init_val The initialisation value for this field. Takes
+                 precendence over both Field.type.init_val and
+                 Field.type.init_fn.
+
+idl.Struct
+
+ A subclass of idl.Aggregate representing the C struct type.
+
+ Struct.kind == "struct"
+
+idl.Union
+
+ A subclass of idl.Aggregate representing the C union type.
+
+ Union.kind == "union"
+
+idl.KeyedUnion
+
+ A subclass of idl.Aggregate which represents the C union type
+ where the currently valid member of the union can be determined based
+ upon another member in the containing type. An idl.KeyedUnion must
+ always be a member of a containing idl.Aggregate type.
+
+ The KeyedUnion.keyvar contains an idl.Field, this is the member of
+ the containing type which determines the valid member of the
+ union. The idl.Field.type of the keyvar must be an Enumeration type.
+
+idl.Array
+
+  A class representing an array of similar elements. An idl.Array must
+  always be an idl.Field of a containing idl.Aggregate.
+
+  idl.Array.elem_type contains an idl.Type which is the type of each
+  element of the array.
+
+  idl.Array.len_var contains an idl.Field which is added to the parent
+  idl.Aggregate and will contain the length of the array. The field
+  MUST be named num_ARRAYNAME.
+
+Standard Types
+--------------
+
+Several standard types a predefined. They are
+
+void                    (void pointer type)
+bool
+size_t
+integer                 24 bit signed integer.
+
+uint{8,16,32,64}        uint{8,16,32,64}_t
+
+domid                   Domain ID
+
+string                  NULL terminated string
diff --git a/tools/libs/light/include/libxl.h b/tools/libs/light/include/libxl.h
new file mode 100644 (file)
index 0000000..1ea5b4f
--- /dev/null
@@ -0,0 +1,2732 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+/*
+ * libxl API compatibility
+ *
+ * From Xen 4.2 onwards the API of libxl will be maintained in a
+ * stable manner. This means that it should be possible to write an
+ * application against the API provided by libxl in Xen 4.2 and expect
+ * that it will continue to compile against future versions of Xen
+ * without source modification.
+ *
+ * In order to make such compatibility possible it is required that
+ * application which want to be exposed to a particular API #define
+ * LIBXL_API_VERSION before including libxl.h or any other libxl
+ * header. The syntax of the LIBXL_API_VERSION is:
+ *    0xVVSSEE
+ * where ($(XEN_xxx) from xen/Makefile):
+ *   VV is the Xen major release number, $(XEN_VERSION)
+ *   SS is the Xen sub version number, $(XEN_SUBVERSION)
+ *   EE is the Xen extra version digit, first numeric part of
+ *     $(XEN_EXTRAVERSION) not including the leading "."
+ * For example the first stable API version, supported by Xen 4.2.0,
+ * is 0x040200.
+ *
+ * Lack of LIBXL_API_VERSION means "the latest" which will
+ * change. Specifying an unknown LIBXL_API_VERSION will result in a
+ * compile time error.
+ *
+ * Identical versions of the libxl API will represented by the version
+ * containing the earliest instance of that API. e.g. if 4.2.0 and
+ * 4.3.0 contain an identical libxl API then only LIBXL_API_VERSION
+ * 0x040200 will be valid.
+ *
+ * We will try especially hard to avoid changing the API during a
+ * stable series, i.e. it should be unusual for the last byte of
+ * LIBXL_API_VERSION to be non-zero.
+ *
+ * In the event that a change is required which cannot be made
+ * backwards compatible in this manner a #define of the form
+ * LIBXL_HAVE_<interface> will always be added in order to make it
+ * possible to write applications which build against any version of
+ * libxl. Such changes are expected to be exceptional and used as a
+ * last resort. The barrier for backporting such a change to a stable
+ * branch will be very high.
+ *
+ * These guarantees apply only to stable releases of Xen. When an
+ * incompatible change is made in the unstable tree then
+ * LIBXL_API_VERSION will be bumped to the next expected stable
+ * release number on the first such change only. Applications which
+ * want to support building against Xen unstable are expected to track
+ * API changes in that tree until it is released as a stable release.
+ *
+ * API compatibility will be maintained for all versions of Xen using
+ * the same $(XEN_VERSION) (e.g. throughout a major release).
+ */
+
+/* LIBXL_HAVE_PHYSINFO_CAP_PV
+ *
+ * If this is defined, libxl_physinfo has a "cap_pv" field.
+ */
+#define LIBXL_HAVE_PHYSINFO_CAP_PV 1
+
+/* LIBXL_HAVE_CONSOLE_NOTIFY_FD
+ *
+ * If this is defined, libxl_console_exec and
+ * libxl_primary_console_exe take a notify_fd parameter. That
+ * parameter will be used to notify the caller that the console is connected.
+ */
+#define LIBXL_HAVE_CONSOLE_NOTIFY_FD 1
+
+/* LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS
+ *
+ * If this is defined, the copy functions have constified src parameter and the
+ * length functions accept constified parameter.
+ */
+#define LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS 1
+
+/* LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO
+ *
+ * If this is defined, libxl_domain_need_memory no longer modifies
+ * the b_info paseed in.
+ */
+#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO 1
+
+/* LIBXL_HAVE_VNUMA
+ *
+ * If this is defined the type libxl_vnode_info exists, and a
+ * field 'vnuma_nodes' is present in libxl_domain_build_info.
+ */
+#define LIBXL_HAVE_VNUMA 1
+
+/* LIBXL_HAVE_USERDATA_UNLINK
+ *
+ * If it is defined, libxl has a library function called
+ * libxl_userdata_unlink.
+ */
+#define LIBXL_HAVE_USERDATA_UNLINK 1
+
+/* LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID
+ *
+ * If this is defined, libxl has a library function called
+ * libxl_cpupool_qualifier_to_cpupoolid, which takes in a CPU pool
+ * qualifier in the form of number or string, then returns the ID of
+ * that CPU pool.
+ */
+#define LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID 1
+
+/* LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP
+ *
+ * If this is defined, libxl has two library functions called
+ * libxl_cpupool_cpuadd_cpumap and libxl_cpupool_cpuremove_cpumap,
+ * which allow to add to or remove from a cpupool all the cpus
+ * specified in a bitmap.
+ */
+#define LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP 1
+
+/*
+ *
+ * LIBXL_HAVE_BITMAP_AND_OR
+ *
+ * If this is defined, libxl has two library functions, libxl_bitmap_and
+ * and libxl_bitmap_or to compute the logical and and or of two bitmaps
+ */
+#define LIBXL_HAVE_BITMAP_AND_OR 1
+
+/*
+ * LIBXL_HAVE_FIRMWARE_PASSTHROUGH indicates the feature for
+ * passing in SMBIOS and ACPI firmware to HVM guests is present
+ * in the library.
+ */
+#define LIBXL_HAVE_FIRMWARE_PASSTHROUGH 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_NODEAFFINITY indicates that a 'nodemap' field
+ * (of libxl_bitmap type) is present in libxl_domain_build_info,
+ * containing the node-affinity for the domain.
+ */
+#define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1
+
+/*
+ * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices
+ * through pvusb -- both hotplug and at domain creation time..
+ */
+#define LIBXL_HAVE_PVUSB 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the
+ * libxl_vendor_device field is present in the hvm sections of
+ * libxl_domain_build_info. This field tells libxl which
+ * flavour of xen-pvdevice to enable in QEMU.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE 1
+
+/*
+ * The libxl_domain_build_info has the event_channels field.
+ */
+#define LIBXL_HAVE_BUILDINFO_EVENT_CHANNELS 1
+
+/*
+ * libxl_domain_build_info has the u.hvm.ms_vm_genid field.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_MS_VM_GENID 1
+
+/*
+ * LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY indicates that a 'cpumap_soft'
+ * field (of libxl_bitmap type) is present in libxl_vcpuinfo,
+ * containing the soft affinity of a vcpu.
+ */
+#define LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY 1
+
+/*
+ * LIBXL_HAVE_SET_VCPUAFFINITY_FORCE indicates that the
+ * libxl_set_vcpuaffinity_force() library call is available.
+ */
+#define LIBXL_HAVE_SET_VCPUAFFINITY_FORCE 1
+
+/*
+ * LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE indicates that a
+ * 'direct_io_safe' field (of boolean type) is present in
+ * libxl_device_disk.
+ */
+#define LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE 1
+
+/*
+ * The libxl_device_disk has the discard_enable field.
+ */
+#define LIBXL_HAVE_LIBXL_DEVICE_DISK_DISCARD_ENABLE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN indicates that it is possible
+ * to specify the start guest frame number used to map a range of I/O
+ * memory machine frame numbers via the 'gfn' field (of type uint64)
+ * of the 'iomem' structure. An array of iomem structures is embedded
+ * in libxl_domain_build_info and used to map the indicated memory
+ * ranges during domain build.
+ */
+#define LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN 1
+
+/*
+ * LIBXL_HAVE_SCHED_RTDS indicates that the RTDS real time scheduler
+ * is available. A 'budget' field added in libxl_domain_sched_params.
+ */
+#define LIBXL_HAVE_SCHED_RTDS 1
+
+/*
+ * LIBXL_HAVE_SCHED_NULL indicates that the 'null' static scheduler
+ * is available.
+ */
+#define LIBXL_HAVE_SCHED_NULL 1
+
+/*
+ * libxl_domain_build_info has u.hvm.viridian_enable and _disable bitmaps
+ * of the specified width.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE 1
+#define LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH 64
+
+/*
+ * libxl_domain_build_info has the u.hvm.mmio_hole_memkb field.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_MMIO_HOLE_MEMKB 1
+
+/*
+ * libxl_domain_info returns ERROR_DOMAIN_NOTFOUND if the domain
+ * is not present, instead of ERROR_INVAL.
+ */
+#define LIBXL_HAVE_ERROR_DOMAIN_NOTFOUND 1
+
+/*
+ * libxl_domain_build_info has device_tree and libxl_device_dtdev
+ * exists. This mean Device Tree passthrough is supported for ARM
+ */
+#define LIBXL_HAVE_DEVICETREE_PASSTHROUGH 1
+
+/*
+ * libxl_domain_build_info has device_model_user to specify the user to
+ * run the device model with. See docs/misc/qemu-deprivilege.txt.
+ */
+#define LIBXL_HAVE_DEVICE_MODEL_USER 1
+
+/*
+ * libxl_vcpu_sched_params is used to store per-vcpu params.
+ */
+#define LIBXL_HAVE_VCPU_SCHED_PARAMS 1
+
+/*
+ * LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS indicates RTDS scheduler
+ * now supports per-vcpu settings.
+ */
+#define LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS 1
+
+/*
+ * LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA indicates RTDS scheduler
+ * now supports per-vcpu extratime settings.
+ */
+#define LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA 1
+
+/*
+ * libxl_domain_build_info has the arm.gic_version field.
+ */
+#define LIBXL_HAVE_BUILDINFO_ARM_GIC_VERSION 1
+
+/*
+ * libxl_domain_build_info has the arch_arm.tee field.
+ */
+#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_TEE 1
+
+/*
+ * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
+ * 'soft reset' for domains and there is 'soft_reset' shutdown reason
+ * in enum libxl_shutdown_reason.
+ */
+#define LIBXL_HAVE_SOFT_RESET 1
+
+/*
+ * LIBXL_HAVE_APIC_ASSIST indicates that the 'apic_assist' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_APIC_ASSIST 1
+
+/*
+ * LIBXL_HAVE_BUILD_ID means that libxl_version_info has the extra
+ * field for the hypervisor build_id.
+ */
+#define LIBXL_HAVE_BUILD_ID 1
+
+/*
+ * LIBXL_HAVE_QEMU_MONITOR_COMMAND indiactes the availability of the
+ * libxl_qemu_monitor_command() function.
+ */
+#define LIBXL_HAVE_QEMU_MONITOR_COMMAND 1
+
+/*
+ * LIBXL_HAVE_SCHED_CREDIT2_PARAMS indicates the existance of a
+ * libxl_sched_credit2_params structure, containing Credit2 scheduler
+ * wide parameters (i.e., the ratelimiting value).
+ */
+#define LIBXL_HAVE_SCHED_CREDIT2_PARAMS 1
+
+/*
+ * LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY indicates that there is a field
+ * in libxl_sched_credit_params called vcpu_migr_delay_us which controls
+ * the resistance of the vCPUs of the cpupool to migrations among pCPUs.
+ */
+#define LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_CRASH_CTL indicates that the 'crash_ctl' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_CRASH_CTL 1
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_SYNIC indicates that the 'synic' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_SYNIC 1
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_STIMER indicates that the 'stimer' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_STIMER 1
+
+/*
+ * LIBXL_HAVE_VIRIDIAN_HCALL_IPI indicates that the 'hcall_ipi' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_HCALL_IPI 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that
+ * libxl_domain_build_info has the u.hvm.acpi_laptop_slate field.
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE 1
+
+/*
+ * LIBXL_HAVE_P9S indicates that the p9 field in IDL has been changed to p9s
+ */
+#define LIBXL_HAVE_P9S 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_ARM_VUART indicates that the toolstack supports virtual UART
+ * for ARM.
+ */
+#define LIBXL_HAVE_BUILDINFO_ARM_VUART 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_GRANT_LIMITS indicates that libxl_domain_build_info
+ * has the max_grant_frames and max_maptrack_frames fields.
+ */
+#define LIBXL_HAVE_BUILDINFO_GRANT_LIMITS 1
+
+#define LIBXL_MAX_GRANT_DEFAULT (~(uint32_t)0)
+#define LIBXL_MAX_GRANT_FRAMES_DEFAULT 32 /* deprecated */
+#define LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT 1024 /* deprecated */
+/*
+ * LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT indicates that the default
+ * values of max_grant_frames and max_maptrack_frames fields in
+ * libxl_domain_build_info are the special sentinel value
+ * LIBXL_MAX_GRANT_DEFAULT rather than the fixed values above.
+ * This means to use the hypervisor's default.
+ */
+#define LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_* indicates that libxl_domain_build_info has
+ * the field represented by the '*'. The original position of those
+ * fields is:
+ *  - u.hvm.timer_mode
+ *  - u.hvm.apic
+ *  - u.hvm.nested_hvm
+ *  - u.pv.bootloader
+ *  - u.pv.bootloader_args
+ */
+#define LIBXL_HAVE_BUILDINFO_TIMER_MODE 1
+#define LIBXL_HAVE_BUILDINFO_APIC 1
+#define LIBXL_HAVE_BUILDINFO_NESTED_HVM 1
+#define LIBXL_HAVE_BUILDINFO_BOOTLOADER 1
+#define LIBXL_HAVE_BUILDINFO_BOOTLOADER_ARGS 1
+
+/*
+ * LIBXL_HAVE_EXTENDED_VKB indicates that libxl_device_vkb has extended fields:
+ *  - unique_id;
+ *  - feature_disable_keyboard;
+ *  - feature_disable_pointer;
+ *  - feature_abs_pointer;
+ *  - feature_raw_pointer;
+ *  - feature_multi_touch;
+ *  - width;
+ *  - height;
+ *  - multi_touch_width;
+ *  - multi_touch_height;
+ *  - multi_touch_num_contacts.
+ */
+#define LIBXL_HAVE_EXTENDED_VKB 1
+
+/*
+ * LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW indicates that libxl_physinfo has
+ * cap_hap and cap_shadow fields reflecting the hardware and Xen availability
+ * of Hardware Assisted, and Shadow paging support.
+ */
+#define LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW 1
+
+/*
+ * LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE indicates that libxl_physinfo
+ * has a cap_iommu_hap_pt_share field that indicates whether the hardware
+ * supports sharing the IOMMU and HAP page tables.
+ */
+#define LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB indicates thate libxl_domain_build_info
+ * has an iommu_memkb field which should be set with the amount of memory
+ * overhead needed by the domain for populating IOMMU page tables.
+ */
+#define LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB 1
+
+/*
+ * LIBXL_HAVE_CREATEINFO_PASSTHROUGH indicates that
+ * libxl_domain_create_info has a passthrough field (which is a
+ * libxl_passthrough enumeration) that indicates whether device pass-
+ * through is enabled for the domain and, if so, whether the IOMMU and
+ * HAP page tables may be shared or not.
+ */
+#define LIBXL_HAVE_CREATEINFO_PASSTHROUGH 1
+
+/*
+ * LIBXL_HAVE_DISK_SAFE_REMOVE indicates that the
+ * libxl_device_disk_safe_remove() function is defined.
+ */
+#define LIBXL_HAVE_DISK_SAFE_REMOVE 1
+
+/*
+ * libxl ABI compatibility
+ *
+ * The only guarantee which libxl makes regarding ABI compatibility
+ * across releases is that the SONAME will always be bumped whenever
+ * the ABI is changed in an incompatible way.
+ *
+ * This applies within stable branches as well as
+ * development branches. It is possible that a new stable release of
+ * Xen may require a rebuild of applications using the
+ * library. However per the API compatibility gaurantees such a
+ * rebuild should not normally require any source level changes.
+ *
+ * As with the API compatiblity the SONAME will only be bumped for the
+ * first ABI incompatible change in a development branch.
+ */
+
+/*
+ * libxl memory management
+ *
+ * From the point of view of the application (ie, libxl's caller),
+ * struct libxl_ctx* is threadsafe, and all returned allocated
+ * structures are obtained from malloc(), and must be freed by the
+ * caller either directly or by calling an appropriate free function
+ * provided by libxl.  Ie the application does not get automatic
+ * assistance from libxl in managing these allocations.
+ *
+ * Specific details are in the header comments which should be found
+ * in libxl.h or libxlutil.h, next to the relevant function
+ * declarations.
+ *
+ * Internally, libxl has a garbage collection scheme which allows much libxl
+ * code to allocate strings etc. for internal use without needing to
+ * free them.  These are called "temporary allocations".
+ *
+ * The pool for these temporary allocations, along with any other
+ * thread-specific data which is private to libxl but shared between
+ * libxl functions (such as the current xenstore transaction), is
+ * stored in the "gc context" which is a special enhanced context
+ * structure allocated automatically by convenience macros at every
+ * entry to libxl.
+ *
+ * Every libxl function falls into one of these categories:
+ *
+ * 1. Public functions (declared in libxl.h, libxlutil.h), which may
+ *    be called by libxl applications.  If a public function returns
+ *    any allocated object to its caller, that object must have come
+ *    from malloc.
+ *
+ *    The definitions of public functions MUST use the gc context
+ *    initialisation macros (or do the equivalent work themselves).
+ *    These macros will ensure that all temporary allocations will be
+ *    automatically freed before the function returns to its caller.
+ *
+ *    A public function may be called from within libxl; the call
+ *    context initialisation macros will make sure that the internal
+ *    caller's context is reused (eg, so that the same xenstore
+ *    transaction is used).  But in-libxl callers of libxl public
+ *    functions should note that any libxl public function may cause
+ *    recursively reentry into libxl via the application's event
+ *    callback hook.
+ *
+ *    Public functions have names like libxl_foobar.
+ *
+ * 2. Private functions, which may not be called by libxl
+ *    applications; they are not declared in libxl.h or libxlutil.h
+ *    and they may not be called other than by other libxl functions.
+ *
+ *    Private functions should not use the gc context initialisation
+ *    macros.
+ *
+ *    Private functions have names like libxl__foobar (NB, two underscores).
+ *    Also the declaration of such functions must be preceeded by the _hidden
+ *    macro.
+ *
+ * Allocations made by a libxl function fall into one of the following
+ * categories (where "object" includes any memory allocation):
+ *
+ * (a) Objects which are not returned to the function's caller.
+ *     These should be allocated from the temporary pool.
+ *
+ * (b) Objects which are intended for return to the calling
+ *     application.  This includes all allocated objects returned by
+ *     any public function.
+ *
+ *     It may also include objects allocated by an internal function
+ *     specifically for eventual return by the function's external
+ *     callers, but this situation should be clearly documented in
+ *     comments.
+ *
+ *     These should be allocated from malloc() et al. and comments
+ *     near the function declaration should explain the memory
+ *     ownership.  If a simple free() by the application is not
+ *     sufficient, a suitable public freeing function should be
+ *     provided.
+ *
+ * (c) Internal objects whose size and/or lifetime dictate explicit
+ *     memory management within libxl.  This includes objects which
+ *     will be embedded in opaque structures which will be returned to
+ *     the libxl caller (more generally, any internal object whose
+ *     lifetime exceeds the libxl entrypoint which creates it) and
+ *     objects which are so large or numerous that explicit memory
+ *     management is required.
+ *
+ *     These should be allocated from malloc() et al., and freed
+ *     explicitly at the appropriate point.  The situation should be
+ *     documented in comments.
+ *
+ * (d) Objects which are allocated by internal-only functions and
+ *     returned to the function's (therefore, internal) caller but are
+ *     strictly for internal use by other parts of libxl.  These
+ *     should be allocated from the temporary pool.
+ *
+ *     Where a function's primary purpose is to return such an object,
+ *     it should have a libxl__gc * as it's first argument.
+ *
+ *     Note that there are two ways to change an allocation from this
+ *     category to the "public" category. Either the implementation
+ *     is kept internal and a wrapper function duplicates all memory
+ *     allocations so that they are suitable for return to external
+ *     callers or the implementation uses plain malloc() et al calls
+ *     and an internal wrapper adds the relevant pointers to the gc.
+ *     The latter method is preferred for obvious performance reasons.
+ *
+ * No temporary objects allocated from the pool may be explicitly freed.
+ * Therefore public functions which initialize a libxl__gc MUST call
+ * libxl__free_all() before returning.
+ *
+ * Memory allocation failures are not handled gracefully.  If malloc
+ * (or realloc) fails, libxl will cause the entire process to print
+ * a message to stderr and exit with status 255.
+ */
+/*
+ * libxl types
+ *
+ * Most libxl types are defined by the libxl IDL (see
+ * libxl_types.idl). The library provides a common set of methods for
+ * initialising and freeing these types.
+ *
+ * IDL-generated libxl types should be used as follows: the user must
+ * always call the "init" function before using a type, even if the
+ * variable is simply being passed by reference as an out parameter
+ * to a libxl function.  The user must always calls "dispose" exactly
+ * once afterwards, to clean up, regardless of whether operations on
+ * this object succeeded or failed.  See the xl code for examples.
+ *
+ * "init" and "dispose" are idempotent.
+ *
+ * void libxl_<type>_init(<type> *p):
+ *
+ *    Initialises the members of "p" to all defaults. These may either
+ *    be special value which indicates to the library that it should
+ *    select an appropriate default when using this field or actual
+ *    default values.
+ *
+ *    Some fields within a data type (e.g. unions) cannot be sensibly
+ *    initialised without further information. In these cases a
+ *    separate subfield initialisation function is provided (see
+ *    below).
+ *
+ *    An instance which has been initialised using this method can
+ *    always be safely passed to the dispose function (see
+ *    below). This is true even if the data type contains fields which
+ *    require a separate call to a subfield initialisation function.
+ *
+ *    This method is provided for any aggregate type which is used as
+ *    an input parameter.
+ *
+ * void libxl_<type>_init_<subfield>(<type> *p, subfield):
+ *
+ *    Initialise those parts of "p" which are not initialised by the
+ *    main init function due to the unknown value of "subfield". Sets
+ *    p->subfield as well as initialising any fields to their default
+ *    values.
+ *
+ *    p->subfield must not have been previously initialised.
+ *
+ *    This method is provided for any aggregate type.
+ *
+ * void libxl_<type>_dispose(instance *p):
+ *
+ *    Frees any dynamically allocated memory used by the members of
+ *    "p" but not the storage used by "p" itself (this allows for the
+ *    allocation of arrays of types and for the composition of types).
+ *
+ * char *libxl_<type>_to_json(instance *p)
+ *
+ *    Generates a JSON object from "p" in the form of a NULL terminated
+ *    string.
+ *
+ * <type *> libxl_<type>_from_json(const char *json)
+ * int      libxl_<type>_from_json(const char *json)
+ *
+ *    Parses "json" and returns:
+ *
+ *    an int value, if <type> is enumeration type. The value is the enum value
+ *    representing the respective string in "json".
+ *
+ *    an instance of <type>, if <type> is aggregate type. The returned
+ *    instance has its fields filled in by the parser according to "json".
+ *
+ *    If the parsing fails, caller cannot rely on the value / instance
+ *    returned.
+ */
+#ifndef LIBXL_H
+#define LIBXL_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/wait.h> /* for pid_t */
+
+#include <xentoollog.h>
+
+typedef struct libxl__ctx libxl_ctx;
+
+#include <libxl_uuid.h>
+#include <_libxl_list.h>
+
+/* API compatibility. */
+#ifdef LIBXL_API_VERSION
+#if LIBXL_API_VERSION != 0x040200 && LIBXL_API_VERSION != 0x040300 && \
+    LIBXL_API_VERSION != 0x040400 && LIBXL_API_VERSION != 0x040500 && \
+    LIBXL_API_VERSION != 0x040700 && LIBXL_API_VERSION != 0x040800 && \
+    LIBXL_API_VERSION != 0x041300 && LIBXL_API_VERSION != 0x041400
+#error Unknown LIBXL_API_VERSION
+#endif
+#endif
+
+/* LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION
+ *
+ * If this is defined we have libxl_retrieve_domain_configuration which
+ * returns the current configuration of a domain, which can be used to
+ * rebuild a domain.
+ */
+#define LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain two arrays of libxl_bitmap-s, with all the necessary information
+ * to set the hard affinity (vcpu_hard_affinity) and the soft affinity
+ * (vcpu_soft_affinity) of the VCPUs.
+ *
+ * Note that, if the vcpu_hard_affinity array is used, libxl will ignore
+ * the content of the cpumap field of libxl_domain_build_info. That is to
+ * say, if the array is allocated and used by the caller, it is it and
+ * only it that determines the hard affinity of the domain's VCPUs.
+ *
+ * The number of libxl_bitmap-s in the arrays should be equal to the
+ * maximum number of VCPUs of the domain. If there only are N elements in
+ * an array, with N smaller the the maximum number of VCPUs, the hard or
+ * soft affinity (depending on which array we are talking about) will be
+ * set only for the first N VCPUs. The other VCPUs will just have affinity,
+ * both hard and soft, with all the host PCPUs.
+ * Each bitmap should be big enough to accommodate the maximum number of
+ * PCPUs of the host.
+ */
+#define LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_VKB_DEVICE
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain a boolean hvm.vkb_device which instructs libxl whether to include
+ * a vkbd at build time or not.
+ */
+#define LIBXL_HAVE_BUILDINFO_VKB_DEVICE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain hvm.usbdevice_list, a libxl_string_list type that contains
+ * a list of USB devices to specify on the qemu command-line.
+ *
+ * If it is set, callers may use either hvm.usbdevice or
+ * hvm.usbdevice_list, but not both; if both are set, libxl will
+ * throw an error.
+ *
+ * If this is not defined, callers can only use hvm.usbdevice.  Note
+ * that this means only one device can be added at domain build time.
+ */
+#define LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_USBVERSION
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain hvm.usbversion, a integer type that contains a USB
+ * controller version to specify on the qemu upstream command-line.
+ *
+ * If it is set, callers may use hvm.usbversion to specify if the usb
+ * controller is usb1, usb2 or usb3.
+ *
+ * If this is not defined, the hvm.usbversion field does not exist.
+ */
+#define LIBXL_HAVE_BUILDINFO_USBVERSION 1
+
+/*
+ * LIBXL_HAVE_DEVICE_BACKEND_DOMNAME
+ *
+ * If this is defined, libxl_device_* structures containing a backend_domid
+ * field also contain a backend_domname field.  If backend_domname is set, it is
+ * resolved to a domain ID when the device is used and takes precedence over the
+ * backend_domid field.
+ *
+ * If this is not defined, the backend_domname field does not exist.
+ */
+#define LIBXL_HAVE_DEVICE_BACKEND_DOMNAME 1
+
+/*
+ * LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
+ *
+ * This argument was erroneously "const" in the 4.2 release despite
+ * the requirement for the callback to free the event.
+ */
+#if LIBXL_API_VERSION != 0x040200
+#define LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG 1
+#endif
+
+/*
+ * LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
+ *
+ * The return value of libxl_basename is malloc'ed but the erroneously
+ * marked as "const" in releases before 4.5.
+ */
+#if !defined(LIBXL_API_VERSION) || LIBXL_API_VERSION >= 0x040500
+#define LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE 1
+#endif
+
+/*
+ * LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES
+ *
+ * If this is defined, libxl_physinfo structure will contain an uint64 field
+ * called outstanding_pages, containing the number of pages claimed but not
+ * yet allocated for all domains.
+ */
+#define LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES 1
+
+/*
+ * LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN
+ *
+ * If this is defined, libxl_physinfo structure will contain an uint64 field
+ * called max_possible_mfn, containing the highest possible mfn on this host,
+ * possibly taking memory hotplug into account.
+ */
+#define LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN 1
+
+/*
+ * LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
+ *
+ * If this is defined, libxl_dominfo will contain a MemKB type field called
+ * outstanding_memkb, containing the amount of claimed but not yet allocated
+ * memory for a specific domain.
+ */
+#define LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
+
+/*
+ * LIBXL_HAVE_DOMINFO_NEVER_STOP
+ *
+ * If this is defined, libxl_dominfo will contain a flag called never_stop
+ * indicating that the specific domain should never be stopped by the
+ * toolstack.
+ */
+#define LIBXL_HAVE_DOMINFO_NEVER_STOP 1
+
+/*
+ * LIBXL_HAVE_QXL
+ *
+ * If defined, then the libxl_vga_interface_type will contain another value:
+ * "QXL". This value define if qxl vga is supported.
+ *
+ * If this is not defined, the qxl vga support is missed.
+ */
+#define LIBXL_HAVE_QXL 1
+
+/*
+ * LIBXL_HAVE_SPICE_VDAGENT
+ *
+ * If defined, then the libxl_spice_info structure will contain a boolean type:
+ * vdagent and clipboard_sharing. These values define if Spice vdagent and
+ * clipboard sharing are enabled.
+ *
+ * If this is not defined, the Spice vdagent support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_VDAGENT 1
+
+/*
+ * LIBXL_HAVE_SPICE_USBREDIRECTION
+ *
+ * If defined, then the libxl_spice_info structure will contain an integer type
+ * field: usbredirection. This value defines if Spice usbredirection is enabled
+ * and with how much channels.
+ *
+ * If this is not defined, the Spice usbredirection support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_USBREDIREDIRECTION 1
+
+/*
+ * LIBXL_HAVE_SPICE_IMAGECOMPRESSION
+ *
+ * If defined, then the libxl_spice_info structure will contain a string type
+ * field: image_compression. This value defines what Spice image compression
+ * is used.
+ *
+ * If this is not defined, the Spice image compression setting support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_IMAGECOMPRESSION 1
+
+/*
+ * LIBXL_HAVE_SPICE_STREAMINGVIDEO
+ *
+ * If defined, then the libxl_spice_info structure will contain a string type
+ * field: streaming_video. This value defines what Spice streaming video setting
+ * is used.
+ *
+ * If this is not defined, the Spice streaming video setting support is ignored.
+ */
+#define LIBXL_HAVE_SPICE_STREAMINGVIDEO 1
+
+/*
+ * LIBXL_HAVE_HVM_HDTYPE
+ *
+ * If defined, then the u.hvm structure will contain a enum type
+ * hdtype.
+ */
+#define LIBXL_HAVE_HVM_HDTYPE 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
+ *
+ * If this is defined, libxl_domain_create_restore()'s API has changed to
+ * include a params structure.
+ */
+#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
+ *
+ * If this is defined, libxl_domain_create_restore()'s API includes the
+ * send_back_fd param. This is used only with COLO, for the libxl migration
+ * back channel; other callers should pass -1.
+ */
+#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
+
+/*
+ * LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
+ *
+ * If this is defined, libxl_domain_create_info contains a driver_domain
+ * field that can be used to tell libxl that the domain that is going
+ * to be created is a driver domain, so the necessary actions are taken.
+ */
+#define LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
+
+/*
+ * LIBXL_HAVE_SIGCHLD_SELECTIVE_REAP
+ *
+ * If this is defined:
+ *
+ * Firstly, the enum libxl_sigchld_owner (in libxl_event.h) has the
+ * value libxl_sigchld_owner_libxl_always_selective_reap which may be
+ * passed to libxl_childproc_setmode in hooks->chldmode.
+ *
+ * Secondly, the function libxl_childproc_sigchld_occurred exists.
+ */
+#define LIBXL_HAVE_SIGCHLD_OWNER_SELECTIVE_REAP 1
+
+/*
+ * LIBXL_HAVE_SIGCHLD_SHARING
+ *
+ * If this is defined, it is permissible for multiple libxl ctxs
+ * to simultaneously "own" SIGCHLD.  See "Subprocess handling"
+ * in libxl_event.h.
+ */
+#define LIBXL_HAVE_SIGCHLD_SHARING 1
+
+/*
+ * LIBXL_HAVE_NO_SUSPEND_RESUME
+ *
+ * Is this is defined then the platform has no support for saving,
+ * restoring or migrating a domain. In this case the related functions
+ * should be expected to return failure. That is:
+ *  - libxl_domain_suspend
+ *  - libxl_domain_resume
+ *  - libxl_domain_remus_start
+ */
+#if defined(__arm__) || defined(__aarch64__)
+#define LIBXL_HAVE_NO_SUSPEND_RESUME 1
+#endif
+
+/*
+ * LIBXL_HAVE_DOMAIN_SUSPEND_ONLY
+ *
+ * If this is defined, function libxl_domains_suspend_only() is available.
+ */
+
+#define LIBXL_HAVE_DOMAIN_SUSPEND_ONLY 1
+
+/*
+ * LIBXL_HAVE_DEVICE_PCI_SEIZE
+ *
+ * If this is defined, then the libxl_device_pci struct will contain
+ * the "seize" boolean field.  If this field is set, libxl_pci_add will
+ * check to see if the device is currently assigned to pciback, and if not,
+ * it will attempt to do so (unbinding the device from the existing driver).
+ */
+#define LIBXL_HAVE_DEVICE_PCI_SEIZE 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_KERNEL
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain 'kernel', 'ramdisk', 'cmdline' fields. 'kernel' is a string
+ * to indicate kernel image location, 'ramdisk' is a string to indicate
+ * ramdisk location, 'cmdline' is a string to indicate the paramters which
+ * would be appended to kernel image.
+ *
+ * Both PV guest and HVM guest can use these fields for direct kernel boot.
+ * But for compatibility reason, u.pv.kernel, u.pv.ramdisk and u.pv.cmdline
+ * still exist.
+ */
+#define LIBXL_HAVE_BUILDINFO_KERNEL 1
+
+/*
+ * LIBXL_HAVE_DEVICE_CHANNEL
+ *
+ * If this is defined, then the libxl_device_channel struct exists
+ * and channels can be attached to a domain. Channels manifest as consoles
+ * with names, see docs/misc/console.txt.
+ */
+#define LIBXL_HAVE_DEVICE_CHANNEL 1
+
+/*
+ * LIBXL_HAVE_AO_ABORT indicates the availability of libxl_ao_abort
+ */
+#define LIBXL_HAVE_AO_ABORT 1
+
+/* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be
+ * called from within libxl itself. Callers outside libxl, who
+ * do not #include libxl_internal.h, are fine. */
+#ifndef LIBXL_EXTERNAL_CALLERS_ONLY
+#define LIBXL_EXTERNAL_CALLERS_ONLY /* disappears for callers outside libxl */
+#endif
+
+/*
+ *  LIBXL_HAVE_UUID_COPY_CTX_PARAM
+ *
+ * If this is defined, libxl_uuid_copy has changed to take a libxl_ctx
+ * structure.
+ */
+#define LIBXL_HAVE_UUID_COPY_CTX_PARAM 1
+
+/*
+ * LIBXL_HAVE_SSID_LABEL
+ *
+ * If this is defined, then libxl IDL contains string of XSM security
+ * label in all XSM related structures.
+ *
+ * If set this string takes precedence over the numeric field.
+ */
+#define LIBXL_HAVE_SSID_LABEL 1
+
+/*
+ * LIBXL_HAVE_CPUPOOL_NAME
+ *
+ * If this is defined, then libxl IDL contains string of CPU pool
+ * name in all CPU pool related structures.
+ *
+ * If set this string takes precedence over the numeric field.
+ */
+#define LIBXL_HAVE_CPUPOOL_NAME 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_SERIAL_LIST
+ *
+ * If this is defined, then the libxl_domain_build_info structure will
+ * contain hvm.serial_list, a libxl_string_list type that contains
+ * a list of serial ports to specify on the qemu command-line.
+ *
+ * If it is set, callers may use either hvm.serial or
+ * hvm.serial_list, but not both; if both are set, libxl will
+ * throw an error.
+ *
+ * If this is not defined, callers can only use hvm.serial.  Note
+ * that this means only one serial port can be added at domain build time.
+ */
+#define LIBXL_HAVE_BUILDINFO_SERIAL_LIST 1
+
+/*
+ * LIBXL_HAVE_ALTP2M
+ * If this is defined, then libxl supports alternate p2m functionality.
+ */
+#define LIBXL_HAVE_ALTP2M 1
+
+/*
+ * LIBXL_HAVE_REMUS
+ * If this is defined, then libxl supports remus.
+ */
+#define LIBXL_HAVE_REMUS 1
+
+/*
+ * LIBXL_HAVE_COLO_USERSPACE_PROXY
+ * If this is defined, then libxl supports COLO userspace proxy.
+ */
+#define LIBXL_HAVE_COLO_USERSPACE_PROXY 1
+
+typedef uint8_t libxl_mac[6];
+#define LIBXL_MAC_FMT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
+#define LIBXL_MAC_FMTLEN ((2*6)+5) /* 6 hex bytes plus 5 colons */
+#define LIBXL_MAC_BYTES(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
+void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src);
+
+#if defined(__i386__) || defined(__x86_64__)
+/*
+ * LIBXL_HAVE_PSR_CMT
+ *
+ * If this is defined, the Cache Monitoring Technology feature is supported.
+ */
+#define LIBXL_HAVE_PSR_CMT 1
+
+/*
+ * LIBXL_HAVE_PSR_MBM
+ *
+ * If this is defined, the Memory Bandwidth Monitoring feature is supported.
+ */
+#define LIBXL_HAVE_PSR_MBM 1
+
+/*
+ * LIBXL_HAVE_PSR_CAT
+ *
+ * If this is defined, the Cache Allocation Technology feature is supported.
+ */
+#define LIBXL_HAVE_PSR_CAT 1
+
+/*
+ * LIBXL_HAVE_PSR_CDP
+ *
+ * If this is defined, the Code and Data Prioritization feature is supported.
+ */
+#define LIBXL_HAVE_PSR_CDP 1
+
+/*
+ * LIBXL_HAVE_PSR_L2_CAT
+ *
+ * If this is defined, the L2 Cache Allocation Technology feature is supported.
+ */
+#define LIBXL_HAVE_PSR_L2_CAT 1
+
+/*
+ * LIBXL_HAVE_PSR_GENERIC
+ *
+ * If this is defined, the Memory Bandwidth Allocation feature is supported.
+ * The following public functions are available:
+ *   libxl_psr_{set/get}_val
+ *   libxl_psr_get_hw_info
+ *   libxl_psr_hw_info_list_free
+ */
+#define LIBXL_HAVE_PSR_GENERIC 1
+
+/*
+ * LIBXL_HAVE_MCA_CAPS
+ *
+ * If this is defined, setting MCA capabilities for HVM domain is supported.
+ */
+#define LIBXL_HAVE_MCA_CAPS 1
+#endif
+
+/*
+ * LIBXL_HAVE_PCITOPOLOGY
+ *
+ * If this is defined, then interface to query hypervisor about PCI device
+ * topology is available.
+ */
+#define LIBXL_HAVE_PCITOPOLOGY 1
+
+/*
+ * LIBXL_HAVE_SOCKET_BITMAP
+ *
+ * If this is defined, then libxl_socket_bitmap_alloc and
+ * libxl_get_online_socketmap exist.
+ */
+#define LIBXL_HAVE_SOCKET_BITMAP 1
+
+/*
+ * LIBXL_HAVE_SRM_V2
+ *
+ * If this is defined, then the libxl_domain_create_restore() interface takes
+ * a "stream_version" parameter and supports a value of 2.
+ *
+ * libxl_domain_suspend() will produce a v2 stream.
+ */
+#define LIBXL_HAVE_SRM_V2 1
+
+/*
+ * LIBXL_HAVE_SRM_V1
+ *
+ * In the case that LIBXL_HAVE_SRM_V2 is set, LIBXL_HAVE_SRM_V1
+ * indicates that libxl_domain_create_restore() can handle a "stream_version"
+ * parameter of 1, and convert the stream format automatically.
+ */
+#define LIBXL_HAVE_SRM_V1 1
+
+/*
+ * libxl_domain_build_info has the u.hvm.gfx_passthru_kind field and
+ * the libxl_gfx_passthru_kind enumeration is defined.
+*/
+#define LIBXL_HAVE_GFX_PASSTHRU_KIND
+
+/*
+ * LIBXL_HAVE_CHECKPOINTED_STREAM
+ *
+ * If this is defined, then libxl_checkpointed_stream exists.
+ */
+#define LIBXL_HAVE_CHECKPOINTED_STREAM 1
+
+/*
+ * LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
+ *
+ * libxl_domain_build_info has u.hvm.system_firmware field which can be use
+ * to provide a different firmware blob (like SeaBIOS or OVMF).
+ */
+#define LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
+
+/*
+ * ERROR_REMUS_XXX error code only exists from Xen 4.5, Xen 4.6 and it
+ * is changed to ERROR_CHECKPOINT_XXX in Xen 4.7
+ */
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040500 \
+                               && LIBXL_API_VERSION < 0x040700
+#define ERROR_REMUS_DEVOPS_DOES_NOT_MATCH \
+        ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH
+#define ERROR_REMUS_DEVICE_NOT_SUPPORTED \
+        ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED
+#endif
+
+/*
+ * LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN
+ *
+ * In the case that LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN is set the
+ * libxl_vga_interface_type enumeration type contains a
+ * LIBXL_VGA_INTERFACE_TYPE_UNKNOWN identifier. This is used to signal
+ * that a libxl_vga_interface_type type has not been initialized yet.
+ */
+#define LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN 1
+
+/*
+ * LIBXL_HAVE_BYTEARRAY_UUID
+ *
+ * If this is defined, the internal member of libxl_uuid is defined
+ * as a 16 byte array that contains the UUID in big endian format.
+ * Also, the same structure layout is used across all OSes.
+ */
+#define LIBXL_HAVE_BYTEARRAY_UUID 1
+
+/*
+ * LIBXL_HAVE_MEMKB_64BITS
+ *
+ * If this is defined libxl_set_memory_target(), libxl_domain_setmaxmem()
+ * and libxl_wait_for_free_memory()  will take a 64 bit value for the memory
+ * size parameter.
+ * From Xen 4.8 on libxl_get_memory_target(), libxl_domain_need_memory() and
+ * libxl_get_free_memory() return the memory size in a 64 bit value, too.
+ */
+#define LIBXL_HAVE_MEMKB_64BITS 1
+
+/*
+ * LIBXL_HAVE_QED
+ *
+ * If this is defined QED disk formats can be used for both HVM and PV guests.
+ */
+#define LIBXL_HAVE_QED 1
+
+/*
+ * LIBXL_HAVE_SET_PARAMETERS
+ *
+ * If this is defined setting hypervisor parameters is supported.
+ */
+#define LIBXL_HAVE_SET_PARAMETERS 1
+
+/*
+ * LIBXL_HAVE_PV_SHIM
+ *
+ * If this is defined, libxl_domain_build_info's pvh type information
+ * contains members pvshim, pvshim_path, pvshim_cmdline, pvshim_extra.
+ */
+#define LIBXL_HAVE_PV_SHIM 1
+
+/*
+ * LIBXL_HAVE_PVCALLS
+ *
+ * If this is defined, libxl supports creating pvcalls interfaces.
+ */
+#define LIBXL_HAVE_PVCALLS 1
+
+/*
+ * LIBXL_HAVE_FN_USING_QMP_ASYNC
+ *
+ * This define indicates that some function's API has changed and have an
+ * extra parameter "ao_how" which means that the function can be executed
+ * asynchronously. Those functions are:
+ *   libxl_domain_pause()
+ *   libxl_domain_unpause()
+ *   libxl_send_trigger()
+ *   libxl_set_vcpuonline()
+ *   libxl_retrieve_domain_configuration()
+ *   libxl_qemu_monitor_command()
+ *   libxl_domain_shutdown()
+ *   libxl_domain_reboot()
+ */
+#define LIBXL_HAVE_FN_USING_QMP_ASYNC 1
+
+/*
+ * LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
+ *
+ * If this is set, libxl_domain_need_memory takes a
+ * libxl_domain_config* (non-const) and uint32_t domid_for_logging
+ * (instead of a const libxl_domain_build_info*).
+ *
+ * If this is set, there is no need to call
+ * libxl_get_required_shadow_memory and instead the caller should
+ * simply leave shadow_memkb set to LIBXL_MEMKB_DEFAULT and allow
+ * libxl to fill in a suitable default in the usual way.
+ */
+#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
+
+/*
+ * LIBXL_HAVE_CREATEINFO_DOMID
+ *
+ * libxl_domain_create_new() and libxl_domain_create_restore() will use
+ * a domid specified in libxl_domain_create_info.
+ */
+#define LIBXL_HAVE_CREATEINFO_DOMID
+
+/*
+ * LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
+ *
+ * libxl_domain_create_info contains a boolean 'xend_suspend_evtchn_compat'
+ * value to control creation of the xenstore path for a domain's suspend
+ * event channel.
+ */
+#define LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
+
+typedef char **libxl_string_list;
+void libxl_string_list_dispose(libxl_string_list *sl);
+int libxl_string_list_length(const libxl_string_list *sl);
+void libxl_string_list_copy(libxl_ctx *ctx, libxl_string_list *dst,
+                            const libxl_string_list *src);
+
+typedef char **libxl_key_value_list;
+void libxl_key_value_list_dispose(libxl_key_value_list *kvl);
+int libxl_key_value_list_length(const libxl_key_value_list *kvl);
+void libxl_key_value_list_copy(libxl_ctx *ctx,
+                               libxl_key_value_list *dst,
+                               const libxl_key_value_list *src);
+
+typedef uint32_t libxl_hwcap[8];
+void libxl_hwcap_copy(libxl_ctx *ctx, libxl_hwcap *dst, const libxl_hwcap *src);
+
+typedef uint64_t libxl_ev_user;
+
+typedef struct {
+    uint32_t size;          /* number of bytes in map */
+    uint8_t *map;
+} libxl_bitmap;
+void libxl_bitmap_init(libxl_bitmap *map);
+void libxl_bitmap_dispose(libxl_bitmap *map);
+
+/*
+ * libxl_cpuid_policy is opaque in the libxl ABI.  Users of both libxl and
+ * libxc may not make assumptions about xc_xend_cpuid.
+ */
+typedef struct xc_xend_cpuid libxl_cpuid_policy;
+typedef libxl_cpuid_policy * libxl_cpuid_policy_list;
+void libxl_cpuid_dispose(libxl_cpuid_policy_list *cpuid_list);
+int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *l);
+void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
+                                  libxl_cpuid_policy_list *dst,
+                                  const libxl_cpuid_policy_list *src);
+
+#define LIBXL_PCI_FUNC_ALL (~0U)
+
+typedef uint32_t libxl_domid;
+typedef int libxl_devid;
+
+/*
+ * Formatting Enumerations.
+ *
+ * Each enumeration type libxl_E declares an associated lookup table
+ * libxl_E_string_table and a lookup function libxl_E_from_string.
+ */
+typedef struct {
+    const char *s;
+    int v;
+} libxl_enum_string_table;
+
+struct libxl_event;
+typedef LIBXL_TAILQ_ENTRY(struct libxl_event) libxl_ev_link;
+
+/*
+ * A boolean variable with an explicit default state.
+ *
+ * Users should treat this struct as opaque and use the following
+ * defined macros and accessor functions.
+ *
+ * To allow users of the library to naively select all defaults this
+ * state is represented as 0. False is < 0 and True is > 0.
+ */
+typedef struct {
+    int val;
+} libxl_defbool;
+
+void libxl_defbool_set(libxl_defbool *db, bool b);
+/* Resets to default */
+void libxl_defbool_unset(libxl_defbool *db);
+/* Sets db only if it is currently == default */
+void libxl_defbool_setdefault(libxl_defbool *db, bool b);
+bool libxl_defbool_is_default(libxl_defbool db);
+/* db must not be == default */
+bool libxl_defbool_val(libxl_defbool db);
+
+const char *libxl_defbool_to_string(libxl_defbool b);
+
+#define LIBXL_TIMER_MODE_DEFAULT -1
+#define LIBXL_MEMKB_DEFAULT ~0ULL
+
+/*
+ * We'd like to set a memory boundary to determine if we need to check
+ * any overlap with reserved device memory.
+ */
+#define LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT (2048 * 1024)
+
+#define LIBXL_MS_VM_GENID_LEN 16
+typedef struct {
+    uint8_t bytes[LIBXL_MS_VM_GENID_LEN];
+} libxl_ms_vm_genid;
+
+#include "_libxl_types.h"
+
+const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx);
+
+/*
+ * Some libxl operations can take a long time.  These functions take a
+ * parameter to control their concurrency:
+ *     libxl_asyncop_how *ao_how
+ *
+ * If ao_how==NULL, the function will be synchronous.
+ *
+ * If ao_how!=NULL, the function will set the operation going, and if
+ * this is successful will return 0.  In this case the zero error
+ * response does NOT mean that the operation was successful; it just
+ * means that it has been successfully started.  It will finish later,
+ * perhaps with an error.
+ *
+ * If ao_how->callback!=NULL, the callback will be called when the
+ * operation completes.  The same rules as for libxl_event_hooks
+ * apply, including the reentrancy rules and the possibility of
+ * "disaster", except that libxl calls ao_how->callback instead of
+ * libxl_event_hooks.event_occurs.  (See libxl_event.h.)
+ *
+ * If ao_how->callback==NULL, a libxl_event will be generated which
+ * can be obtained from libxl_event_wait or libxl_event_check.  The
+ * event will have type OPERATION_COMPLETE (which is not used
+ * elsewhere).
+ *
+ * Note that it is possible for an asynchronous operation which is to
+ * result in a callback to complete during its initiating function
+ * call.  In this case the initiating function will return 0
+ * indicating the at the operation is "in progress", even though by
+ * the time it returns the operation is complete and the callback has
+ * already happened.
+ *
+ * The application must set and use ao_how->for_event (which will be
+ * copied into libxl_event.for_user) or ao_how->for_callback (passed
+ * to the callback) to determine which operation finished, and it must
+ * of course check the rc value for errors.
+ *
+ * *ao_how does not need to remain valid after the initiating function
+ * returns. All other parameters must remain valid for the lifetime of
+ * the asynchronous operation, unless otherwise specified.
+ *
+ * Callbacks may occur on any thread in which the application calls
+ * libxl.
+ */
+
+typedef struct {
+    void (*callback)(libxl_ctx *ctx, int rc, void *for_callback);
+    union {
+        libxl_ev_user for_event; /* used if callback==NULL */
+        void *for_callback; /* passed to callback */
+    } u;
+} libxl_asyncop_how;
+
+/*
+ * Some more complex asynchronous operations can report intermediate
+ * progress.  How this is to be reported is controlled, for each
+ * function, by a parameter
+ *    libxl_asyncprogress_how *aop_FOO_how;
+ * for each kind of progress FOO supported by that function.  Each
+ * such kind of progress is associated with an event type.
+ *
+ * The function description will document whether, when, and how
+ * many times, the intermediate progress will be reported, and
+ * what the corresponding event type(s) are.
+ *
+ * If aop_FOO_how==NULL, intermediate progress reports are discarded.
+ *
+ * If aop_FOO_how->callback==NULL, intermediate progress reports
+ * generate libxl events which can be obtained from libxl_event_wait
+ * or libxl_event_check.
+ *
+ * If aop_FOO_how->callback!=NULL, libxl will report intermediate
+ * progress by calling callback(ctx, &event, for_callback).
+ *
+ * The rules for these events are otherwise the same as those for
+ * ordinary events.  The reentrancy and threading rules for the
+ * callback are the same as those for ao completion callbacks.
+ *
+ * Note that the callback, if provided, is responsible for freeing
+ * the event.
+ *
+ * If callbacks are requested, they will be made, and returned, before
+ * the long-running libxl operation is considered finished (so if the
+ * long-running libxl operation was invoked with ao_how==NULL then any
+ * callbacks will occur strictly before the long-running operation
+ * returns).  However, the callbacks may occur on any thread.
+ *
+ * In general, otherwise, no promises are made about the relative
+ * order of callbacks in a multithreaded program.  In particular
+ * different callbacks relating to the same long-running operation may
+ * be delivered out of order.
+ */
+
+typedef struct {
+    void (*callback)(libxl_ctx *ctx, libxl_event*, void *for_callback);
+    libxl_ev_user for_event; /* always used */
+    void *for_callback; /* passed to callback */
+} libxl_asyncprogress_how;
+
+/*
+ * It is sometimes possible to abort an asynchronous operation.
+ *
+ * libxl_ao_abort searches for an ongoing asynchronous operation whose
+ * ao_how is identical to *how, and tries to abort it.  The return
+ * values from libxl_ao_abort are as follows:
+ *
+ *  0
+ *
+ *     The operation was found, and attempts are being made to cut it
+ *     short.  However, it may still take some time to stop.  It is
+ *     also possible that the operation will nevertheless complete
+ *     successfully.
+ *
+ *  ERROR_NOTFOUND
+ *
+ *      No matching ongoing operation was found.  This might happen
+ *      for an actual operation if the operation has already completed
+ *      (perhaps on another thread).  The call to libxl_ao_abort has
+ *      had no effect.
+ *
+ *  ERROR_ABORTED
+ *
+ *     The operation has already been the subject of at least one
+ *     call to libxl_ao_abort.
+ *
+ * If the operation was indeed cut short due to the abort request, it
+ * will complete, at some point in the future, with ERROR_ABORTED.  In
+ * that case, depending on the operation it have performed some of the
+ * work in question and left the operation half-done.  Consult the
+ * documentation for individual operations.
+ *
+ * Note that an aborted operation might still fail for other reasons
+ * even after the abort was requested.
+ *
+ * If your application is multithreaded you must not reuse an
+ * ao_how->for_event or ao_how->for_callback value (with a particular
+ * ao_how->callback) unless you are sure that none of your other
+ * threads are going to abort the previous operation using that
+ * value; otherwise you risk aborting the wrong operation if the
+ * intended target of the abort request completes in the meantime.
+ *
+ * It is possible to abort even an operation which is being performed
+ * synchronously, but since in that case how==NULL you had better only
+ * have one such operation, because it is not possible to tell them
+ * apart (and libxl_ao_abort will abort only the first one it finds).
+ * (And, if you want to do this, obviously the abort would have to be
+ * requested on a different thread.)
+ */
+int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
+                   LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+#define LIBXL_VERSION 0
+
+/* context functions */
+int libxl_ctx_alloc(libxl_ctx **pctx, int version,
+                    unsigned flags /* none currently defined */,
+                    xentoollog_logger *lg);
+int libxl_ctx_free(libxl_ctx *ctx /* 0 is OK */);
+
+/* domain related functions */
+
+#define INVALID_DOMID ~0
+#define RANDOM_DOMID (INVALID_DOMID - 1)
+
+/* If the result is ERROR_ABORTED, the domain may or may not exist
+ * (in a half-created state).  *domid will be valid and will be the
+ * domain id, or INVALID_DOMID, as appropriate */
+
+int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
+                            uint32_t *domid,
+                            const libxl_asyncop_how *ao_how,
+                            const libxl_asyncprogress_how *aop_console_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
+                                uint32_t *domid, int restore_fd,
+                                int send_back_fd,
+                                const libxl_domain_restore_params *params,
+                                const libxl_asyncop_how *ao_how,
+                                const libxl_asyncprogress_how *aop_console_how)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040400
+
+static inline int libxl_domain_create_restore_0x040200(
+    libxl_ctx *ctx, libxl_domain_config *d_config,
+    uint32_t *domid, int restore_fd,
+    const libxl_asyncop_how *ao_how,
+    const libxl_asyncprogress_how *aop_console_how)
+    LIBXL_EXTERNAL_CALLERS_ONLY
+{
+    libxl_domain_restore_params params;
+    int ret;
+
+    libxl_domain_restore_params_init(&params);
+
+    ret = libxl_domain_create_restore(
+        ctx, d_config, domid, restore_fd, -1, &params, ao_how, aop_console_how);
+
+    libxl_domain_restore_params_dispose(&params);
+    return ret;
+}
+
+#define libxl_domain_create_restore libxl_domain_create_restore_0x040200
+
+#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040400 \
+                                 && LIBXL_API_VERSION < 0x040700
+
+static inline int libxl_domain_create_restore_0x040400(
+    libxl_ctx *ctx, libxl_domain_config *d_config,
+    uint32_t *domid, int restore_fd,
+    const libxl_domain_restore_params *params,
+    const libxl_asyncop_how *ao_how,
+    const libxl_asyncprogress_how *aop_console_how)
+    LIBXL_EXTERNAL_CALLERS_ONLY
+{
+    return libxl_domain_create_restore(ctx, d_config, domid, restore_fd,
+                                       -1, params, ao_how, aop_console_how);
+}
+
+#define libxl_domain_create_restore libxl_domain_create_restore_0x040400
+
+#endif
+
+int libxl_domain_soft_reset(libxl_ctx *ctx,
+                            libxl_domain_config *d_config,
+                            uint32_t domid,
+                            const libxl_asyncop_how *ao_how,
+                            const libxl_asyncprogress_how
+                            *aop_console_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+
+  /* A progress report will be made via ao_console_how, of type
+   * domain_create_console_available, when the domain's primary
+   * console is available and can be connected to.
+   */
+
+void libxl_domain_config_init(libxl_domain_config *d_config);
+void libxl_domain_config_dispose(libxl_domain_config *d_config);
+
+/*
+ * Retrieve domain configuration and filled it in d_config. The
+ * returned configuration can be used to rebuild a domain. It only
+ * works with DomU.
+ */
+int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
+                                        libxl_domain_config *d_config,
+                                        const libxl_asyncop_how *ao_how)
+                                        LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_retrieve_domain_configuration_0x041200(
+    libxl_ctx *ctx, uint32_t domid, libxl_domain_config *d_config)
+{
+    return libxl_retrieve_domain_configuration(ctx, domid, d_config, NULL);
+}
+#define libxl_retrieve_domain_configuration \
+    libxl_retrieve_domain_configuration_0x041200
+#endif
+
+int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd,
+                         int flags, /* LIBXL_SUSPEND_* */
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+#define LIBXL_SUSPEND_DEBUG 1
+#define LIBXL_SUSPEND_LIVE 2
+
+/*
+ * Only suspend domain, do not save its state to file, do not destroy it.
+ * Suspended domain can be resumed with libxl_domain_resume()
+ */
+int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* @param suspend_cancel [from xenctrl.h:xc_domain_resume( @param fast )]
+ *   If this parameter is true, use co-operative resume. The guest
+ *   must support this.
+ */
+int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
+                        const libxl_asyncop_how *ao_how)
+                        LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * This function doesn't return unless something has gone wrong with
+ * the replication to the secondary. If this function returns then the
+ * caller should resume the (primary) domain.
+ */
+int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
+                             uint32_t domid, int send_fd, int recv_fd,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
+                          const libxl_asyncop_how *ao_how)
+                          LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
+                        const libxl_asyncop_how *ao_how)
+                        LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_domain_shutdown_0x041200(libxl_ctx *ctx,
+                                                 uint32_t domid)
+{
+    return libxl_domain_shutdown(ctx, domid, NULL);
+}
+#define libxl_domain_shutdown libxl_domain_shutdown_0x041200
+static inline int libxl_domain_reboot_0x041200(libxl_ctx *ctx,
+                                               uint32_t domid)
+{
+    return libxl_domain_reboot(ctx, domid, NULL);
+}
+#define libxl_domain_reboot libxl_domain_reboot_0x041200
+#endif
+
+int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid);
+
+/* get max. number of cpus supported by hypervisor */
+int libxl_get_max_cpus(libxl_ctx *ctx);
+
+/* get the actual number of currently online cpus on the host */
+int libxl_get_online_cpus(libxl_ctx *ctx);
+  /* Beware that no locking or serialization is provided by libxl,
+   * so the information can be outdated as far as the function
+   * returns. If there are other entities in the system capable
+   * of onlining/offlining CPUs, it is up to the application
+   * to guarantee consistency, if that is important. */
+
+/* get max. number of NUMA nodes supported by hypervisor */
+int libxl_get_max_nodes(libxl_ctx *ctx);
+
+int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
+                        const char *old_name, const char *new_name);
+
+  /* if old_name is NULL, any old name is OK; otherwise we check
+   * transactionally that the domain has the old old name; if
+   * trans is not 0 we use caller's transaction and caller must do retries */
+
+int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
+                       const libxl_asyncop_how *ao_how)
+                       LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_domain_pause_0x041200(
+    libxl_ctx *ctx, uint32_t domid)
+{
+    return libxl_domain_pause(ctx, domid, NULL);
+}
+static inline int libxl_domain_unpause_0x041200(
+    libxl_ctx *ctx, uint32_t domid)
+{
+    return libxl_domain_unpause(ctx, domid, NULL);
+}
+#define libxl_domain_pause libxl_domain_pause_0x041200
+#define libxl_domain_unpause libxl_domain_unpause_0x041200
+#endif
+
+
+int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
+                           const char *filename,
+                           const libxl_asyncop_how *ao_how)
+                           LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t target_memkb);
+int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int64_t target_memkb, int relative, int enforce);
+int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint64_t *out_target);
+int libxl_get_memory_target_0x040700(libxl_ctx *ctx, uint32_t domid,
+                                     uint32_t *out_target)
+    LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * WARNING
+ * This memory management API is unstable even in Xen 4.2.
+ * It has a numer of deficiencies and we intend to replace it.
+ *
+ * The semantics of these functions should not be relied on to be very
+ * coherent or stable.  We will however endeavour to keep working
+ * existing programs which use them in roughly the same way as libxl.
+ */
+/* how much free memory in the system a domain needs to be built */
+int libxl_domain_need_memory(libxl_ctx *ctx,
+                             libxl_domain_config *config
+                             /* ^ will be partially defaulted */,
+                             uint32_t domid_for_logging /* INVALID_DOMID ok */,
+                             uint64_t *need_memkb);
+int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
+                                      const libxl_domain_build_info *b_info_in,
+                                      uint64_t *need_memkb);
+int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
+                                      const libxl_domain_build_info *b_info_in,
+                                      uint32_t *need_memkb)
+    LIBXL_EXTERNAL_CALLERS_ONLY;
+/* how much free memory is available in the system */
+int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb);
+int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
+    LIBXL_EXTERNAL_CALLERS_ONLY;
+/* wait for a given amount of memory to be free in the system */
+int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint64_t memory_kb, int wait_secs);
+/*
+ * Wait for the memory target of a domain to be reached. Does not
+ * decrement wait_secs if the domain is making progress toward reaching
+ * the target. If the domain is not making progress, wait_secs is
+ * decremented. If the timeout expires before the target is reached, the
+ * function returns ERROR_FAIL.
+ *
+ * Older versions of this function (Xen 4.5 and older), decremented
+ * wait_secs even if the domain was making progress, resulting in far
+ * lower overall wait times. To make sure that your calling routine
+ * works with new and old implementations of the function, pass enough
+ * time for the guest to reach its target as an argument.
+ */
+int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs);
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
+#define libxl_get_memory_target libxl_get_memory_target_0x040700
+#define libxl_domain_need_memory libxl_domain_need_memory_0x040700
+#define libxl_get_free_memory libxl_get_free_memory_0x040700
+#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+#define libxl_domain_need_memory libxl_domain_need_memory_0x041200
+#endif
+
+int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass);
+
+/*
+ * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
+ * the caller that it has connected to the guest console.
+ */
+int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
+                       libxl_console_type type, int notify_fd);
+/* libxl_primary_console_exec finds the domid and console number
+ * corresponding to the primary console of the given vm, then calls
+ * libxl_console_exec with the right arguments (domid might be different
+ * if the guest is using stubdoms).
+ * This function can be called after creating the device model, in
+ * case of HVM guests, and before libxl_run_bootloader in case of PV
+ * guests using pygrub.
+ * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
+ * the caller that it has connected to the guest console.
+ */
+int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm,
+                               int notify_fd);
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
+
+static inline int libxl_console_exec_0x040700(libxl_ctx *ctx,
+                                              uint32_t domid, int cons_num,
+                                              libxl_console_type type)
+{
+    return libxl_console_exec(ctx, domid, cons_num, type, -1);
+}
+#define libxl_console_exec libxl_console_exec_0x040700
+
+static inline int libxl_primary_console_exec_0x040700(libxl_ctx *ctx,
+                                                      uint32_t domid_vm)
+{
+    return libxl_primary_console_exec(ctx, domid_vm, -1);
+}
+#define libxl_primary_console_exec libxl_primary_console_exec_0x040700
+
+#endif
+
+/* libxl_console_get_tty retrieves the specified domain's console tty path
+ * and stores it in path. Caller is responsible for freeing the memory.
+ */
+int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
+                          libxl_console_type type, char **path);
+
+/* libxl_primary_console_get_tty retrieves the specified domain's primary
+ * console tty path and stores it in path. Caller is responsible for freeing
+ * the memory.
+ */
+int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, char **path);
+
+/* May be called with info_r == NULL to check for domain's existence.
+ * Returns ERROR_DOMAIN_NOTFOUND if domain does not exist (used to return
+ * ERROR_INVAL for this scenario). */
+int libxl_domain_info(libxl_ctx*, libxl_dominfo *info_r,
+                      uint32_t domid);
+
+/* These functions each return (on success) an array of elements,
+ * and the length via the int* out parameter.  These arrays and
+ * their contents come from malloc, and must be freed with the
+ * corresponding libxl_THING_list_free function.
+ */
+libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out);
+void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain);
+
+libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out);
+void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool);
+
+libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out);
+void libxl_vminfo_list_free(libxl_vminfo *list, int nb_vm);
+
+#define LIBXL_CPUTOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
+libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out);
+void libxl_cputopology_list_free(libxl_cputopology *, int nb_cpu);
+
+#define LIBXL_PCITOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
+libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs);
+void libxl_pcitopology_list_free(libxl_pcitopology *, int num_devs);
+
+#define LIBXL_NUMAINFO_INVALID_ENTRY (~(uint32_t)0)
+libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr);
+void libxl_numainfo_list_free(libxl_numainfo *, int nr);
+
+libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
+                                int *nb_vcpu, int *nr_cpus_out);
+void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus);
+
+/*
+ * Devices
+ * =======
+ *
+ * Each device is represented by a libxl_device_<TYPE> data structure
+ * which is defined via the IDL. In addition some devices have an
+ * additional data type libxl_device_<TYPE>_getinfo which contains
+ * further runtime information about the device.
+ *
+ * In addition to the general methods available for libxl types (see
+ * "libxl types" above) a common set of methods are available for each
+ * device type. These are described below.
+ *
+ * Querying
+ * --------
+ *
+ * libxl_device_<type>_list(ctx, domid, nr):
+ *
+ *   Returns an array of libxl_device_<type> length nr representing
+ *   the devices attached to the specified domain.
+ *
+ * libxl_device_<type>_getinfo(ctx, domid, device, info):
+ *
+ *   Initialises info with details of the given device which must be
+ *   attached to the specified domain.
+ *
+ * Creation / Control
+ * ------------------
+ *
+ * libxl_device_<type>_add(ctx, domid, device):
+ *
+ *   Adds the given device to the specified domain. This can be called
+ *   while the guest is running (hotplug) or before boot (coldplug).
+ *
+ *   This function only sets up the device but does not wait for the
+ *   domain to connect to the device and therefore cannot block on the
+ *   guest.
+ *
+ *   device is an in/out parameter:  fields left unspecified when the
+ *   structure is passed in are filled in with appropriate values for
+ *   the device created.
+ *
+ * libxl_device_<type>_destroy(ctx, domid, device):
+ *
+ *   Removes the given device from the specified domain without guest
+ *   co-operation. It is guest specific what affect this will have on
+ *   a running guest.
+ *
+ *   This function does not interact with the guest and therefore
+ *   cannot block on the guest.
+ *
+ * libxl_device_<type>_remove(ctx, domid, device):
+ *
+ *   Removes the given device from the specified domain by performing
+ *   an orderly unplug with guest co-operation. This requires that the
+ *   guest is running.
+ *
+ *   This method is currently synchronous and therefore can block
+ *   while interacting with the guest. There is a time-out of 10s on
+ *   this interaction after which libxl_device_<type>_destroy()
+ *   semantics apply.
+ *
+ * libxl_device_<type>_safe_remove(ctx, domid, device):
+ *
+ *   This has the same semantics as libxl_device_<type>_remove() but,
+ *   in the event of hitting the 10s time-out, this function will fail.
+ *
+ * Controllers
+ * -----------
+ *
+ * Most devices are treated individually.  Some classes of device,
+ * however, like USB or SCSI, inherently have the need to have a
+ * hierarchy of different levels, with lower-level devices "attached"
+ * to higher-level ones.  USB for instance has "controllers" at the
+ * top, which have buses, on which are devices, which consist of
+ * multiple interfaces.  SCSI has "hosts" at the top, then buses,
+ * targets, and LUNs.
+ *
+ * In that case, for each <class>, there will be a set of functions
+ * and types for each <level>.  For example, for <class>=usb, there
+ * may be <levels> ctrl (controller) and dev (device), with ctrl being
+ * level 0.
+ *
+ * libxl_device_<class><level0>_<function> will act more or
+ * less like top-level non-bus devices: they will either create or
+ * accept a libxl_devid which will be unique within the
+ * <class><level0> libxl_devid namespace.
+ *
+ * Lower-level devices must have a unique way to be identified.  One
+ * way to do this would be to name it via the name of the next level
+ * up plus an index; for instance, <ctrl devid, port number>.  Another
+ * way would be to have another devid namespace for that level.  This
+ * identifier will be used for queries and removals.
+ *
+ * Lower-level devices will include in their
+ * libxl_device_<class><level> struct a field referring to the unique
+ * index of the level above.  For instance, libxl_device_usbdev might
+ * contain the controller devid.
+ *
+ * In the case where there are multiple different ways to implement a
+ * given device -- for instance, one which is fully PV and one which
+ * uses an emulator -- the controller will contain a field which
+ * specifies what type of implementation is used.  The implementations
+ * of individual devices will be known by the controller to which they
+ * are attached.
+ *
+ * If libxl_device_<class><level>_add receives an empty reference to
+ * the level above, it may return an error.  Or it may (but is not
+ * required to) automatically choose a suitable device in the level
+ * above to which to attach the new device at this level.  It may also
+ * (but is not required to) automatically create a new device at the
+ * level above if no suitable devices exist.  Each class should
+ * document its behavior.
+ *
+ * libxl_device_<class><level>_list will list all devices of <class>
+ * at <level> in the domain.  For example, libxl_device_usbctrl_list
+ * will list all usb controllers; libxl_class_usbdev_list will list
+ * all usb devices across all controllers.
+ *
+ * For each class, the domain config file will contain a single list
+ * for each level.  libxl will first iterate through the list of
+ * top-level devices, then iterate through each level down in turn,
+ * adding devices to devices in the level above.  For instance, there
+ * will be one list for all usb controllers, and one list for all usb
+ * devices.
+ *
+ * If libxl_device_<class><level>_add automatically creates
+ * higher-level devices as necessary, then it is permissible for the
+ * higher-level lists to be empty and the device list to have devices
+ * with the field containing a reference to the higher level device
+ * uninitialized.
+ */
+
+/* Disks */
+int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
+                          libxl_device_disk *disk,
+                          const libxl_asyncop_how *ao_how)
+                          LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_disk *disk,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_destroy(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_disk *disk,
+                              const libxl_asyncop_how *ao_how)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_safe_remove(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_device_disk *disk,
+                                  const libxl_asyncop_how *ao_how)
+                                  LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx,
+                                          uint32_t domid, int *num)
+                                          LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_disk_list_free(libxl_device_disk* list, int num)
+                                 LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
+                              const libxl_device_disk *disk, libxl_diskinfo *diskinfo)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * Insert a CD-ROM device. A device corresponding to disk must already
+ * be attached to the guest.
+ */
+int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
+                       const libxl_asyncop_how *ao_how)
+                       LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * USB
+ *
+ * For each device removed or added, one of these protocols is available:
+ * - PV (i.e., PVUSB)
+ * - DEVICEMODEL (i.e, qemu)
+ *
+ * PV is available for either PV or HVM domains.  DEVICEMODEL is only
+ * available for HVM domains.  The caller can additionally specify
+ * "AUTO", in which case the library will try to determine the best
+ * protocol automatically.
+ *
+ * At the moment, the only protocol implemented is PV.
+ *
+ * One can add/remove USB controllers to/from guest, and attach/detach USB
+ * devices to/from USB controllers.
+ *
+ * To add USB controllers and USB devices, one can adding USB controllers
+ * first and then attaching USB devices to some USB controller, or adding
+ * USB devices to guest directly, it will automatically create a USB
+ * controller for USB devices to attach.
+ *
+ * To remove USB controllers or USB devices, one can remove USB devices
+ * under USB controller one by one and then remove USB controller, or
+ * remove USB controller directly, it will remove all USB devices under
+ * it automatically.
+ *
+ */
+/* USB Controllers*/
+int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_usbctrl *usbctrl,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid,
+                                libxl_device_usbctrl *usbctrl,
+                                const libxl_asyncop_how *ao_how)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_usbctrl *usbctrl,
+                                 const libxl_asyncop_how *ao_how)
+                                 LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx,
+                                                uint32_t domid, int *num);
+
+void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr);
+
+
+int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                 const libxl_device_usbctrl *usbctrl,
+                                 libxl_usbctrlinfo *usbctrlinfo);
+
+/* USB Devices */
+
+int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_usbdev *usbdev,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
+                               libxl_device_usbdev *usbdev,
+                               const libxl_asyncop_how *ao_how)
+                               LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_usbdev *
+libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num);
+
+void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr);
+
+/* Network Interfaces */
+int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_nic_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_nic *nic,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_nic_destroy(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_nic *nic,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx,
+                                        uint32_t domid, int *num)
+                                        LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_nic_list_free(libxl_device_nic* list, int num)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
+                             const libxl_device_nic *nic, libxl_nicinfo *nicinfo)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * Virtual Channels
+ * Channels manifest as consoles with names, see docs/misc/channels.txt
+ */
+libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
+                                                uint32_t domid,
+                                                int *num);
+int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                 const libxl_device_channel *channel,
+                                 libxl_channelinfo *channelinfo);
+
+/* Virtual TPMs */
+int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm,
+                          const libxl_asyncop_how *ao_how)
+                          LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vtpm_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_vtpm *vtpm,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vtpm_destroy(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_vtpm *vtpm,
+                              const libxl_asyncop_how *ao_how)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vtpm *libxl_device_vtpm_list(libxl_ctx *ctx,
+                                          uint32_t domid, int *num)
+                                          LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vtpm_list_free(libxl_device_vtpm*, int num)
+                                 LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vtpm_getinfo(libxl_ctx *ctx, uint32_t domid,
+                              const libxl_device_vtpm *vtpm, libxl_vtpminfo *vtpminfo)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Virtual displays */
+int libxl_device_vdispl_add(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_vdispl *displ,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vdispl_remove(libxl_ctx *ctx, uint32_t domid,
+                               libxl_device_vdispl *vdispl,
+                               const libxl_asyncop_how *ao_how)
+                               LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vdispl_destroy(libxl_ctx *ctx, uint32_t domid,
+                                libxl_device_vdispl *vdispl,
+                                const libxl_asyncop_how *ao_how)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vdispl *libxl_device_vdispl_list(libxl_ctx *ctx,
+                                              uint32_t domid, int *num)
+                                              LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vdispl_list_free(libxl_device_vdispl* list, int num)
+                                   LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                const libxl_device_vdispl *vdispl,
+                                libxl_vdisplinfo *vdisplinfo)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Virtual sounds */
+int libxl_device_vsnd_add(libxl_ctx *ctx, uint32_t domid,
+                          libxl_device_vsnd *vsnd,
+                          const libxl_asyncop_how *ao_how)
+                          LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vsnd_remove(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_vsnd *vsnd,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vsnd_destroy(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_vsnd *vsnd,
+                              const libxl_asyncop_how *ao_how)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vsnd *libxl_device_vsnd_list(libxl_ctx *ctx,
+                                          uint32_t domid, int *num)
+                                          LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vsnd_list_free(libxl_device_vsnd* list, int num)
+                                 LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
+                              const libxl_device_vsnd *vsnd,
+                              libxl_vsndinfo *vsndlinfo)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Keyboard */
+int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vkb_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_vkb *vkb,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vkb_destroy(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_vkb *vkb,
+                             const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vkb *libxl_device_vkb_list(libxl_ctx *ctx,
+                                        uint32_t domid, int *num)
+                                        LIBXL_EXTERNAL_CALLERS_ONLY;
+void libxl_device_vkb_list_free(libxl_device_vkb* list, int num)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
+                             const libxl_device_vkb *vkb,
+                             libxl_vkbinfo *vkbinfo)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Framebuffer */
+int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vfb_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_vfb *vfb,
+                            const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vfb_destroy(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_vfb *vfb,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* 9pfs */
+int libxl_device_p9_remove(libxl_ctx *ctx, uint32_t domid,
+                           libxl_device_p9 *p9,
+                           const libxl_asyncop_how *ao_how)
+                           LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_p9_destroy(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_p9 *p9,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* pvcalls interface */
+int libxl_device_pvcallsif_remove(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_device_pvcallsif *pvcallsif,
+                                  const libxl_asyncop_how *ao_how)
+                                  LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_pvcallsif_destroy(libxl_ctx *ctx, uint32_t domid,
+                                   libxl_device_pvcallsif *pvcallsif,
+                                   const libxl_asyncop_how *ao_how)
+                                   LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* PCI Passthrough */
+int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
+                         libxl_device_pci *pcidev,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_pci *pcidev,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_pci *pcidev,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid,
+                                        int *num);
+
+/*
+ * Turns the current process into a backend device service daemon
+ * for a driver domain.
+ *
+ * From a libxl API point of view, this starts a long-running
+ * operation.  That operation consists of "being a driver domain"
+ * and never completes.
+ *
+ * Attempting to abort this operation is not advisable; proper
+ * shutdown of the driver domain task is not supported.
+ */
+int libxl_device_events_handler(libxl_ctx *ctx,
+                                const libxl_asyncop_how *ao_how)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * Functions related to making devices assignable -- that is, bound to
+ * the pciback driver, ready to be given to a guest via
+ * libxl_pci_device_add.
+ *
+ * - ..._add() will unbind the device from its current driver (if
+ * already bound) and re-bind it to pciback; at that point it will be
+ * ready to be assigned to a VM.  If rebind is set, it will store the
+ * path to the old driver in xenstore so that it can be handed back to
+ * dom0 on restore.
+ *
+ * - ..._remove() will unbind the device from pciback, and if
+ * rebind is non-zero, attempt to assign it back to the driver
+ * from whence it came.
+ *
+ * - ..._list() will return a list of the PCI devices available to be
+ * assigned.
+ *
+ * add and remove are idempotent: if the device in question is already
+ * added or is not bound, the functions will emit a warning but return
+ * SUCCESS.
+ */
+int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
+int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
+libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num);
+
+/* CPUID handling */
+int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str);
+int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
+                                  const char* str);
+#if LIBXL_API_VERSION < 0x041400
+/*
+ * Dropped from the API in Xen 4.14.  At the time of writing, these functions
+ * don't appear to ever have had external callers.
+ *
+ * These have always been used internally during domain construction, and
+ * can't easily be used externally because of their implicit parameters in
+ * other pieces of global state.
+ *
+ * Furthermore, an API user can't usefully determine whether they get
+ * libxl_cpuid (the real implementation) or libxl_nocpuid (no-op stubs).
+ *
+ * The internal behaviour of these functions also needs to change.  Therefore
+ * for simplicitly, provide the no-op stubs.  Yes technically this is an API
+ * change in some cases for existing software, but there is 0 of that in
+ * practice.
+ */
+static inline void libxl_cpuid_apply_policy(libxl_ctx *ctx __attribute__((unused)),
+                                            uint32_t domid __attribute__((unused)))
+{}
+static inline void libxl_cpuid_set(libxl_ctx *ctx __attribute__((unused)),
+                                   uint32_t domid __attribute__((unused)),
+                                   libxl_cpuid_policy_list cpuid __attribute__((unused)))
+{}
+#endif
+
+/*
+ * Functions for allowing users of libxl to store private data
+ * relating to a domain.  The data is an opaque sequence of bytes and
+ * is not interpreted or used by libxl.
+ *
+ * Data is indexed by the userdata userid, which is a short printable
+ * ASCII string.  The following list is a registry of userdata userids
+ * (the registry may be updated by posting a patch to xen-devel):
+ *
+ *  userid        Data contents
+ *  "xl"          domain config file in xl format, Unix line endings
+ *  "libvirt-xml" domain config file in libvirt XML format.  See
+ *                http://libvirt.org/formatdomain.html
+ *  "domain-userdata-lock"  lock file to protect domain userdata in libxl.
+ *                          It's a per-domain lock. Applications should
+ *                          not touch this file.
+ *  "libxl-json"  libxl_domain_config object in JSON format, generated
+ *                by libxl. Applications should not access this file
+ *                directly. This file is protected by domain-userdata-lock
+ *                for against Read-Modify-Write operation and domain
+ *                destruction.
+ *
+ * libxl does not enforce the registration of userdata userids or the
+ * semantics of the data.  For specifications of the data formats
+ * see the code or documentation for the libxl caller in question.
+ */
+int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
+                              const char *userdata_userid,
+                              const uint8_t *data, int datalen)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+  /* If datalen==0, data is not used and the user data for
+   * that domain and userdata_userid is deleted. */
+int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
+                                 const char *userdata_userid,
+                                 uint8_t **data_r, int *datalen_r)
+                                 LIBXL_EXTERNAL_CALLERS_ONLY;
+  /* On successful return, *data_r is from malloc.
+   * If there is no data for that domain and userdata_userid,
+   * *data_r and *datalen_r will be set to 0.
+   * data_r and datalen_r may be 0.
+   * On error return, *data_r and *datalen_r are undefined.
+   */
+int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
+                          const char *userdata_userid);
+
+
+int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo);
+int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
+                           const libxl_bitmap *cpumap_hard,
+                           const libxl_bitmap *cpumap_soft);
+int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
+                                 uint32_t vcpuid,
+                                 const libxl_bitmap *cpumap_hard,
+                                 const libxl_bitmap *cpumap_soft);
+int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
+                               unsigned int max_vcpus,
+                               const libxl_bitmap *cpumap_hard,
+                               const libxl_bitmap *cpumap_soft);
+
+#if defined (LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
+
+#define libxl_set_vcpuaffinity(ctx, domid, vcpuid, map) \
+    libxl_set_vcpuaffinity((ctx), (domid), (vcpuid), (map), NULL)
+#define libxl_set_vcpuaffinity_all(ctx, domid, max_vcpus, map) \
+    libxl_set_vcpuaffinity_all((ctx), (domid), (max_vcpus), (map), NULL)
+
+#endif
+
+int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_bitmap *nodemap);
+int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_bitmap *nodemap);
+int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
+                         libxl_bitmap *cpumap,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_set_vcpuonline_0x041200(libxl_ctx *ctx,
+                                                uint32_t domid,
+                                                libxl_bitmap *cpumap)
+{
+    return libxl_set_vcpuonline(ctx, domid, cpumap, NULL);
+}
+#define libxl_set_vcpuonline libxl_set_vcpuonline_0x041200
+#endif
+
+/* A return value less than 0 should be interpreted as a libxl_error, while a
+ * return value greater than or equal to 0 should be interpreted as a
+ * libxl_scheduler. */
+int libxl_get_scheduler(libxl_ctx *ctx);
+
+/* Per-scheduler parameters */
+int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
+                                  libxl_sched_credit_params *scinfo);
+int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
+                                  libxl_sched_credit_params *scinfo);
+int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
+                                   libxl_sched_credit2_params *scinfo);
+int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
+                                   libxl_sched_credit2_params *scinfo);
+
+/* Scheduler Per-domain parameters */
+
+#define LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT    -1
+#define LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT       -1
+#define LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT    -1
+#define LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT     -1
+#define LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT   -1
+#define LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT -1
+#define LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT    -1
+
+/* Per-VCPU parameters */
+#define LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT   -1
+
+/* Get the per-domain scheduling parameters.
+ * For schedulers that support per-vcpu settings (e.g., RTDS),
+ * calling *_domain_get functions will get default scheduling
+ * parameters.
+ */
+int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_domain_sched_params *params);
+
+/* Set the per-domain scheduling parameters.
+ * For schedulers that support per-vcpu settings (e.g., RTDS),
+ * calling *_domain_set functions will set all vcpus with the same
+ * scheduling parameters.
+ */
+int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+                                  const libxl_domain_sched_params *params);
+
+/* Get the per-vcpu scheduling parameters */
+int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+                                libxl_vcpu_sched_params *params);
+
+/* Get the per-vcpu scheduling parameters of all vcpus of a domain */
+int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
+                                    libxl_vcpu_sched_params *params);
+
+/* Set the per-vcpu scheduling parameters */
+int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+                                const libxl_vcpu_sched_params *params);
+
+/* Set the per-vcpu scheduling parameters of all vcpus of a domain */
+int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
+                                    const libxl_vcpu_sched_params *params);
+
+int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
+                       libxl_trigger trigger, uint32_t vcpuid,
+                       const libxl_asyncop_how *ao_how)
+                       LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_send_trigger_0x041200(
+    libxl_ctx *ctx, uint32_t domid, libxl_trigger trigger, uint32_t vcpuid)
+{
+    return libxl_send_trigger(ctx, domid, trigger, vcpuid, NULL);
+}
+#define libxl_send_trigger libxl_send_trigger_0x041200
+#endif
+int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq);
+int libxl_send_debug_keys(libxl_ctx *ctx, char *keys);
+int libxl_set_parameters(libxl_ctx *ctx, char *params);
+
+typedef struct libxl__xen_console_reader libxl_xen_console_reader;
+
+libxl_xen_console_reader *
+    libxl_xen_console_read_start(libxl_ctx *ctx, int clear);
+int libxl_xen_console_read_line(libxl_ctx *ctx,
+                                libxl_xen_console_reader *cr,
+                                char **line_r);
+void libxl_xen_console_read_finish(libxl_ctx *ctx,
+                                   libxl_xen_console_reader *cr);
+
+uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid);
+
+char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long);
+int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid);
+int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid);
+int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name,
+                   uint32_t set);
+int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid, char* uuid,
+                           int auth);
+int libxl_tmem_freeable(libxl_ctx *ctx);
+
+int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap);
+
+/*
+ * Set poolid to LIBXL_CPUOOL_POOLID_ANY to have Xen choose a
+ * free poolid for you.
+ */
+#define LIBXL_CPUPOOL_POOLID_ANY 0xFFFFFFFF
+int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
+                         libxl_scheduler sched,
+                         libxl_bitmap cpumap, libxl_uuid *uuid,
+                         uint32_t *poolid);
+int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid);
+int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid);
+int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu);
+int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
+int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
+                                const libxl_bitmap *cpumap);
+int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu);
+int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
+int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
+                                   const libxl_bitmap *cpumap);
+int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid);
+int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid);
+
+int libxl_domid_valid_guest(uint32_t domid);
+
+int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
+                               uint32_t *ssidref);
+int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref, char **buf,
+                               size_t *len);
+int libxl_flask_getenforce(libxl_ctx *ctx);
+int libxl_flask_setenforce(libxl_ctx *ctx, int mode);
+int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size);
+
+int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id);
+bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id);
+void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
+                            const libxl_ms_vm_genid *src);
+
+#if defined(__i386__) || defined(__x86_64__)
+int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid);
+int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid);
+int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid);
+int libxl_psr_cmt_enabled(libxl_ctx *ctx);
+int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid);
+int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
+                                    uint32_t socketid,
+                                    uint32_t *l3_cache_size);
+int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
+                                      uint32_t domid,
+                                      uint32_t socketid,
+                                      uint32_t *l3_cache_occupancy);
+
+int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type);
+int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
+                             uint32_t domid,
+                             libxl_psr_cmt_type type,
+                             uint64_t scope,
+                             uint64_t *sample_r,
+                             uint64_t *tsc_r);
+
+/*
+ * Function to set a domain's cbm. It operates on a single or multiple
+ * target(s) defined in 'target_map'. The definition of 'target_map' is
+ * related to 'type':
+ * 'L3_CBM': 'target_map' specifies all the sockets to be operated on.
+ */
+int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
+                          libxl_psr_cbm_type type, libxl_bitmap *target_map,
+                          uint64_t cbm);
+/*
+ * Function to get a domain's cbm. It operates on a single 'target'.
+ * The definition of 'target' is related to 'type':
+ * 'L3_CBM': 'target' specifies which socket to be operated on.
+ */
+int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
+                          libxl_psr_cbm_type type, uint32_t target,
+                          uint64_t *cbm_r);
+
+/*
+ * On success, the function returns an array of elements in 'info',
+ * and the length in 'nr'.
+ */
+int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+                           unsigned int *nr, unsigned int lvl);
+int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+                              int *nr);
+void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr);
+
+typedef enum libxl_psr_cbm_type libxl_psr_type;
+
+/*
+ * Function to set a domain's value. It operates on a single or multiple
+ * target(s) defined in 'target_map'. 'target_map' specifies all the sockets
+ * to be operated on.
+ */
+int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
+                      libxl_psr_type type, libxl_bitmap *target_map,
+                      uint64_t val);
+/*
+ * Function to get a domain's cbm. It operates on a single 'target'.
+ * 'target' specifies which socket to be operated on.
+ */
+int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
+                      libxl_psr_type type, unsigned int target,
+                      uint64_t *val);
+/*
+ * On success, the function returns an array of elements in 'info',
+ * and the length in 'nr'.
+ */
+int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
+                          unsigned int lvl, unsigned int *nr,
+                          libxl_psr_hw_info **info);
+void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr);
+#endif
+
+/* misc */
+
+/* Each of these sets or clears the flag according to whether the
+ * 2nd parameter is nonzero.  On failure, they log, and
+ * return ERROR_FAIL, but also leave errno valid. */
+int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec);
+int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock);
+
+/*
+ * Issue a qmp monitor command to the device model of the specified domain.
+ * The function returns the output of the command in a new allocated buffer
+ * via output.
+ */
+int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
+                               const char *command_line, char **output,
+                               const libxl_asyncop_how *ao_how)
+                               LIBXL_EXTERNAL_CALLERS_ONLY;
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
+static inline int libxl_qemu_monitor_command_0x041200(libxl_ctx *ctx,
+    uint32_t domid, const char *command_line, char **output)
+{
+    return libxl_qemu_monitor_command(ctx, domid, command_line, output,
+                                      NULL);
+}
+#define libxl_qemu_monitor_command libxl_qemu_monitor_command_0x041200
+#endif
+
+#include <libxl_event.h>
+
+/*
+ * This function is for use only during host initialisation. If it is
+ * invoked on a host with running domains, or concurrent libxl
+ * processes then the system may malfuntion.
+ */
+int libxl_clear_domid_history(libxl_ctx *ctx);
+
+#endif /* LIBXL_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/include/libxl_event.h b/tools/libs/light/include/libxl_event.h
new file mode 100644 (file)
index 0000000..8d0aa64
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2011      Citrix Ltd.
+ * Author Ian Jackson <ian.jackson@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef LIBXL_EVENT_H
+#define LIBXL_EVENT_H
+
+#include <libxl.h>
+#include <poll.h>
+#include <sys/time.h>
+
+/*======================================================================*/
+
+/*
+ * Domain event handling - getting Xen events from libxl
+ *
+ * (Callers inside libxl may not call libxl_event_check or _wait.)
+ */
+
+#define LIBXL_EVENTMASK_ALL (~(unsigned long)0)
+
+typedef int libxl_event_predicate(const libxl_event*, void *user);
+  /* Return value is 0 if the event is unwanted or non-0 if it is.
+   * Predicates are not allowed to fail.
+   */
+
+int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
+                      uint64_t typemask,
+                      libxl_event_predicate *predicate, void *predicate_user)
+                      LIBXL_EXTERNAL_CALLERS_ONLY;
+  /* Searches for an event, already-happened, which matches typemask
+   * and predicate.  predicate==0 matches any event.
+   * libxl_event_check returns the event, which must then later be
+   * freed by the caller using libxl_event_free.
+   *
+   * Returns ERROR_NOT_READY if no such event has happened.
+   */
+
+int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
+                     uint64_t typemask,
+                     libxl_event_predicate *predicate, void *predicate_user)
+                     LIBXL_EXTERNAL_CALLERS_ONLY;
+  /* Like libxl_event_check but blocks if no suitable events are
+   * available, until some are.  Uses libxl_osevent_beforepoll/
+   * _afterpoll so may be inefficient if very many domains are being
+   * handled by a single program.
+   */
+
+void libxl_event_free(libxl_ctx *ctx, libxl_event *event);
+
+
+/* Alternatively or additionally, the application may also use this: */
+
+typedef struct libxl_event_hooks {
+    uint64_t event_occurs_mask;
+    void (*event_occurs)(void *user,
+#ifndef LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
+                         const
+#endif
+                         libxl_event *event);
+    void (*disaster)(void *user, libxl_event_type type,
+                     const char *msg, int errnoval);
+} libxl_event_hooks;
+
+void libxl_event_register_callbacks(libxl_ctx *ctx,
+                                    const libxl_event_hooks *hooks, void *user);
+  /*
+   * Arranges that libxl will henceforth call event_occurs for any
+   * events whose type is set in event_occurs_mask, rather than
+   * queueing the event for retrieval by libxl_event_check/wait.
+   * Events whose bit is clear in mask are not affected.
+   *
+   * event becomes owned by the application and must be freed, either
+   * by event_occurs or later.
+   *
+   * event_occurs may be NULL if mask is 0.
+   *
+   * libxl_event_register_callback also provides a way for libxl to
+   * report to the application that there was a problem reporting
+   * events; this can occur due to lack of host memory during event
+   * handling, or other wholly unrecoverable errors from system calls
+   * made by libxl.  This will not happen for frivolous reasons - only
+   * if the system, or the Xen components of it, are badly broken.
+   *
+   * msg and errnoval will describe the action that libxl was trying
+   * to do, and type specifies the type of libxl events which may be
+   * missing.  type may be 0 in which case events of all types may be
+   * missing.
+   *
+   * disaster may be NULL.  If it is, or if _register_callbacks has
+   * not been called, errors of this kind are fatal to the entire
+   * application: libxl will print messages to its logs and to stderr
+   * and call exit(-1).
+   *
+   * If disaster returns, it may be the case that some or all future
+   * libxl calls will return errors; likewise it may be the case that
+   * no more events (of the specified type, if applicable) can be
+   * produced.  An application which supplies a disaster function
+   * should normally react either by exiting, or by (when it has
+   * returned to its main event loop) shutting down libxl with
+   * libxl_ctx_free and perhaps trying to restart it with
+   * libxl_ctx_init.
+   *
+   * In any case before calling disaster, libxl will have logged a
+   * message with level XTL_CRITICAL.
+   *
+   * Reentrancy: it IS permitted to call libxl from within
+   * event_occurs.  It is NOT permitted to call libxl from within
+   * disaster.  The event_occurs and disaster callbacks may occur on
+   * any thread in which the application calls libxl.
+   *
+   * libxl_event_register_callbacks may be called as many times, with
+   * different parameters, as the application likes; the most recent
+   * call determines the libxl behaviour.  However it is NOT safe to
+   * call _register_callbacks concurrently with, or reentrantly from,
+   * any other libxl function, nor while any event-generation
+   * facilities are enabled.
+   */
+
+
+/*
+ * Events are only generated if they have been requested.
+ * The following functions request the generation of specific events.
+ *
+ * Each set of functions for controlling event generation has this form:
+ *
+ *   typedef struct libxl__evgen_FOO libxl__evgen_FOO;
+ *   int libxl_evenable_FOO(libxl_ctx *ctx, FURTHER PARAMETERS,
+ *                          libxl_ev_user user, libxl__evgen_FOO **evgen_out);
+ *   void libxl_evdisable_FOO(libxl_ctx *ctx, libxl__evgen_FOO *evgen);
+ *
+ * The evenable function arranges that the events (as described in the
+ * doc comment for the individual function) will start to be generated
+ * by libxl.  On success, *evgen_out is set to a non-null pointer to
+ * an opaque struct.
+ *
+ * The user value is returned in the generated events and may be
+ * used by the caller for whatever it likes.  The type ev_user is
+ * guaranteed to be an unsigned integer type which is at least
+ * as big as uint64_t and is also guaranteed to be big enough to
+ * contain any intptr_t value.
+ *
+ * If it becomes desirable to stop generation of the relevant events,
+ * or to reclaim the resources in libxl associated with the evgen
+ * structure, the same evgen value should be passed to the evdisable
+ * function.  However, note that events which occurred prior to the
+ * evdisable call may still be returned.
+ *
+ * The caller may enable identical events more than once.  If they do
+ * so, each actual occurrence will generate several events to be
+ * returned by libxl_event_check, with the appropriate user value(s).
+ * Aside from this, each occurrence of each event is returned by
+ * libxl_event_check exactly once.
+ *
+ * An evgen is associated with the libxl_ctx used for its creation.
+ * After libxl_ctx_free, all corresponding evgen handles become
+ * invalid and must no longer be passed to evdisable.
+ *
+ * Applications should ensure that they eventually retrieve every
+ * event using libxl_event_check or libxl_event_wait, since events
+ * which occur but are not retrieved by the application will be queued
+ * inside libxl indefinitely.  libxl_event_check/_wait may be O(n)
+ * where n is the number of queued events which do not match the
+ * criteria specified in the arguments to check/wait.
+ */
+
+typedef struct libxl__evgen_domain_death libxl_evgen_domain_death;
+int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
+                         libxl_ev_user, libxl_evgen_domain_death **evgen_out);
+void libxl_evdisable_domain_death(libxl_ctx *ctx, libxl_evgen_domain_death*);
+  /* Arranges for the generation of DOMAIN_SHUTDOWN and DOMAIN_DEATH
+   * events.  A domain which is destroyed before it shuts down
+   * may generate only a DEATH event.
+   */
+
+typedef struct libxl__evgen_disk_eject libxl_evgen_disk_eject;
+int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t domid, const char *vdev,
+                        libxl_ev_user, libxl_evgen_disk_eject **evgen_out);
+void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject*);
+  /* Arranges for the generation of DISK_EJECT events.  A copy of the
+   * string *vdev will be made for libxl's internal use, and a pointer
+   * to this (or some other) copy will be returned as the vdev
+   * member of event.u.
+   */
+
+
+/*======================================================================*/
+
+/*
+ * OS event handling - passing low-level OS events to libxl
+ *
+ * Event-driven programs must use these facilities to allow libxl
+ * to become aware of readability/writeability of file descriptors
+ * and the occurrence of timeouts.
+ *
+ * There are two approaches available.  The first is appropriate for
+ * simple programs handling reasonably small numbers of domains:
+ *
+ *   for (;;) {
+ *      libxl_osevent_beforepoll(...)
+ *      poll();
+ *      libxl_osevent_afterpoll(...);
+ *      for (;;) {
+ *          r = libxl_event_check(...);
+ *          if (r==ERROR_NOT_READY) break;
+ *          if (r) goto error_out;
+ *          do something with the event;
+ *      }
+ *   }
+ *
+ * The second approach uses libxl_osevent_register_hooks and is
+ * suitable for programs which are already using a callback-based
+ * event library.
+ *
+ * An application may freely mix the two styles of interaction.
+ *
+ * (Callers inside libxl may not call libxl_osevent_... functions.)
+ */
+
+struct pollfd;
+
+/* The caller should provide beforepoll with some space for libxl's
+ * fds, and tell libxl how much space is available by setting *nfds_io.
+ * fds points to the start of this space (and fds may be a pointer into
+ * a larger array, for example, if the application has some fds of
+ * its own that it is interested in).
+ *
+ * On return *nfds_io will in any case have been updated by libxl
+ * according to how many fds libxl wants to poll on.
+ *
+ * If the space was sufficient, libxl fills in fds[0..<new
+ * *nfds_io>] suitably for poll(2), updates *timeout_upd if needed,
+ * and returns ok.
+ *
+ * If space was insufficient, fds[0..<old *nfds_io>] is undefined on
+ * return; *nfds_io on return will be greater than the value on
+ * entry; *timeout_upd may or may not have been updated; and
+ * libxl_osevent_beforepoll returns ERROR_BUFERFULL.  In this case
+ * the application needs to make more space (enough space for
+ * *nfds_io struct pollfd) and then call beforepoll again, before
+ * entering poll(2).  Typically this will involve calling realloc.
+ *
+ * The application may call beforepoll with fds==NULL and
+ * *nfds_io==0 in order to find out how much space is needed.
+ *
+ * *timeout_upd is as for poll(2): it's in milliseconds, and
+ * negative values mean no timeout (infinity).
+ * libxl_osevent_beforepoll will only reduce the timeout, naturally.
+ */
+int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
+                             struct pollfd *fds, int *timeout_upd,
+                             struct timeval now)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* nfds and fds[0..nfds] must be from the most recent call to
+ * _beforepoll, as modified by poll.  (It is therefore not possible
+ * to have multiple threads simultaneously polling using this
+ * interface.)
+ *
+ * This function actually performs all of the IO and other actions,
+ * and generates events (libxl_event), which are implied by either
+ * (a) the time of day or (b) both (i) the returned information from
+ * _beforepoll, and (ii) the results from poll specified in
+ * fds[0..nfds-1].  Generated events can then be retrieved by
+ * libxl_event_check.
+ */
+void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
+                             struct timeval now)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+typedef struct libxl_osevent_hooks {
+  int (*fd_register)(void *user, int fd, void **for_app_registration_out,
+                     short events, void *for_libxl);
+  int (*fd_modify)(void *user, int fd, void **for_app_registration_update,
+                   short events);
+  void (*fd_deregister)(void *user, int fd, void *for_app_registration);
+  int (*timeout_register)(void *user, void **for_app_registration_out,
+                          struct timeval abs, void *for_libxl);
+  int (*timeout_modify)(void *user, void **for_app_registration_update,
+                         struct timeval abs)
+      /* only ever called with abs={0,0}, meaning ASAP */;
+  void (*timeout_deregister)(void *user, void *for_app_registration)
+      /* will never be called */;
+} libxl_osevent_hooks;
+
+/* The application which calls register_fd_hooks promises to
+ * maintain a register of fds and timeouts that libxl is interested
+ * in, and make calls into libxl (libxl_osevent_occurred_*)
+ * when those fd events and timeouts occur.  This is more efficient
+ * than _beforepoll/_afterpoll if there are many fds (which can
+ * happen if the same libxl application is managing many domains).
+ *
+ * For an fd event, events is as for poll().  register or modify may
+ * be called with events==0, in which case it must still work
+ * normally, just not generate any events.
+ *
+ * For a timeout event, milliseconds is as for poll().
+ * Specifically, negative values of milliseconds mean NO TIMEOUT.
+ * This is used by libxl to temporarily disable a timeout.
+ *
+ * If the register or modify hook succeeds it may update
+ * *for_app_registration_out/_update and must then return 0.
+ * On entry to register, *for_app_registration_out is always NULL.
+ *
+ * A registration or modification hook may fail, in which case it
+ * must leave the registration state of the fd or timeout unchanged.
+ * It may then either return ERROR_OSEVENT_REG_FAIL or any positive
+ * int.  The value returned will be passed up through libxl and
+ * eventually returned back to the application.  When register
+ * fails, any value stored into *for_registration_out is ignored by
+ * libxl; when modify fails, any changed value stored into
+ * *for_registration_update is honoured by libxl and will be passed
+ * to future modify or deregister calls.
+ *
+ * libxl may want to register more than one callback for any one fd;
+ * in that case: (i) each such registration will have at least one bit
+ * set in revents which is unique to that registration; (ii) if an
+ * event occurs which is relevant for multiple registrations the
+ * application's event system may call libxl_osevent_occurred_fd
+ * for one, some, or all of those registrations.
+ *
+ * If fd_modify is used, it is permitted for the application's event
+ * system to still make calls to libxl_osevent_occurred_fd for the
+ * "old" set of requested events; these will be safely ignored by
+ * libxl.
+ *
+ * libxl will remember the value stored in *for_app_registration_out
+ * (or *for_app_registration_update) by a successful call to
+ * register (or modify), and pass it to subsequent calls to modify
+ * or deregister.
+ *
+ * Note that the application must cope with a call from libxl to
+ * timeout_modify racing with its own call to
+ * libxl__osevent_occurred_timeout.  libxl guarantees that
+ * timeout_modify will only be called with abs={0,0} but the
+ * application must still ensure that libxl's attempt to cause the
+ * timeout to occur immediately is safely ignored even the timeout is
+ * actually already in the process of occurring.
+ *
+ * timeout_deregister is not used because it forms part of a
+ * deprecated unsafe mode of use of the API.
+ *
+ * osevent_register_hooks may be called only once for each libxl_ctx.
+ * libxl may make calls to register/modify/deregister from within
+ * any libxl function (indeed, it will usually call register from
+ * register_event_hooks).  Conversely, the application MUST NOT make
+ * the event occurrence calls (libxl_osevent_occurred_*) into libxl
+ * reentrantly from within libxl (for example, from within the
+ * register/modify functions).
+ *
+ * Lock hierarchy: the register/modify/deregister functions may be
+ * called with locks held.  These locks (the "libxl internal locks")
+ * are inside the libxl_ctx.  Therefore, if those register functions
+ * acquire any locks of their own ("caller register locks") outside
+ * libxl, to avoid deadlock one of the following must hold for each
+ * such caller register lock:
+ *  (a) "acquire libxl internal locks before caller register lock":
+ *      No libxl function may be called with the caller register
+ *      lock held.
+ *  (b) "acquire caller register lock before libxl internal locks":
+ *      No libxl function may be called _without_ the caller
+ *      register lock held.
+ * Of these we would normally recommend (a).
+ *
+ * The value *hooks is not copied and must outlast the libxl_ctx.
+ */
+void libxl_osevent_register_hooks(libxl_ctx *ctx,
+                                  const libxl_osevent_hooks *hooks,
+                                  void *user);
+
+/* It is NOT legal to call _occurred_ reentrantly within any libxl
+ * function.  Specifically it is NOT legal to call it from within
+ * a register callback.  Conversely, libxl MAY call register/deregister
+ * from within libxl_event_occurred_call_*.
+ */
+
+void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
+                               int fd, short events, short revents)
+                               LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* Implicitly, on entry to this function the timeout has been
+ * deregistered.  If _occurred_timeout is called, libxl will not
+ * call timeout_deregister; if it wants to requeue the timeout it
+ * will call timeout_register again.
+ */
+void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
+                                    LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+/*======================================================================*/
+
+/*
+ * Subprocess handling.
+ *
+ * Unfortunately the POSIX interface makes this very awkward.
+ *
+ * There are two possible arrangements for collecting statuses from
+ * wait/waitpid.
+ *
+ * For naive programs:
+ *
+ *     libxl will keep a SIGCHLD handler installed whenever it has an
+ *     active (unreaped) child.  It will reap all children with
+ *     wait(); any children it does not recognise will be passed to
+ *     the application via an optional callback (and will result in
+ *     logged warnings if no callback is provided or the callback
+ *     denies responsibility for the child).
+ *
+ *     libxl may have children whenever:
+ *
+ *       - libxl is performing an operation which can be made
+ *         asynchronous; ie one taking a libxl_asyncop_how, even
+ *         if NULL is passed indicating that the operation is
+ *         synchronous; or
+ *
+ *       - events of any kind are being generated, as requested
+ *         by libxl_evenable_....
+ *
+ *     A multithreaded application which is naive in this sense may
+ *     block SIGCHLD on some of its threads, but there must be at
+ *     least one thread that has SIGCHLD unblocked.  libxl will not
+ *     modify the blocking flag for SIGCHLD (except that it may create
+ *     internal service threads with all signals blocked).
+ *
+ *     A naive program must only have at any one time only
+ *     one libxl context which might have children.
+ *
+ * For programs which run their own children alongside libxl's:
+ *
+ *     A program which does this must call libxl_childproc_setmode.
+ *     There are three options:
+ * 
+ *     libxl_sigchld_owner_libxl:
+ *
+ *       While any libxl operation which might use child processes
+ *       is running, works like libxl_sigchld_owner_libxl_always;
+ *       but, deinstalls the handler the rest of the time.
+ *
+ *       In this mode, the application, while it uses any libxl
+ *       operation which might create or use child processes (see
+ *       above):
+ *           - Must not have any child processes running.
+ *           - Must not install a SIGCHLD handler.
+ *           - Must not reap any children.
+ *
+ *       This is the default (i.e. if setmode is not called, or 0 is
+ *       passed for hooks).
+ *
+ *     libxl_sigchld_owner_mainloop:
+ *
+ *       The application must install a SIGCHLD handler and reap (at
+ *       least) all of libxl's children and pass their exit status to
+ *       libxl by calling libxl_childproc_exited.  (If the application
+ *       has multiple libxl ctx's, it must call libxl_childproc_exited
+ *       on each ctx.)
+ *
+ *     libxl_sigchld_owner_libxl_always:
+ *
+ *       The application expects this libxl ctx to reap all of the
+ *       process's children, and provides a callback to be notified of
+ *       their exit statuses.  The application must have only one
+ *       libxl_ctx configured this way.
+ *
+ *     libxl_sigchld_owner_libxl_always_selective_reap:
+ *
+ *       The application expects to reap all of its own children
+ *       synchronously, and does not use SIGCHLD.  libxl is to install
+ *       a SIGCHLD handler.  The application may have multiple
+ *       libxl_ctxs configured this way; in which case all of its ctxs
+ *       must be so configured.
+ */
+
+
+typedef enum {
+    /* libxl owns SIGCHLD whenever it has a child, and reaps
+     * all children, including those not spawned by libxl. */
+    libxl_sigchld_owner_libxl,
+
+    /* Application promises to discover when SIGCHLD occurs and call
+     * libxl_childproc_exited or libxl_childproc_sigchld_occurred (but
+     * NOT from within a signal handler).  libxl will not itself
+     * arrange to (un)block or catch SIGCHLD. */
+    libxl_sigchld_owner_mainloop,
+
+    /* libxl owns SIGCHLD all the time, and the application is
+     * relying on libxl's event loop for reaping its children too. */
+    libxl_sigchld_owner_libxl_always,
+
+    /* libxl owns SIGCHLD all the time, but it must only reap its own
+     * children.  The application will reap its own children
+     * synchronously with waitpid, without the assistance of SIGCHLD. */
+    libxl_sigchld_owner_libxl_always_selective_reap,
+} libxl_sigchld_owner;
+
+typedef struct {
+    libxl_sigchld_owner chldowner;
+
+    /* All of these are optional: */
+
+    /* Called by libxl instead of fork.  Should behave exactly like
+     * fork, including setting errno etc.  May NOT reenter into libxl.
+     * Application may use this to discover pids of libxl's children,
+     * for example.
+     */
+    pid_t (*fork_replacement)(void *user);
+
+    /* With libxl_sigchld_owner_libxl, called by libxl when it has
+     * reaped a pid.  (Not permitted with _owner_mainloop.)
+     *
+     * Should return 0 if the child was recognised by the application
+     * (or if the application does not keep those kind of records),
+     * ERROR_UNKNOWN_CHILD if the application knows that the child is not
+     * the application's; if it returns another error code it is a
+     * disaster as described for libxl_event_register_callbacks.
+     * (libxl will report unexpected children to its error log.)
+     *
+     * If not supplied, the application is assumed not to start
+     * any children of its own.
+     *
+     * This function is NOT called from within the signal handler.
+     * Rather it will be called from inside a libxl's event handling
+     * code and thus only when libxl is running, for example from
+     * within libxl_event_wait.  (libxl uses the self-pipe trick
+     * to implement this.)
+     *
+     * childproc_exited_callback may call back into libxl, but it
+     * is best to avoid making long-running libxl calls as that might
+     * stall the calling event loop while the nested operation
+     * completes.
+     */
+    int (*reaped_callback)(pid_t, int status, void *user);
+} libxl_childproc_hooks;
+
+/* hooks may be 0 in which is equivalent to &{ libxl_sigchld_owner_libxl, 0, 0 }
+ *
+ * May not be called when libxl might have any child processes, or the
+ * behaviour is undefined.  So it is best to call this at
+ * initialisation.
+ *
+ * The value *hooks is not copied and must outlast the libxl_ctx.
+ */
+void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
+                             void *user);
+
+/*
+ * This function is for an application which owns SIGCHLD and which
+ * reaps all of the process's children, and dispatches the exit status
+ * to the correct place inside the application.
+ *
+ * May be called only by an application which has called setmode with
+ * chldowner == libxl_sigchld_owner_mainloop.  If pid was a process started
+ * by this instance of libxl, returns 0 after doing whatever
+ * processing is appropriate.  Otherwise silently returns
+ * ERROR_UNKNOWN_CHILD.  No other error returns are possible.
+ *
+ * May NOT be called from within a signal handler which might
+ * interrupt any libxl operation.  The application will almost
+ * certainly need to use the self-pipe trick (or a working pselect or
+ * ppoll) to implement this.
+ */
+int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status)
+                           LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/*
+ * This function is for an application which owns SIGCHLD but which
+ * doesn't keep track of all of its own children in a manner suitable
+ * for reaping all of them and then dispatching them.
+ *
+ * Such an the application must notify libxl, by calling this
+ * function, that a SIGCHLD occurred.  libxl will then check all its
+ * children, reap any that are ready, and take any action necessary -
+ * but it will not reap anything else.
+ *
+ * May be called only by an application which has called setmode with
+ * chldowner == libxl_sigchld_owner_mainloop.
+ *
+ * May NOT be called from within a signal handler which might
+ * interrupt any libxl operation (just like libxl_childproc_reaped).
+ */
+void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
+                           LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
+/*
+ * An application which initialises a libxl_ctx in a parent process
+ * and then forks a child which does not quickly exec, must
+ * instead libxl_postfork_child_noexec in the child.  One call
+ * on any existing (or specially made) ctx is sufficient; after
+ * this all previously existing libxl_ctx's are invalidated and
+ * must not be used - or even freed.  It is harmless to call this
+ * postfork function and then exec anyway.
+ *
+ * Until libxl_postfork_child_noexec has returned:
+ *  - No other libxl calls may be made.
+ *  - If any libxl ctx was configured handle the process's SIGCHLD,
+ *    the child may not create further (grand)child processes, nor
+ *    manipulate SIGCHLD.
+ *
+ * libxl_postfork_child_noexec may not reclaim all the resources
+ * associated with the libxl ctx.  This includes but is not limited
+ * to: ordinary memory; files on disk and in /var/run; file
+ * descriptors; memory mapped into the process from domains being
+ * managed (grant maps); Xen event channels.  Use of libxl in
+ * processes which fork long-lived children is not recommended for
+ * this reason.  libxl_postfork_child_noexec is provided so that
+ * an application can make further libxl calls in a child which
+ * is going to exec or exit soon.
+ */
+void libxl_postfork_child_noexec(libxl_ctx *ctx);
+
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/include/libxl_json.h b/tools/libs/light/include/libxl_json.h
new file mode 100644 (file)
index 0000000..260783b
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef LIBXL_JSON_H
+#define LIBXL_JSON_H
+
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_parse.h>
+
+#ifdef HAVE_YAJL_YAJL_VERSION_H
+#  include <yajl/yajl_version.h>
+#endif
+
+yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val);
+yajl_gen_status libxl_defbool_gen_json(yajl_gen hand, libxl_defbool *p);
+yajl_gen_status libxl_uuid_gen_json(yajl_gen hand, libxl_uuid *p);
+yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *p);
+yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand, libxl_bitmap *p);
+yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
+                                                 libxl_cpuid_policy_list *p);
+yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *p);
+yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
+                                              libxl_key_value_list *p);
+yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand, libxl_hwcap *p);
+yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p);
+
+#include <_libxl_types_json.h>
+
+/* YAJL version check */
+#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
+#  define HAVE_YAJL_V2 1
+#endif
+
+#ifdef HAVE_YAJL_V2
+
+typedef size_t libxl_yajl_length;
+
+static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
+                                            yajl_alloc_funcs *allocFuncs,
+                                            void *ctx)
+{
+    yajl_handle hand = yajl_alloc(callbacks, allocFuncs, ctx);
+    if (hand)
+        yajl_config(hand, yajl_allow_trailing_garbage, 1);
+    return hand;
+}
+
+static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
+{
+    yajl_gen g;
+    g = yajl_gen_alloc(allocFuncs);
+    if (g)
+        yajl_gen_config(g, yajl_gen_beautify, 1);
+    return g;
+}
+
+#else /* !HAVE_YAJL_V2 */
+
+#define yajl_complete_parse yajl_parse_complete
+
+typedef unsigned int libxl_yajl_length;
+
+static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
+                                            const yajl_alloc_funcs *allocFuncs,
+                                            void *ctx)
+{
+    yajl_parser_config cfg = {
+        .allowComments = 1,
+        .checkUTF8 = 1,
+    };
+    return yajl_alloc(callbacks, &cfg, allocFuncs, ctx);
+}
+
+static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
+{
+    yajl_gen_config conf = { 1, "    " };
+    return yajl_gen_alloc(&conf, allocFuncs);
+}
+
+#endif /* !HAVE_YAJL_V2 */
+
+yajl_gen_status libxl_domain_config_gen_json(yajl_gen hand,
+                                             libxl_domain_config *p);
+
+#endif /* LIBXL_JSON_H */
diff --git a/tools/libs/light/include/libxl_utils.h b/tools/libs/light/include/libxl_utils.h
new file mode 100644 (file)
index 0000000..46918ae
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef LIBXL_UTILS_H
+#define LIBXL_UTILS_H
+
+#include "libxl.h"
+
+#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
+const
+#endif
+char *libxl_basename(const char *name); /* returns string from strdup */
+
+unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus);
+  /* deprecated; see LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG in libxl.h */
+int libxl_name_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
+int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
+char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid);
+int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
+                                         uint32_t *poolid_r,
+                                         int *was_name_r);
+int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name, uint32_t *poolid);
+char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid);
+int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid);
+int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid);
+int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid);
+int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name);
+int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend);
+
+int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
+                             void **data_r, int *datalen_r);
+  /* Reads the contents of the plain file filename into a mallocd
+   * buffer.  Returns 0 or errno.  Any errors other than ENOENT are logged.
+   * If the file is empty, *data_r and *datalen_r are set to 0.
+   * On error, *data_r and *datalen_r are unchanged.
+   * data_r and/or datalen_r may be 0.
+   */
+
+int libxl_read_exactly(libxl_ctx *ctx, int fd, void *data, ssize_t sz,
+                       const char *filename, const char *what);
+int libxl_write_exactly(libxl_ctx *ctx, int fd, const void *data,
+                        ssize_t sz, const char *filename, const char *what);
+  /* Returns 0 or errno.  If file is truncated on reading, returns
+   * EPROTO and you have no way to tell how much was read.  Errors are
+   * logged using filename (which is only used for logging) and what
+   * (which may be 0). */
+
+int libxl_pipe(libxl_ctx *ctx, int pipes[2]);
+  /* Just like pipe(2), but log errors. */
+
+void libxl_report_child_exitstatus(libxl_ctx *ctx, xentoollog_level,
+                                   const char *what, pid_t pid, int status);
+    /* treats all exit statuses as errors; if that's not what you want,
+     * check status yourself first */
+
+int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
+                            const char *mac, libxl_device_nic *nic);
+int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid, int devid,
+                              libxl_device_nic *nic);
+
+int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, const char *vdev,
+                               libxl_device_disk *disk);
+
+int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
+                               libxl_uuid *uuid, libxl_device_vtpm *vtpm);
+int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
+                               int devid, libxl_device_vtpm *vtpm);
+int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid,
+                                  int devid, libxl_device_usbctrl *usbctrl);
+
+int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
+                              int devid, libxl_device_vkb *vkb);
+
+int libxl_devid_to_device_vdispl(libxl_ctx *ctx, uint32_t domid,
+                                 int devid, libxl_device_vdispl *vdispl);
+
+int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
+                               int devid, libxl_device_vsnd *vsnd);
+
+int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid,
+                                    int ctrl, int port,
+                                    libxl_device_usbdev *usbdev);
+
+int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits);
+    /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be
+     * called by the application when done. */
+void libxl_bitmap_copy_alloc(libxl_ctx *ctx, libxl_bitmap *dptr,
+                             const libxl_bitmap *sptr);
+void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
+                       const libxl_bitmap *sptr);
+int libxl_bitmap_is_full(const libxl_bitmap *bitmap);
+int libxl_bitmap_is_empty(const libxl_bitmap *bitmap);
+int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit);
+void libxl_bitmap_set(libxl_bitmap *bitmap, int bit);
+void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit);
+int libxl_bitmap_count_set(const libxl_bitmap *bitmap);
+int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
+                    const libxl_bitmap *map1,
+                    const libxl_bitmap *map2);
+int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
+                     const libxl_bitmap *map1,
+                     const libxl_bitmap *map2);
+char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap);
+static inline void libxl_bitmap_set_any(libxl_bitmap *bitmap)
+{
+    memset(bitmap->map, -1, bitmap->size);
+}
+static inline void libxl_bitmap_set_none(libxl_bitmap *bitmap)
+{
+    memset(bitmap->map, 0, bitmap->size);
+}
+static inline int libxl_bitmap_cpu_valid(libxl_bitmap *bitmap, int bit)
+{
+    return bit >= 0 && bit < (bitmap->size * 8);
+}
+#define libxl_for_each_bit(var, map) for (var = 0; var < (map).size * 8; var++)
+#define libxl_for_each_set_bit(v, m) for (v = 0; v < (m).size * 8; v++) \
+                                             if (libxl_bitmap_test(&(m), v))
+
+/*
+ * Compares two bitmaps bit by bit, up to nr_bits or, if nr_bits is 0, up
+ * to the size of the largest bitmap. If sizes does not match, bits past the
+ * of a bitmap are considered as being 0, which matches with the semantic and
+ * implementation of libxl_bitmap_test I think().
+ *
+ * So, basically, [0,1,0] and [0,1] are considered equal, while [0,1,1] and
+ * [0,1] are different.
+ */
+static inline int libxl_bitmap_equal(const libxl_bitmap *ba,
+                                     const libxl_bitmap *bb,
+                                     int nr_bits)
+{
+    int i;
+
+    if (nr_bits == 0)
+        nr_bits = ba->size > bb->size ? ba->size * 8 : bb->size * 8;
+
+    for (i = 0; i < nr_bits; i++) {
+        if (libxl_bitmap_test(ba, i) != libxl_bitmap_test(bb, i))
+            return 0;
+    }
+    return 1;
+}
+
+int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus);
+int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
+                            int max_nodes);
+int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
+                              int max_sockets);
+/* Fill socketmap with the CPU topology information on the system. */
+int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap);
+
+/* Populate cpumap with the cpus spanned by the nodes in nodemap */
+int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
+                            const libxl_bitmap *nodemap,
+                            libxl_bitmap *cpumap);
+/* Populate cpumap with the cpus spanned by node */
+int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
+                         libxl_bitmap *cpumap);
+/* Populate nodemap with the nodes of the cpus in cpumap */
+int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
+                            const libxl_bitmap *cpumap,
+                            libxl_bitmap *nodemap);
+
+ static inline uint32_t libxl__sizekb_to_mb(uint32_t s) {
+    return (s + 1023) / 1024;
+}
+
+void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src);
+
+
+#define LIBXL_FILLZERO(object) (memset(&(object), 0, sizeof((object))))
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/include/libxl_uuid.h b/tools/libs/light/include/libxl_uuid.h
new file mode 100644 (file)
index 0000000..17c8b97
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008,2010 Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef __LIBXL_UUID_H__
+#define __LIBXL_UUID_H__
+
+#define LIBXL_UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+#define LIBXL_UUID_FMTLEN ((2*16)+4) /* 16 hex bytes plus 4 hypens */
+#define LIBXL__UUID_BYTES(uuid) uuid[0], uuid[1], uuid[2], uuid[3], \
+                                uuid[4], uuid[5], uuid[6], uuid[7], \
+                                uuid[8], uuid[9], uuid[10], uuid[11], \
+                                uuid[12], uuid[13], uuid[14], uuid[15]
+#define LIBXL_UUID_BYTES(arg) LIBXL__UUID_BYTES((arg).uuid)
+
+typedef struct {
+    /* UUID as an octet stream in big-endian byte-order. */
+    unsigned char uuid[16];
+} libxl_uuid;
+
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040700
+#if defined(__linux__)
+
+#include <uuid/uuid.h>
+#include <stdint.h>
+
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+
+#include <uuid.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#else
+
+#error "Please update libxl_uuid.h for your OS"
+
+#endif
+#endif
+
+int libxl_uuid_is_nil(const libxl_uuid *uuid);
+void libxl_uuid_generate(libxl_uuid *uuid);
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in);
+void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
+                     const libxl_uuid *src);
+#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
+static inline void libxl_uuid_copy_0x040400(libxl_uuid *dst,
+                                            const libxl_uuid *src)
+{
+    libxl_uuid_copy(NULL, dst, src);
+}
+#define libxl_uuid_copy libxl_uuid_copy_0x040400
+#endif
+
+void libxl_uuid_clear(libxl_uuid *uuid);
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2);
+const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid);
+uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid);
+
+#endif /* __LIBXL_UUID_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl.c b/tools/libs/light/libxl.c
new file mode 100644 (file)
index 0000000..621acc8
--- /dev/null
@@ -0,0 +1,831 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+int libxl_ctx_alloc(libxl_ctx **pctx, int version,
+                    unsigned flags, xentoollog_logger * lg)
+{
+    libxl_ctx *ctx = NULL;
+    libxl__gc gc_buf, *gc = NULL;
+    int rc;
+
+    if (version != LIBXL_VERSION) { rc = ERROR_VERSION; goto out; }
+
+    ctx = malloc(sizeof(*ctx));
+    if (!ctx) {
+        xtl_log(lg, XTL_ERROR, errno, "libxl",
+                "%s:%d:%s: Failed to allocate context\n",
+                __FILE__, __LINE__, __func__);
+        rc = ERROR_NOMEM; goto out;
+    }
+
+    memset(ctx, 0, sizeof(libxl_ctx));
+    ctx->lg = lg;
+
+    /* First initialise pointers etc. (cannot fail) */
+
+    ctx->nogc_gc.alloc_maxsize = -1;
+    ctx->nogc_gc.owner = ctx;
+
+    LIBXL_TAILQ_INIT(&ctx->occurred);
+
+    ctx->osevent_hooks = 0;
+
+    ctx->poller_app = 0;
+    LIBXL_LIST_INIT(&ctx->pollers_event);
+    LIBXL_LIST_INIT(&ctx->pollers_idle);
+    LIBXL_LIST_INIT(&ctx->pollers_active);
+
+    LIBXL_LIST_INIT(&ctx->efds);
+    LIBXL_TAILQ_INIT(&ctx->etimes);
+
+    ctx->watch_slots = 0;
+    LIBXL_SLIST_INIT(&ctx->watch_freeslots);
+    libxl__ev_fd_init(&ctx->watch_efd);
+
+    ctx->xce = 0;
+    LIBXL_LIST_INIT(&ctx->evtchns_waiting);
+    libxl__ev_fd_init(&ctx->evtchn_efd);
+
+    LIBXL_LIST_INIT(&ctx->aos_inprogress);
+
+    LIBXL_TAILQ_INIT(&ctx->death_list);
+    libxl__ev_xswatch_init(&ctx->death_watch);
+
+    ctx->childproc_hooks = &libxl__childproc_default_hooks;
+    ctx->childproc_user = 0;
+
+    ctx->sigchld_selfpipe[0] = -1;
+    ctx->sigchld_selfpipe[1] = -1;
+    libxl__ev_fd_init(&ctx->sigchld_selfpipe_efd);
+
+    /* The mutex is special because we can't idempotently destroy it */
+
+    if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Failed to initialize mutex");
+        free(ctx);
+        ctx = 0;
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* Now ctx is safe for ctx_free; failures simply set rc and "goto out" */
+    LIBXL_INIT_GC(gc_buf,ctx);
+    gc = &gc_buf;
+    /* Now gc is useable */
+
+    rc = libxl__atfork_init(ctx);
+    if (rc) goto out;
+
+    ctx->poller_app = libxl__poller_get(gc);
+    if (!ctx->poller_app) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    ctx->xch = xc_interface_open(lg,lg,0);
+    if (!ctx->xch) {
+        LOGEV(ERROR, errno, "cannot open libxc handle");
+        rc = ERROR_FAIL; goto out;
+    }
+
+    ctx->xsh = xs_daemon_open();
+    if (!ctx->xsh)
+        ctx->xsh = xs_domain_open();
+    if (!ctx->xsh) {
+        LOGEV(ERROR, errno, "cannot connect to xenstore");
+        rc = ERROR_FAIL; goto out;
+    }
+
+    *pctx = ctx;
+    return 0;
+
+ out:
+    if (gc) libxl__free_all(gc);
+    libxl_ctx_free(ctx);
+    *pctx = NULL;
+    return rc;
+}
+
+static void free_disable_deaths(libxl__gc *gc,
+                                struct libxl__evgen_domain_death_list *l) {
+    libxl_evgen_domain_death *death;
+    while ((death = LIBXL_TAILQ_FIRST(l)))
+        libxl__evdisable_domain_death(gc, death);
+}
+
+static void discard_events(struct libxl__event_list *l) {
+    /* doesn't bother unlinking from the list, so l is corrupt on return */
+    libxl_event *ev, *next;
+    LIBXL_TAILQ_FOREACH_SAFE(ev, l, link, next)
+        libxl_event_free(0, ev);
+}
+
+int libxl_ctx_free(libxl_ctx *ctx)
+{
+    if (!ctx) return 0;
+
+    int i;
+    GC_INIT(ctx);
+
+    CTX_LOCK;
+    assert(!ctx->osevent_in_hook);
+    CTX->osevent_in_hook += 1000; /* make violations easier to debug */
+
+    /* Deregister all libxl__ev_KINDs: */
+
+    free_disable_deaths(gc, &CTX->death_list);
+    free_disable_deaths(gc, &CTX->death_reported);
+
+    libxl_evgen_disk_eject *eject;
+    while ((eject = LIBXL_LIST_FIRST(&CTX->disk_eject_evgens)))
+        libxl__evdisable_disk_eject(gc, eject);
+
+    libxl_childproc_setmode(CTX,0,0);
+    for (i = 0; i < ctx->watch_nslots; i++)
+        assert(!libxl__watch_slot_contents(gc, i));
+    assert(!libxl__ev_fd_isregistered(&ctx->watch_efd));
+    assert(!libxl__ev_fd_isregistered(&ctx->evtchn_efd));
+    assert(!libxl__ev_fd_isregistered(&ctx->sigchld_selfpipe_efd));
+
+    /* Now there should be no more events requested from the application: */
+
+    assert(LIBXL_LIST_EMPTY(&ctx->efds));
+    assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
+    assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting));
+    assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress));
+
+    if (ctx->xch) xc_interface_close(ctx->xch);
+    libxl_version_info_dispose(&ctx->version_info);
+    if (ctx->xsh) xs_daemon_close(ctx->xsh);
+    if (ctx->xce) xenevtchn_close(ctx->xce);
+
+    libxl__poller_put(ctx, ctx->poller_app);
+    ctx->poller_app = NULL;
+    assert(LIBXL_LIST_EMPTY(&ctx->pollers_event));
+    assert(LIBXL_LIST_EMPTY(&ctx->pollers_active));
+    libxl__poller *poller, *poller_tmp;
+    LIBXL_LIST_FOREACH_SAFE(poller, &ctx->pollers_idle, entry, poller_tmp) {
+        libxl__poller_dispose(poller);
+        free(poller);
+    }
+
+    free(ctx->watch_slots);
+
+    discard_events(&ctx->occurred);
+
+    /* If we have outstanding children, then the application inherits
+     * them; we wish the application good luck with understanding
+     * this if and when it reaps them. */
+    libxl__sigchld_notneeded(gc);
+    libxl__pipe_close(ctx->sigchld_selfpipe);
+
+    CTX_UNLOCK;
+    pthread_mutex_destroy(&ctx->lock);
+
+    GC_FREE;
+    free(ctx);
+    return 0;
+}
+
+void libxl_string_list_dispose(libxl_string_list *psl)
+{
+    int i;
+    libxl_string_list sl = *psl;
+
+    if (!sl)
+        return;
+
+    for (i = 0; sl[i] != NULL; i++) {
+        free(sl[i]);
+        sl[i] = NULL;
+    }
+    free(sl);
+    *psl = NULL;
+}
+
+void libxl_string_list_copy(libxl_ctx *ctx,
+                            libxl_string_list *dst,
+                            const libxl_string_list *src)
+{
+    GC_INIT(ctx);
+    int i, len;
+
+    if (!*src) {
+        *dst = NULL;
+        goto out;
+    }
+
+    len = libxl_string_list_length(src);
+    /* one extra slot for sentinel */
+    *dst = libxl__calloc(NOGC, len + 1, sizeof(char *));
+
+    for (i = 0; i < len; i++)
+        (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
+
+out:
+    GC_FREE;
+}
+
+int libxl_string_list_length(const libxl_string_list *psl)
+{
+    int i = 0;
+
+    if (*psl)
+        while ((*psl)[i])
+            i++;
+
+    return i;
+}
+
+int libxl_key_value_list_length(const libxl_key_value_list *pkvl)
+{
+    int i = 0;
+    libxl_key_value_list kvl = *pkvl;
+
+    if (kvl) {
+        while (kvl[2 * i]) /* Only checks keys */
+            i++;
+    }
+
+    return i;
+}
+
+void libxl_key_value_list_dispose(libxl_key_value_list *pkvl)
+{
+    int i;
+    libxl_key_value_list kvl = *pkvl;
+
+    if (!kvl)
+        return;
+
+    for (i = 0; kvl[i] != NULL; i += 2) {
+        free(kvl[i]);
+        kvl[i] = NULL;
+        if (kvl[i + 1]) {
+            free(kvl[i + 1]);
+            kvl[i+1] = NULL;
+        }
+    }
+    free(kvl);
+    *pkvl = NULL;
+}
+
+void libxl_key_value_list_copy(libxl_ctx *ctx,
+                               libxl_key_value_list *dst,
+                               const libxl_key_value_list *src)
+{
+    GC_INIT(ctx);
+    int i, len;
+
+    if (*src == NULL) {
+        *dst = NULL;
+        goto out;
+    }
+
+    len = libxl_key_value_list_length(src);
+    /* one extra slot for sentinel */
+    *dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *));
+
+    for (i = 0; i < len * 2; i += 2) {
+        (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
+        if ((*src)[i+1])
+            (*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]);
+        else
+            (*dst)[i+1] = NULL;
+    }
+
+out:
+    GC_FREE;
+}
+
+void libxl_defbool_set(libxl_defbool *db, bool b)
+{
+    db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE;
+}
+
+void libxl_defbool_unset(libxl_defbool *db)
+{
+    db->val = LIBXL__DEFBOOL_DEFAULT;
+}
+
+bool libxl_defbool_is_default(libxl_defbool db)
+{
+    return !db.val;
+}
+
+void libxl_defbool_setdefault(libxl_defbool *db, bool b)
+{
+    if (libxl_defbool_is_default(*db))
+        libxl_defbool_set(db, b);
+}
+
+bool libxl_defbool_val(libxl_defbool db)
+{
+    assert(!libxl_defbool_is_default(db));
+    return db.val > 0;
+}
+
+const char *libxl_defbool_to_string(libxl_defbool b)
+{
+    if (b.val < 0)
+        return LIBXL__DEFBOOL_STR_FALSE;
+    else if (b.val > 0)
+        return LIBXL__DEFBOOL_STR_TRUE;
+    else
+        return LIBXL__DEFBOOL_STR_DEFAULT;
+}
+
+/******************************************************************************/
+int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo)
+{
+    xc_physinfo_t xcphysinfo = { 0 };
+    int rc;
+    long l;
+    GC_INIT(ctx);
+
+    rc = xc_physinfo(ctx->xch, &xcphysinfo);
+    if (rc != 0) {
+        LOGE(ERROR, "getting physinfo");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    physinfo->threads_per_core = xcphysinfo.threads_per_core;
+    physinfo->cores_per_socket = xcphysinfo.cores_per_socket;
+    physinfo->max_cpu_id = xcphysinfo.max_cpu_id;
+    physinfo->nr_cpus = xcphysinfo.nr_cpus;
+    physinfo->cpu_khz = xcphysinfo.cpu_khz;
+    physinfo->total_pages = xcphysinfo.total_pages;
+    physinfo->free_pages = xcphysinfo.free_pages;
+    physinfo->scrub_pages = xcphysinfo.scrub_pages;
+    physinfo->outstanding_pages = xcphysinfo.outstanding_pages;
+    physinfo->max_possible_mfn = xcphysinfo.max_mfn;
+    l = xc_sharing_freed_pages(ctx->xch);
+    if (l < 0 && errno == ENOSYS) {
+        l = 0;
+    } else if (l < 0) {
+        LOGEV(ERROR, l, "getting sharing freed pages");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    physinfo->sharing_freed_pages = l;
+    l = xc_sharing_used_frames(ctx->xch);
+    if (l < 0 && errno == ENOSYS) {
+        l = 0;
+    } else if (l < 0) {
+        LOGEV(ERROR, l, "getting sharing used frames");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    physinfo->sharing_used_frames = l;
+    physinfo->nr_nodes = xcphysinfo.nr_nodes;
+    memcpy(physinfo->hw_cap,xcphysinfo.hw_cap, sizeof(physinfo->hw_cap));
+
+    physinfo->cap_hvm = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm);
+    physinfo->cap_pv = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_pv);
+    physinfo->cap_hvm_directio =
+        !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_directio);
+    physinfo->cap_hap = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hap);
+    physinfo->cap_shadow =
+        !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_shadow);
+    physinfo->cap_iommu_hap_pt_share =
+        !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_iommu_hap_pt_share);
+
+    GC_FREE;
+    return 0;
+}
+
+libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out)
+{
+    GC_INIT(ctx);
+    xc_cputopo_t *cputopo;
+    libxl_cputopology *ret = NULL;
+    int i;
+    unsigned num_cpus = 0;
+
+    /* Setting buffer to NULL makes the call return number of CPUs */
+    if (xc_cputopoinfo(ctx->xch, &num_cpus, NULL))
+    {
+        LOGE(ERROR, "Unable to determine number of CPUS");
+        goto out;
+    }
+
+    cputopo = libxl__zalloc(gc, sizeof(*cputopo) * num_cpus);
+
+    if (xc_cputopoinfo(ctx->xch, &num_cpus, cputopo)) {
+        LOGE(ERROR, "CPU topology info hypercall failed");
+        goto out;
+    }
+
+    ret = libxl__zalloc(NOGC, sizeof(libxl_cputopology) * num_cpus);
+
+    for (i = 0; i < num_cpus; i++) {
+#define V(map, i, invalid) ( cputopo[i].map == invalid) ? \
+   LIBXL_CPUTOPOLOGY_INVALID_ENTRY : cputopo[i].map
+        ret[i].core = V(core, i, XEN_INVALID_CORE_ID);
+        ret[i].socket = V(socket, i, XEN_INVALID_SOCKET_ID);
+        ret[i].node = V(node, i, XEN_INVALID_NODE_ID);
+#undef V
+    }
+
+    *nb_cpu_out = num_cpus;
+
+ out:
+    GC_FREE;
+    return ret;
+}
+
+libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs)
+{
+    GC_INIT(ctx);
+    physdev_pci_device_t *devs;
+    uint32_t *nodes;
+    libxl_pcitopology *ret = NULL;
+    int i, rc;
+
+    *num_devs = libxl__pci_numdevs(gc);
+    if (*num_devs < 0) {
+        LOG(ERROR, "Unable to determine number of PCI devices, rc %d",
+            *num_devs);
+        goto out;
+    }
+
+    devs = libxl__zalloc(gc, sizeof(*devs) * *num_devs);
+    nodes = libxl__zalloc(gc, sizeof(*nodes) * *num_devs);
+
+    rc = libxl__pci_topology_init(gc, devs, *num_devs);
+    if (rc) {
+        LOG(ERROR, "Cannot initialize PCI hypercall structure, rc %d", rc);
+        goto out;
+    }
+
+    if (xc_pcitopoinfo(ctx->xch, *num_devs, devs, nodes) != 0) {
+        LOGE(ERROR, "PCI topology info hypercall failed");
+        goto out;
+    }
+
+    ret = libxl__zalloc(NOGC, sizeof(libxl_pcitopology) * *num_devs);
+
+    for (i = 0; i < *num_devs; i++) {
+        ret[i].seg = devs[i].seg;
+        ret[i].bus = devs[i].bus;
+        ret[i].devfn = devs[i].devfn;
+        ret[i].node = ((nodes[i] == XEN_INVALID_NODE_ID) ||
+                       (nodes[i] == XEN_INVALID_DEV)) ?
+            LIBXL_PCITOPOLOGY_INVALID_ENTRY : nodes[i];
+    }
+
+ out:
+    GC_FREE;
+    return ret;
+}
+
+libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr)
+{
+    GC_INIT(ctx);
+    xc_meminfo_t *meminfo;
+    uint32_t *distance;
+    libxl_numainfo *ret = NULL;
+    int i, j;
+    unsigned num_nodes = 0;
+
+    if (xc_numainfo(ctx->xch, &num_nodes, NULL, NULL)) {
+        LOGE(ERROR, "Unable to determine number of nodes");
+        goto out;
+    }
+
+    meminfo = libxl__zalloc(gc, sizeof(*meminfo) * num_nodes);
+    distance = libxl__zalloc(gc, sizeof(*distance) * num_nodes * num_nodes);
+
+    if (xc_numainfo(ctx->xch, &num_nodes, meminfo, distance)) {
+        LOGE(ERROR, "getting numainfo");
+        goto out;
+    }
+
+    *nr = num_nodes;
+
+    ret = libxl__zalloc(NOGC, sizeof(libxl_numainfo) * num_nodes);
+    for (i = 0; i < num_nodes; i++)
+        ret[i].dists = libxl__calloc(NOGC, num_nodes, sizeof(*distance));
+
+    for (i = 0; i < num_nodes; i++) {
+#define V(val, invalid) (val == invalid) ? \
+       LIBXL_NUMAINFO_INVALID_ENTRY : val
+        ret[i].size = V(meminfo[i].memsize, XEN_INVALID_MEM_SZ);
+        ret[i].free = V(meminfo[i].memfree, XEN_INVALID_MEM_SZ);
+        ret[i].num_dists = num_nodes;
+        for (j = 0; j < ret[i].num_dists; j++) {
+            unsigned idx = i * num_nodes + j;
+            ret[i].dists[j] = V(distance[idx], XEN_INVALID_NODE_DIST);
+        }
+#undef V
+    }
+
+ out:
+    GC_FREE;
+    return ret;
+}
+
+static int libxl__xc_version_wrap(libxl__gc *gc, libxl_version_info *info,
+                                  xen_build_id_t *build)
+{
+    int r;
+
+    r = xc_version(CTX->xch, XENVER_build_id, build);
+    switch (r) {
+    case -EPERM:
+    case -ENODATA:
+    case 0:
+        info->build_id = libxl__strdup(NOGC, "");
+        break;
+
+    case -ENOBUFS:
+        break;
+
+    default:
+        if (r > 0) {
+            unsigned int i;
+
+            info->build_id = libxl__zalloc(NOGC, (r * 2) + 1);
+
+            for (i = 0; i < r ; i++)
+                snprintf(&info->build_id[i * 2], 3, "%02hhx", build->buf[i]);
+
+            r = 0;
+        }
+        break;
+    }
+    return r;
+}
+
+const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx)
+{
+    GC_INIT(ctx);
+    union {
+        xen_extraversion_t xen_extra;
+        xen_compile_info_t xen_cc;
+        xen_changeset_info_t xen_chgset;
+        xen_capabilities_info_t xen_caps;
+        xen_platform_parameters_t p_parms;
+        xen_commandline_t xen_commandline;
+        xen_build_id_t build_id;
+    } u;
+    long xen_version;
+    int r;
+    libxl_version_info *info = &ctx->version_info;
+
+    if (info->xen_version_extra != NULL)
+        goto out;
+
+    xen_version = xc_version(ctx->xch, XENVER_version, NULL);
+    info->xen_version_major = xen_version >> 16;
+    info->xen_version_minor = xen_version & 0xFF;
+
+    xc_version(ctx->xch, XENVER_extraversion, &u.xen_extra);
+    info->xen_version_extra = libxl__strdup(NOGC, u.xen_extra);
+
+    xc_version(ctx->xch, XENVER_compile_info, &u.xen_cc);
+    info->compiler = libxl__strdup(NOGC, u.xen_cc.compiler);
+    info->compile_by = libxl__strdup(NOGC, u.xen_cc.compile_by);
+    info->compile_domain = libxl__strdup(NOGC, u.xen_cc.compile_domain);
+    info->compile_date = libxl__strdup(NOGC, u.xen_cc.compile_date);
+
+    xc_version(ctx->xch, XENVER_capabilities, &u.xen_caps);
+    info->capabilities = libxl__strdup(NOGC, u.xen_caps);
+
+    xc_version(ctx->xch, XENVER_changeset, &u.xen_chgset);
+    info->changeset = libxl__strdup(NOGC, u.xen_chgset);
+
+    xc_version(ctx->xch, XENVER_platform_parameters, &u.p_parms);
+    info->virt_start = u.p_parms.virt_start;
+
+    info->pagesize = xc_version(ctx->xch, XENVER_pagesize, NULL);
+
+    xc_version(ctx->xch, XENVER_commandline, &u.xen_commandline);
+    info->commandline = libxl__strdup(NOGC, u.xen_commandline);
+
+    u.build_id.len = sizeof(u) - sizeof(u.build_id);
+    r = libxl__xc_version_wrap(gc, info, &u.build_id);
+    if (r == -ENOBUFS) {
+            xen_build_id_t *build_id;
+
+            build_id = libxl__zalloc(gc, info->pagesize);
+            build_id->len = info->pagesize - sizeof(*build_id);
+            r = libxl__xc_version_wrap(gc, info, build_id);
+            if (r) LOGEV(ERROR, r, "getting build_id");
+    }
+ out:
+    GC_FREE;
+    return info;
+}
+
+int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq)
+{
+    GC_INIT(ctx);
+    char *dompath = libxl__xs_get_dompath(gc, domid);
+
+    libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/control/sysrq", dompath),
+                     "%c", sysrq);
+
+    GC_FREE;
+    return 0;
+}
+
+int libxl_send_debug_keys(libxl_ctx *ctx, char *keys)
+{
+    int ret;
+    GC_INIT(ctx);
+    ret = xc_send_debug_keys(ctx->xch, keys);
+    if ( ret < 0 ) {
+        LOGE(ERROR, "sending debug keys");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    GC_FREE;
+    return 0;
+}
+
+int libxl_set_parameters(libxl_ctx *ctx, char *params)
+{
+    int ret;
+    GC_INIT(ctx);
+    char *par, *val, *end, *path;
+    xenhypfs_handle *hypfs;
+
+    hypfs = xenhypfs_open(ctx->lg, 0);
+    if (!hypfs) {
+        LOGE(ERROR, "opening Xen hypfs");
+        ret = ERROR_FAIL;
+        goto out;
+    }
+
+    while (isblank(*params))
+        params++;
+
+    for (par = params; *par; par = end) {
+        end = strchr(par, ' ');
+        if (!end)
+            end = par + strlen(par);
+
+        val = strchr(par, '=');
+        if (val > end)
+            val = NULL;
+        if (!val && !strncmp(par, "no", 2)) {
+            path = libxl__sprintf(gc, "/params/%s", par + 2);
+            path[end - par - 2 + 8] = 0;
+            val = "no";
+            par += 2;
+        } else {
+            path = libxl__sprintf(gc, "/params/%s", par);
+            path[val - par + 8] = 0;
+            val = libxl__strndup(gc, val + 1, end - val - 1);
+        }
+
+       LOG(DEBUG, "setting node \"%s\" to value \"%s\"", path, val);
+        ret = xenhypfs_write(hypfs, path, val);
+        if (ret < 0) {
+            LOGE(ERROR, "setting parameters");
+            ret = ERROR_FAIL;
+            goto out;
+        }
+
+        while (isblank(*end))
+            end++;
+    }
+
+    ret = 0;
+
+out:
+    xenhypfs_close(hypfs);
+    GC_FREE;
+    return ret;
+}
+
+static int fd_set_flags(libxl_ctx *ctx, int fd,
+                        int fcntlgetop, int fcntlsetop, const char *fl,
+                        int flagmask, int set_p)
+{
+    int flags, r;
+    GC_INIT(ctx);
+
+    flags = fcntl(fd, fcntlgetop);
+    if (flags == -1) {
+        LOGE(ERROR, "fcntl(,F_GET%s) failed", fl);
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+
+    if (set_p)
+        flags |= flagmask;
+    else
+        flags &= ~flagmask;
+
+    r = fcntl(fd, fcntlsetop, flags);
+    if (r == -1) {
+        LOGE(ERROR, "fcntl(,F_SET%s) failed", fl);
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return 0;
+}
+
+int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec)
+  { return fd_set_flags(ctx,fd, F_GETFD,F_SETFD,"FD", FD_CLOEXEC, cloexec); }
+
+int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock)
+  { return fd_set_flags(ctx,fd, F_GETFL,F_SETFL,"FL", O_NONBLOCK, nonblock); }
+
+int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
+                                int mask, int val, int *r_oldflags)
+{
+    int rc, ret, fdfl;
+
+    fdfl = fcntl(fd, F_GETFL);
+    if (fdfl < 0) {
+        LOGE(ERROR, "failed to fcntl.F_GETFL for fd %d", fd);
+        rc = ERROR_FAIL;
+        goto out_err;
+    }
+
+    LOG(DEBUG, "fnctl F_GETFL flags for fd %d are 0x%x", fd, fdfl);
+
+    if (r_oldflags)
+        *r_oldflags = fdfl;
+
+    fdfl &= mask;
+    fdfl |= val;
+
+    LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
+
+    ret = fcntl(fd, F_SETFL, fdfl);
+    if (ret < 0) {
+        LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
+        rc = ERROR_FAIL;
+        goto out_err;
+    }
+
+    rc = 0;
+
+out_err:
+    return rc;
+}
+
+int libxl__fd_flags_restore(libxl__gc *gc, int fd, int fdfl)
+{
+    int ret, rc;
+
+    LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
+
+    ret = fcntl(fd, F_SETFL, fdfl);
+    if (ret < 0) {
+        LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
+        rc = ERROR_FAIL;
+        goto out_err;
+    }
+
+    rc = 0;
+
+out_err:
+    return rc;
+
+}
+
+void libxl_hwcap_copy(libxl_ctx *ctx,libxl_hwcap *dst, const libxl_hwcap *src)
+{
+    int i;
+
+    for (i = 0; i < 8; i++)
+        (*dst)[i] = (*src)[i];
+}
+
+void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src)
+{
+    int i;
+
+    for (i = 0; i < 6; i++)
+        (*dst)[i] = (*src)[i];
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_9pfs.c b/tools/libs/light/libxl_9pfs.c
new file mode 100644 (file)
index 0000000..e5c41e9
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017      Aporeto
+ * Author Stefano Stabellini <stefano@aporeto.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__device_p9_setdefault(libxl__gc *gc, uint32_t domid,
+                                       libxl_device_p9 *p9, bool hotplug)
+{
+    return libxl__resolve_domid(gc, p9->backend_domname, &p9->backend_domid);
+}
+
+static int libxl__set_xenstore_p9(libxl__gc *gc, uint32_t domid,
+                                  libxl_device_p9 *p9,
+                                  flexarray_t *back, flexarray_t *front,
+                                  flexarray_t *ro_front)
+{
+    flexarray_append_pair(back, "path", p9->path);
+    flexarray_append_pair(back, "security_model", p9->security_model);
+
+    flexarray_append_pair(front, "tag", p9->tag);
+
+    return 0;
+}
+
+#define libxl__add_p9s NULL
+#define libxl_device_p9_list NULL
+#define libxl_device_p9_compare NULL
+
+static LIBXL_DEFINE_UPDATE_DEVID(p9)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(p9)
+
+LIBXL_DEFINE_DEVICE_REMOVE(p9)
+
+DEFINE_DEVICE_TYPE_STRUCT(p9, 9PFS,
+    .skip_attach = 1,
+    .set_xenstore_config = (device_set_xenstore_config_fn_t)
+                           libxl__set_xenstore_p9,
+);
diff --git a/tools/libs/light/libxl_aoutils.c b/tools/libs/light/libxl_aoutils.c
new file mode 100644 (file)
index 0000000..c4c095a
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2010      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*----- xswait -----*/
+
+static libxl__ev_xswatch_callback xswait_xswatch_callback;
+static libxl__ev_time_callback xswait_timeout_callback;
+static void xswait_report_error(libxl__egc*, libxl__xswait_state*, int rc);
+
+void libxl__xswait_init(libxl__xswait_state *xswa)
+{
+    libxl__ev_time_init(&xswa->time_ev);
+    libxl__ev_xswatch_init(&xswa->watch_ev);
+}
+
+void libxl__xswait_stop(libxl__gc *gc, libxl__xswait_state *xswa)
+{
+    libxl__ev_time_deregister(gc, &xswa->time_ev);
+    libxl__ev_xswatch_deregister(gc, &xswa->watch_ev);
+}
+
+bool libxl__xswait_inuse(const libxl__xswait_state *xswa)
+{
+    bool time_inuse = libxl__ev_time_isregistered(&xswa->time_ev);
+    bool watch_inuse = libxl__ev_xswatch_isregistered(&xswa->watch_ev);
+    assert(time_inuse == watch_inuse);
+    return time_inuse;
+}
+
+int libxl__xswait_start(libxl__gc *gc, libxl__xswait_state *xswa)
+{
+    int rc;
+
+    rc = libxl__ev_time_register_rel(xswa->ao, &xswa->time_ev,
+                                     xswait_timeout_callback, xswa->timeout_ms);
+    if (rc) goto err;
+
+    rc = libxl__ev_xswatch_register(gc, &xswa->watch_ev,
+                                    xswait_xswatch_callback, xswa->path);
+    if (rc) goto err;
+
+    return 0;
+
+ err:
+    libxl__xswait_stop(gc, xswa);
+    return rc;
+}
+
+void xswait_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *xsw,
+                             const char *watch_path, const char *event_path)
+{
+    EGC_GC;
+    libxl__xswait_state *xswa = CONTAINER_OF(xsw, *xswa, watch_ev);
+    int rc;
+    const char *data;
+
+    if (xswa->path[0] == '@') {
+        data = 0;
+    } else {
+        rc = libxl__xs_read_checked(gc, XBT_NULL, xswa->path, &data);
+        if (rc) { xswait_report_error(egc, xswa, rc); return; }
+    }
+
+    xswa->callback(egc, xswa, 0, data);
+}
+
+void xswait_timeout_callback(libxl__egc *egc, libxl__ev_time *ev,
+                             const struct timeval *requested_abs,
+                             int rc)
+{
+    EGC_GC;
+    libxl__xswait_state *xswa = CONTAINER_OF(ev, *xswa, time_ev);
+    LOG(DEBUG, "%s: xswait timeout (path=%s)", xswa->what, xswa->path);
+    xswait_report_error(egc, xswa, rc);
+}
+
+static void xswait_report_error(libxl__egc *egc, libxl__xswait_state *xswa,
+                                int rc)
+{
+    EGC_GC;
+    libxl__xswait_stop(gc, xswa);
+    xswa->callback(egc, xswa, rc, 0);
+}
+
+
+/*----- data copier -----*/
+
+void libxl__datacopier_init(libxl__datacopier_state *dc)
+{
+    assert(dc->ao);
+    libxl__ao_abortable_init(&dc->abrt);
+    libxl__ev_fd_init(&dc->toread);
+    libxl__ev_fd_init(&dc->towrite);
+    LIBXL_TAILQ_INIT(&dc->bufs);
+}
+
+void libxl__datacopier_kill(libxl__datacopier_state *dc)
+{
+    STATE_AO_GC(dc->ao);
+    libxl__datacopier_buf *buf, *tbuf;
+
+    libxl__ao_abortable_deregister(&dc->abrt);
+    libxl__ev_fd_deregister(gc, &dc->toread);
+    libxl__ev_fd_deregister(gc, &dc->towrite);
+    LIBXL_TAILQ_FOREACH_SAFE(buf, &dc->bufs, entry, tbuf)
+        free(buf);
+    LIBXL_TAILQ_INIT(&dc->bufs);
+}
+
+static void datacopier_callback(libxl__egc *egc, libxl__datacopier_state *dc,
+                                int rc, int onwrite, int errnoval)
+{
+    libxl__datacopier_kill(dc);
+    dc->callback(egc, dc, rc, onwrite, errnoval);
+}
+
+static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
+                                int fd, short events, short revents);
+
+static void datacopier_check_state(libxl__egc *egc, libxl__datacopier_state *dc)
+{
+    STATE_AO_GC(dc->ao);
+    int rc;
+    
+    if (dc->used && !dc->readbuf) {
+        if (!libxl__ev_fd_isregistered(&dc->towrite)) {
+            rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
+                                       dc->writefd, POLLOUT);
+            if (rc) {
+                LOG(ERROR, "unable to establish write event on %s"
+                    " during copy of %s", dc->writewhat, dc->copywhat);
+                datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
+                return;
+            }
+        }
+    } else if (!libxl__ev_fd_isregistered(&dc->toread) ||
+               dc->bytes_to_read == 0) {
+        /* we have had eof */
+        datacopier_callback(egc, dc, 0, 0, 0);
+        return;
+    } else {
+        /* nothing buffered, but still reading */
+        libxl__ev_fd_deregister(gc, &dc->towrite);
+    }
+}
+
+void libxl__datacopier_prefixdata(libxl__egc *egc, libxl__datacopier_state *dc,
+                                  const void *data, size_t len)
+{
+    EGC_GC;
+    libxl__datacopier_buf *buf;
+    const uint8_t *ptr;
+
+    /*
+     * It is safe for this to be called immediately after _start, as
+     * is documented in the public comment.  _start's caller must have
+     * the ctx locked, so other threads don't get to mess with the
+     * contents, and the fd events cannot happen reentrantly.  So we
+     * are guaranteed to beat the first data from the read fd.
+     */
+
+    assert(len < dc->maxsz - dc->used);
+
+    for (ptr = data; len; len -= buf->used, ptr += buf->used) {
+        buf = libxl__malloc(NOGC, sizeof(*buf));
+        buf->used = min(len, sizeof(buf->buf));
+        memcpy(buf->buf, ptr, buf->used);
+
+        dc->used += buf->used;
+        LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
+    }
+}
+
+static int datacopier_pollhup_handled(libxl__egc *egc,
+                                      libxl__datacopier_state *dc,
+                                      int fd, short revents, int onwrite)
+{
+    STATE_AO_GC(dc->ao);
+
+    if (dc->callback_pollhup && (revents & POLLHUP)) {
+        LOG(DEBUG, "received POLLHUP on fd %d: %s during copy of %s",
+            fd, onwrite ? dc->writewhat : dc->readwhat, dc->copywhat);
+        libxl__datacopier_kill(dc);
+        dc->callback_pollhup(egc, dc, ERROR_FAIL, onwrite, -1);
+        return 1;
+    }
+    return 0;
+}
+
+static void datacopier_abort(libxl__egc *egc, libxl__ao_abortable *abrt,
+                             int rc)
+{
+    libxl__datacopier_state *dc = CONTAINER_OF(abrt, *dc, abrt);
+    STATE_AO_GC(dc->ao);
+
+    datacopier_callback(egc, dc, rc, -1, 0);
+}
+
+static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev,
+                                int fd, short events, short revents) {
+    libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread);
+    STATE_AO_GC(dc->ao);
+
+    if (datacopier_pollhup_handled(egc, dc, fd, revents, 0))
+        return;
+
+    if (revents & ~(POLLIN|POLLHUP)) {
+        LOG(ERROR, "unexpected poll event 0x%x on fd %d (expected POLLIN "
+            "and/or POLLHUP) reading %s during copy of %s",
+            revents, fd, dc->readwhat, dc->copywhat);
+        datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
+        return;
+    }
+    assert(revents & (POLLIN|POLLHUP));
+    for (;;) {
+        libxl__datacopier_buf *buf = NULL;
+        int r;
+
+        if (dc->readbuf) {
+            r = read(ev->fd, dc->readbuf + dc->used, dc->bytes_to_read);
+        } else {
+            while (dc->used >= dc->maxsz) {
+                libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs);
+                dc->used -= rm->used;
+                assert(dc->used >= 0);
+                LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry);
+                free(rm);
+            }
+
+            buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs);
+            if (!buf || buf->used >= sizeof(buf->buf)) {
+                buf = libxl__malloc(NOGC, sizeof(*buf));
+                buf->used = 0;
+                LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
+            }
+            r = read(ev->fd, buf->buf + buf->used,
+                     min_t(size_t, sizeof(buf->buf) - buf->used,
+                           (dc->bytes_to_read == -1) ? SIZE_MAX : dc->bytes_to_read));
+        }
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            assert(errno);
+            if (errno == EWOULDBLOCK) {
+                if (revents & POLLHUP) {
+                    LOG(ERROR,
+                        "poll reported HUP but fd read gave EWOULDBLOCK"
+                        " on %s during copy of %s",
+                        dc->readwhat, dc->copywhat);
+                    datacopier_callback(egc, dc, ERROR_FAIL, -1, 0);
+                    return;
+                }
+                break;
+            }
+            LOGE(ERROR, "error reading %s during copy of %s",
+                 dc->readwhat, dc->copywhat);
+            datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
+            return;
+        }
+        if (r == 0) {
+            if (dc->callback_pollhup) {
+                /* It might be that this "eof" is actually a HUP.  If
+                 * the caller cares about the difference,
+                 * double-check using poll(2). */
+                struct pollfd hupchk;
+                hupchk.fd = ev->fd;
+                hupchk.events = POLLIN;
+                hupchk.revents = 0;
+                r = poll(&hupchk, 1, 0);
+                if (r < 0)
+                    LIBXL__EVENT_DISASTER(gc,
+     "unexpected failure polling fd for datacopier eof hup check",
+                                  errno, 0);
+                if (datacopier_pollhup_handled(egc, dc, fd, hupchk.revents, 0))
+                    return;
+            }
+            libxl__ev_fd_deregister(gc, &dc->toread);
+            break;
+        }
+        if (dc->log) {
+            int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log);
+            if (wrote != r) {
+                assert(ferror(dc->log));
+                assert(errno);
+                LOGE(ERROR, "error logging %s", dc->copywhat);
+                datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
+                return;
+            }
+        }
+        if (!dc->readbuf) {
+            buf->used += r;
+            assert(buf->used <= sizeof(buf->buf));
+        }
+        dc->used += r;
+        if (dc->bytes_to_read > 0)
+            dc->bytes_to_read -= r;
+        if (dc->bytes_to_read == 0)
+            break;
+    }
+    datacopier_check_state(egc, dc);
+}
+
+static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
+                                int fd, short events, short revents) {
+    libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, towrite);
+    STATE_AO_GC(dc->ao);
+
+    if (datacopier_pollhup_handled(egc, dc, fd, revents, 1))
+        return;
+
+    if (revents & ~POLLOUT) {
+        LOG(ERROR, "unexpected poll event 0x%x on fd %d (should be POLLOUT)"
+            " writing %s during copy of %s",
+            revents, fd, dc->writewhat, dc->copywhat);
+        datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
+        return;
+    }
+    assert(revents & POLLOUT);
+    for (;;) {
+        libxl__datacopier_buf *buf = LIBXL_TAILQ_FIRST(&dc->bufs);
+        if (!buf)
+            break;
+        if (!buf->used) {
+            LIBXL_TAILQ_REMOVE(&dc->bufs, buf, entry);
+            free(buf);
+            continue;
+        }
+        int r = write(ev->fd, buf->buf, buf->used);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            if (errno == EWOULDBLOCK) break;
+            assert(errno);
+            LOGE(ERROR, "error writing to %s during copy of %s",
+                 dc->writewhat, dc->copywhat);
+            datacopier_callback(egc, dc, ERROR_FAIL, 1, errno);
+            return;
+        }
+        assert(r > 0);
+        assert(r <= buf->used);
+        buf->used -= r;
+        dc->used -= r;
+        assert(dc->used >= 0);
+        memmove(buf->buf, buf->buf+r, buf->used);
+    }
+    datacopier_check_state(egc, dc);
+}
+
+int libxl__datacopier_start(libxl__datacopier_state *dc)
+{
+    int rc;
+    STATE_AO_GC(dc->ao);
+
+    libxl__datacopier_init(dc);
+
+    assert(dc->readfd >= 0 || dc->writefd >= 0);
+    assert(!(dc->readbuf && dc->bytes_to_read == -1));
+
+    dc->abrt.ao = ao;
+    dc->abrt.callback = datacopier_abort;
+    rc = libxl__ao_abortable_register(&dc->abrt);
+    if (rc) goto out;
+
+    if (dc->readfd >= 0) {
+        rc = libxl__ev_fd_register(gc, &dc->toread, datacopier_readable,
+                                   dc->readfd, POLLIN);
+        if (rc) goto out;
+    }
+
+    if (dc->writefd >= 0) {
+        rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
+                                   dc->writefd, POLLOUT);
+        if (rc) goto out;
+    }
+
+    return 0;
+
+ out:
+    libxl__datacopier_kill(dc);
+    return rc;
+}
+
+/*----- openpty -----*/
+
+/* implementation */
+    
+static void openpty_cleanup(libxl__openpty_state *op)
+{
+    int i;
+
+    for (i=0; i<op->count; i++) {
+        libxl__openpty_result *res = &op->results[i];
+        libxl__carefd_close(res->master);  res->master = 0;
+        libxl__carefd_close(res->slave);   res->slave = 0;
+    }
+}
+
+static void openpty_exited(libxl__egc *egc, libxl__ev_child *child,
+                           pid_t pid, int status) {
+    libxl__openpty_state *op = CONTAINER_OF(child, *op, child);
+    STATE_AO_GC(op->ao);
+
+    if (status) {
+        /* Perhaps the child gave us the fds and then exited nonzero.
+         * Well that would be odd but we don't really care. */
+        libxl_report_child_exitstatus(CTX, op->rc ? LIBXL__LOG_ERROR
+                                                  : LIBXL__LOG_WARNING,
+                                      "openpty child", pid, status);
+    }
+    if (op->rc)
+        openpty_cleanup(op);
+    op->callback(egc, op);
+}
+
+int libxl__openptys(libxl__openpty_state *op,
+                    struct termios *termp,
+                    struct winsize *winp) {
+    /*
+     * This is completely crazy.  openpty calls grantpt which the spec
+     * says may fork, and may not be called with a SIGCHLD handler.
+     * Now our application may have a SIGCHLD handler so that's bad.
+     * We could perhaps block it but we'd need to block it on all
+     * threads.  This is just Too Hard.
+     *
+     * So instead, we run openpty in a child process.  That child
+     * process then of course has only our own thread and our own
+     * signal handlers.  We pass the fds back.
+     *
+     * Since our only current caller actually wants two ptys, we
+     * support calling openpty multiple times for a single fork.
+     */
+    STATE_AO_GC(op->ao);
+    int count = op->count;
+    int r, i, rc, sockets[2], ptyfds[count][2];
+    libxl__carefd *for_child = 0;
+    pid_t pid = -1;
+
+    for (i=0; i<count; i++) {
+        ptyfds[i][0] = ptyfds[i][1] = -1;
+        libxl__openpty_result *res = &op->results[i];
+        assert(!res->master);
+        assert(!res->slave);
+    }
+    sockets[0] = sockets[1] = -1; /* 0 is for us, 1 for our child */
+
+    libxl__carefd_begin();
+    r = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+    if (r) { sockets[0] = sockets[1] = -1; }
+    for_child = libxl__carefd_opened(CTX, sockets[1]);
+    if (r) { LOGE(ERROR,"socketpair failed"); rc = ERROR_FAIL; goto out; }
+
+    pid = libxl__ev_child_fork(gc, &op->child, openpty_exited);
+    if (pid == -1) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (!pid) {
+        /* child */
+        close(sockets[0]);
+        signal(SIGCHLD, SIG_DFL);
+
+        for (i=0; i<count; i++) {
+            r = openpty(&ptyfds[i][0], &ptyfds[i][1], NULL, termp, winp);
+            if (r) { LOGE(ERROR,"openpty failed"); _exit(-1); }
+        }
+        rc = libxl__sendmsg_fds(gc, sockets[1], '\0',
+                                2*count, &ptyfds[0][0], "ptys");
+        if (rc) { LOGE(ERROR,"sendmsg to parent failed"); _exit(-1); }
+        _exit(0);
+    }
+
+    libxl__carefd_close(for_child);
+    for_child = 0;
+
+    /* this should be fast so do it synchronously */
+
+    libxl__carefd_begin();
+    char buf[1];
+    rc = libxl__recvmsg_fds(gc, sockets[0], buf,1,
+                            2*count, &ptyfds[0][0], "ptys");
+    if (!rc) {
+        for (i=0; i<count; i++) {
+            libxl__openpty_result *res = &op->results[i];
+            res->master = libxl__carefd_record(CTX, ptyfds[i][0]);
+            res->slave =  libxl__carefd_record(CTX, ptyfds[i][1]);
+        }
+    }
+    /* now the pty fds are in the carefds, if they were ever open */
+    libxl__carefd_unlock();
+    if (rc)
+        goto out;
+
+    rc = 0;
+
+ out:
+    if (sockets[0] >= 0) close(sockets[0]);
+    libxl__carefd_close(for_child);
+    if (libxl__ev_child_inuse(&op->child)) {
+        op->rc = rc;
+        /* we will get a callback when the child dies */
+        return 0;
+    }
+
+    assert(rc);
+    openpty_cleanup(op);
+    return rc;
+}
+
+/*----- async exec -----*/
+
+static void async_exec_timeout(libxl__egc *egc,
+                               libxl__ev_time *ev,
+                               const struct timeval *requested_abs,
+                               int rc)
+{
+    libxl__async_exec_state *aes = CONTAINER_OF(ev, *aes, time);
+    STATE_AO_GC(aes->ao);
+
+    if (!aes->rc)
+        aes->rc = rc;
+
+    libxl__ev_time_deregister(gc, &aes->time);
+
+    assert(libxl__ev_child_inuse(&aes->child));
+    LOG(ERROR, "killing execution of %s because of timeout", aes->what);
+
+    if (kill(aes->child.pid, SIGKILL)) {
+        LOGEV(ERROR, errno, "unable to kill %s [%ld]",
+              aes->what, (unsigned long)aes->child.pid);
+    }
+
+    return;
+}
+
+static void async_exec_done(libxl__egc *egc,
+                            libxl__ev_child *child,
+                            pid_t pid, int status)
+{
+    libxl__async_exec_state *aes = CONTAINER_OF(child, *aes, child);
+    STATE_AO_GC(aes->ao);
+
+    libxl__ev_time_deregister(gc, &aes->time);
+
+    if (status) {
+        if (!aes->rc)
+            libxl_report_child_exitstatus(CTX, LIBXL__LOG_ERROR,
+                                          aes->what, pid, status);
+    }
+
+    aes->callback(egc, aes, aes->rc, status);
+}
+
+void libxl__async_exec_init(libxl__async_exec_state *aes)
+{
+    libxl__ev_time_init(&aes->time);
+    libxl__ev_child_init(&aes->child);
+}
+
+int libxl__async_exec_start(libxl__async_exec_state *aes)
+{
+    pid_t pid;
+
+    /* Convenience aliases */
+    libxl__ao *ao = aes->ao;
+    AO_GC;
+    libxl__ev_child *const child = &aes->child;
+    char ** const args = aes->args;
+
+    aes->rc = 0;
+
+    /* Set execution timeout */
+    if (libxl__ev_time_register_rel(ao, &aes->time,
+                                    async_exec_timeout,
+                                    aes->timeout_ms)) {
+        LOG(ERROR, "unable to register timeout for executing: %s", aes->what);
+        goto out;
+    }
+
+    LOG(DEBUG, "forking to execute: %s ", aes->what);
+
+    /* Fork and exec */
+    pid = libxl__ev_child_fork(gc, child, async_exec_done);
+    if (pid == -1) {
+        LOG(ERROR, "unable to fork");
+        goto out;
+    }
+
+    if (!pid) {
+        /* child */
+        libxl__exec(gc, aes->stdfds[0], aes->stdfds[1],
+                    aes->stdfds[2], args[0], args, aes->env);
+    }
+
+    return 0;
+
+out:
+    return ERROR_FAIL;
+}
+
+bool libxl__async_exec_inuse(const libxl__async_exec_state *aes)
+{
+    bool time_inuse = libxl__ev_time_isregistered(&aes->time);
+    bool child_inuse = libxl__ev_child_inuse(&aes->child);
+    assert(time_inuse == child_inuse);
+    return child_inuse;
+}
+
+void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what)
+{
+    int r = kill(pid, sig);
+    if (r) LOGE(WARN, "failed to kill() %s [%lu] (signal %d)",
+                what, (unsigned long)pid, sig);
+}
+
+/* Generic function to signal (HUP) a pid stored in xenstore */
+int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
+                        const char *what)
+{
+    const char *xs_pid;
+    int ret, pid;
+
+    ret = libxl__xs_read_checked(gc, XBT_NULL, xs_path_pid, &xs_pid);
+    if (ret || !xs_pid) {
+        LOG(ERROR, "unable to find %s pid in %s", what, xs_path_pid);
+        ret = ret ? : ERROR_FAIL;
+        goto out;
+    }
+    pid = atoi(xs_pid);
+
+    ret = kill(pid, SIGHUP);
+    if (ret < 0 && errno == ESRCH) {
+        LOG(ERROR, "%s already exited", what);
+        ret = 0;
+    } else if (ret == 0) {
+        LOG(DEBUG, "%s signaled", what);
+        ret = 0;
+    } else {
+        LOGE(ERROR, "failed to kill %s [%d]", what, pid);
+        ret = ERROR_FAIL;
+        goto out;
+    }
+
+out:
+    return ret;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_arch.h b/tools/libs/light/libxl_arch.h
new file mode 100644 (file)
index 0000000..6a91775
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef LIBXL_ARCH_H
+#define LIBXL_ARCH_H
+
+/* fill the arch specific configuration for the domain */
+_hidden
+int libxl__arch_domain_prepare_config(libxl__gc *gc,
+                                      libxl_domain_config *d_config,
+                                      struct xen_domctl_createdomain *config);
+
+/* save the arch specific configuration for the domain */
+_hidden
+int libxl__arch_domain_save_config(libxl__gc *gc,
+                                   libxl_domain_config *d_config,
+                                   libxl__domain_build_state *state,
+                                   const struct xen_domctl_createdomain *config);
+
+/* arch specific internal domain creation function */
+_hidden
+int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
+               uint32_t domid);
+
+/* setup arch specific hardware description, i.e. DTB on ARM */
+_hidden
+int libxl__arch_domain_init_hw_description(libxl__gc *gc,
+                                           libxl_domain_build_info *info,
+                                           libxl__domain_build_state *state,
+                                           struct xc_dom_image *dom);
+/* finalize arch specific hardware description. */
+_hidden
+int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
+                                      uint32_t domid,
+                                      libxl_domain_config *d_config,
+                                      struct xc_dom_image *dom);
+
+/* perform any pending hardware initialization */
+_hidden
+int libxl__arch_build_dom_finish(libxl__gc *gc,
+                                 libxl_domain_build_info *info,
+                                 struct xc_dom_image *dom,
+                                 libxl__domain_build_state *state);
+
+/* build vNUMA vmemrange with arch specific information */
+_hidden
+int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
+                                      uint32_t domid,
+                                      libxl_domain_build_info *b_info,
+                                      libxl__domain_build_state *state);
+
+/* arch specific irq map function */
+_hidden
+int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq);
+
+_hidden
+void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
+                                               libxl_domain_create_info *c_info);
+
+_hidden
+void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
+                                              libxl_domain_build_info *b_info);
+
+_hidden
+int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
+                                     uint32_t domid /* for logging, only */,
+                                            libxl_domain_config *d_config,
+                                            const libxl_physinfo *physinfo);
+
+_hidden
+int libxl__arch_extra_memory(libxl__gc *gc,
+                             const libxl_domain_build_info *info,
+                             uint64_t *out);
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#define LAPIC_BASE_ADDRESS  0xfee00000
+#define ACPI_INFO_PHYSICAL_ADDRESS 0xfc000000
+
+int libxl__dom_load_acpi(libxl__gc *gc,
+                         const libxl_domain_build_info *b_info,
+                         struct xc_dom_image *dom);
+#endif
+
+#endif
diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
new file mode 100644 (file)
index 0000000..975a4d7
--- /dev/null
@@ -0,0 +1,1230 @@
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+#include "libxl_libfdt_compat.h"
+#include "libxl_arm.h"
+
+#include <xenctrl_dom.h>
+#include <stdbool.h>
+#include <libfdt.h>
+#include <assert.h>
+#include <xen/device_tree_defs.h>
+
+static const char *gicv_to_string(libxl_gic_version gic_version)
+{
+    switch (gic_version) {
+    case LIBXL_GIC_VERSION_V2:
+        return "V2";
+    case LIBXL_GIC_VERSION_V3:
+        return "V3";
+    default:
+        return "unknown";
+    }
+}
+
+int libxl__arch_domain_prepare_config(libxl__gc *gc,
+                                      libxl_domain_config *d_config,
+                                      struct xen_domctl_createdomain *config)
+{
+    uint32_t nr_spis = 0;
+    unsigned int i;
+    uint32_t vuart_irq;
+    bool vuart_enabled = false;
+
+    /*
+     * If pl011 vuart is enabled then increment the nr_spis to allow allocation
+     * of SPI VIRQ for pl011.
+     */
+    if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
+        nr_spis += (GUEST_VPL011_SPI - 32) + 1;
+        vuart_irq = GUEST_VPL011_SPI;
+        vuart_enabled = true;
+    }
+
+    for (i = 0; i < d_config->b_info.num_irqs; i++) {
+        uint32_t irq = d_config->b_info.irqs[i];
+        uint32_t spi;
+
+        /*
+         * This check ensures the if user has requested pass-through of a certain irq
+         * which conflicts with vpl011 irq then it flags an error to indicate to the
+         * user that the specific HW irq cannot be used as it is dedicated for vpl011.
+         * 
+         * TODO:
+         * The vpl011 irq should be assigned such that it never conflicts with user
+         * specified irqs thereby preventing its pass-through. This TODO is for
+         * implementing that logic in future.
+         */
+        if (vuart_enabled && irq == vuart_irq) {
+            LOG(ERROR, "Physical IRQ %u conflicting with pl011 SPI\n", irq);
+            return ERROR_FAIL;
+        }
+
+        if (irq < 32)
+            continue;
+
+        spi = irq - 32;
+
+        if (nr_spis <= spi)
+            nr_spis = spi + 1;
+    }
+
+    LOG(DEBUG, "Configure the domain");
+
+    config->arch.nr_spis = nr_spis;
+    LOG(DEBUG, " - Allocate %u SPIs", nr_spis);
+
+    switch (d_config->b_info.arch_arm.gic_version) {
+    case LIBXL_GIC_VERSION_DEFAULT:
+        config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE;
+        break;
+    case LIBXL_GIC_VERSION_V2:
+        config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V2;
+        break;
+    case LIBXL_GIC_VERSION_V3:
+        config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V3;
+        break;
+    default:
+        LOG(ERROR, "Unknown GIC version %d",
+            d_config->b_info.arch_arm.gic_version);
+        return ERROR_FAIL;
+    }
+
+    switch (d_config->b_info.tee) {
+    case LIBXL_TEE_TYPE_NONE:
+        config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_NONE;
+        break;
+    case LIBXL_TEE_TYPE_OPTEE:
+        config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_OPTEE;
+        break;
+    default:
+        LOG(ERROR, "Unknown TEE type %d",
+            d_config->b_info.tee);
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+int libxl__arch_domain_save_config(libxl__gc *gc,
+                                   libxl_domain_config *d_config,
+                                   libxl__domain_build_state *state,
+                                   const struct xen_domctl_createdomain *config)
+{
+    switch (config->arch.gic_version) {
+    case XEN_DOMCTL_CONFIG_GIC_V2:
+        d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V2;
+        break;
+    case XEN_DOMCTL_CONFIG_GIC_V3:
+        d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V3;
+        break;
+    default:
+        LOG(ERROR, "Unexpected gic version %u", config->arch.gic_version);
+        return ERROR_FAIL;
+    }
+
+    state->clock_frequency = config->arch.clock_frequency;
+
+    return 0;
+}
+
+int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
+                              uint32_t domid)
+{
+    return 0;
+}
+
+int libxl__arch_extra_memory(libxl__gc *gc,
+                             const libxl_domain_build_info *info,
+                             uint64_t *out)
+{
+    int rc = 0;
+    uint64_t size = 0;
+
+    if (libxl_defbool_val(info->acpi)) {
+        rc = libxl__get_acpi_size(gc, info, &size);
+        if (rc < 0) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    *out = LIBXL_MAXMEM_CONSTANT + DIV_ROUNDUP(size, 1024);
+out:
+    return rc;
+}
+
+static struct arch_info {
+    const char *guest_type;
+    const char *timer_compat;
+    const char *cpu_compat;
+} arch_info[] = {
+    {"xen-3.0-armv7l",  "arm,armv7-timer", "arm,cortex-a15" },
+    {"xen-3.0-aarch64", "arm,armv8-timer", "arm,armv8" },
+};
+
+typedef uint32_t be32;
+typedef be32 gic_interrupt[3];
+
+#define PROP_INITRD_START "linux,initrd-start"
+#define PROP_INITRD_END "linux,initrd-end"
+
+static void set_cell(be32 **cellp, int size, uint64_t val)
+{
+    int cells = size;
+
+    while (size--) {
+        (*cellp)[size] = cpu_to_fdt32(val);
+        val >>= 32;
+    }
+
+    (*cellp) += cells;
+}
+
+static void set_interrupt(gic_interrupt interrupt, unsigned int irq,
+                          unsigned int cpumask, unsigned int level)
+{
+    be32 *cells = interrupt;
+    int is_ppi = (irq < 32);
+
+    /* SGIs are not describe in the device tree */
+    assert(irq >= 16);
+
+    irq -= (is_ppi) ? 16: 32; /* PPIs start at 16, SPIs at 32 */
+
+    /* See linux Documentation/devictree/bindings/arm/gic.txt */
+    set_cell(&cells, 1, is_ppi); /* is a PPI? */
+    set_cell(&cells, 1, irq);
+    set_cell(&cells, 1, (cpumask << 8) | level);
+}
+
+static void set_range(be32 **cellp,
+                      int address_cells, int size_cells,
+                      uint64_t address, uint64_t size)
+{
+    set_cell(cellp, address_cells, address);
+    set_cell(cellp, size_cells, size);
+}
+
+static int fdt_property_compat(libxl__gc *gc, void *fdt, unsigned nr_compat, ...)
+{
+    const char *compats[nr_compat];
+    int i;
+    size_t sz;
+    va_list ap;
+    char *compat, *p;
+
+    va_start(ap, nr_compat);
+    sz = 0;
+    for (i = 0; i < nr_compat; i++) {
+        const char *c = va_arg(ap, const char *);
+        compats[i] = c;
+        sz += strlen(compats[i]) + 1;
+    }
+    va_end(ap);
+
+    p = compat = libxl__zalloc(gc, sz);
+    for (i = 0; i < nr_compat; i++) {
+        strcpy(p, compats[i]);
+        p += strlen(compats[i]) + 1;
+    }
+
+    return fdt_property(fdt, "compatible", compat, sz);
+}
+
+static int fdt_property_interrupts(libxl__gc *gc, void *fdt,
+                                   gic_interrupt *intr,
+                                   unsigned num_irq)
+{
+    int res;
+
+    res = fdt_property(fdt, "interrupts", intr, sizeof (intr[0]) * num_irq);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
+    if (res) return res;
+
+    return 0;
+}
+
+static int fdt_property_regs(libxl__gc *gc, void *fdt,
+                             unsigned addr_cells,
+                             unsigned size_cells,
+                             unsigned num_regs, ...)
+{
+    uint32_t regs[num_regs*(addr_cells+size_cells)];
+    be32 *cells = &regs[0];
+    int i;
+    va_list ap;
+    uint64_t base, size;
+
+    va_start(ap, num_regs);
+    for (i = 0 ; i < num_regs; i++) {
+        base = addr_cells ? va_arg(ap, uint64_t) : 0;
+        size = size_cells ? va_arg(ap, uint64_t) : 0;
+        set_range(&cells, addr_cells, size_cells, base, size);
+    }
+    va_end(ap);
+
+    return fdt_property(fdt, "reg", regs, sizeof(regs));
+}
+
+static int make_root_properties(libxl__gc *gc,
+                                const libxl_version_info *vers,
+                                void *fdt)
+{
+    int res;
+
+    res = fdt_property_string(fdt, "model", GCSPRINTF("XENVM-%d.%d",
+                                                      vers->xen_version_major,
+                                                      vers->xen_version_minor));
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 2,
+                              GCSPRINTF("xen,xenvm-%d.%d",
+                                        vers->xen_version_major,
+                                        vers->xen_version_minor),
+                              "xen,xenvm");
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "#address-cells", GUEST_ROOT_ADDRESS_CELLS);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "#size-cells", GUEST_ROOT_SIZE_CELLS);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_chosen_node(libxl__gc *gc, void *fdt, bool ramdisk,
+                            libxl__domain_build_state *state,
+                            const libxl_domain_build_info *info)
+{
+    int res;
+
+    /* See linux Documentation/devicetree/... */
+    res = fdt_begin_node(fdt, "chosen");
+    if (res) return res;
+
+    if (state->pv_cmdline) {
+        LOG(DEBUG, "/chosen/bootargs = %s", state->pv_cmdline);
+        res = fdt_property_string(fdt, "bootargs", state->pv_cmdline);
+        if (res) return res;
+    }
+
+    if (ramdisk) {
+        uint64_t dummy = 0;
+        LOG(DEBUG, "/chosen adding placeholder linux,initrd properties");
+        res = fdt_property(fdt, PROP_INITRD_START, &dummy, sizeof(dummy));
+        if (res) return res;
+        res = fdt_property(fdt, PROP_INITRD_END, &dummy, sizeof(dummy));
+        if (res) return res;
+    }
+
+    if (libxl_defbool_val(info->acpi)) {
+        const uint64_t acpi_base = GUEST_ACPI_BASE;
+        const char *name = GCSPRINTF("module@%"PRIx64, acpi_base);
+
+        res = fdt_begin_node(fdt, name);
+        if (res) return res;
+
+        res = fdt_property_compat(gc, fdt, 2, "xen,guest-acpi",
+                                  "multiboot,module");
+        if (res) return res;
+
+        res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+                                1, 0, 0);
+        if (res) return res;
+
+        res = fdt_end_node(fdt);
+        if (res) return res;
+    }
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_cpus_node(libxl__gc *gc, void *fdt, int nr_cpus,
+                          const struct arch_info *ainfo)
+{
+    int res, i;
+    uint64_t mpidr_aff;
+
+    res = fdt_begin_node(fdt, "cpus");
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "#address-cells", 1);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "#size-cells", 0);
+    if (res) return res;
+
+    for (i = 0; i < nr_cpus; i++) {
+        const char *name;
+
+        mpidr_aff = libxl__compute_mpdir(i);
+        name = GCSPRINTF("cpu@%"PRIx64, mpidr_aff);
+
+        res = fdt_begin_node(fdt, name);
+        if (res) return res;
+
+        res = fdt_property_string(fdt, "device_type", "cpu");
+        if (res) return res;
+
+        res = fdt_property_compat(gc, fdt, 1, ainfo->cpu_compat);
+        if (res) return res;
+
+        res = fdt_property_string(fdt, "enable-method", "psci");
+        if (res) return res;
+
+        res = fdt_property_regs(gc, fdt, 1, 0, 1, mpidr_aff);
+        if (res) return res;
+
+        res = fdt_end_node(fdt);
+        if (res) return res;
+    }
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_psci_node(libxl__gc *gc, void *fdt)
+{
+    int res;
+
+    res = fdt_begin_node(fdt, "psci");
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 3, "arm,psci-1.0",
+                              "arm,psci-0.2", "arm,psci");
+    if (res) return res;
+
+    res = fdt_property_string(fdt, "method", "hvc");
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "cpu_off", PSCI_cpu_off);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "cpu_on", PSCI_cpu_on);
+    if (res) return res;
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_optee_node(libxl__gc *gc, void *fdt)
+{
+    int res;
+    LOG(DEBUG, "Creating OP-TEE node in dtb");
+
+    res = fdt_begin_node(fdt, "firmware");
+    if (res) return res;
+
+    res = fdt_begin_node(fdt, "optee");
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 1, "linaro,optee-tz");
+    if (res) return res;
+
+    res = fdt_property_string(fdt, "method", "hvc");
+    if (res) return res;
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_memory_nodes(libxl__gc *gc, void *fdt,
+                             const struct xc_dom_image *dom)
+{
+    int res, i;
+    const char *name;
+    const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
+
+    for (i = 0; i < GUEST_RAM_BANKS; i++) {
+        name = GCSPRINTF("memory@%"PRIx64, bankbase[i]);
+
+        LOG(DEBUG, "Creating placeholder node /%s", name);
+
+        res = fdt_begin_node(fdt, name);
+        if (res) return res;
+
+        res = fdt_property_string(fdt, "device_type", "memory");
+        if (res) return res;
+
+        res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+                                1, 0, 0);
+        if (res) return res;
+
+        res = fdt_end_node(fdt);
+        if (res) return res;
+    }
+
+    return 0;
+}
+
+static int make_gicv2_node(libxl__gc *gc, void *fdt,
+                           uint64_t gicd_base, uint64_t gicd_size,
+                           uint64_t gicc_base, uint64_t gicc_size)
+{
+    int res;
+    const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
+
+    res = fdt_begin_node(fdt, name);
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 2,
+                              "arm,cortex-a15-gic",
+                              "arm,cortex-a9-gic");
+    if (res) return res;
+
+
+    res = fdt_property_cell(fdt, "#interrupt-cells", 3);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "#address-cells", 0);
+    if (res) return res;
+
+    res = fdt_property(fdt, "interrupt-controller", NULL, 0);
+    if (res) return res;
+
+    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+                            2,
+                            gicd_base, gicd_size,
+                            gicc_base, gicc_size);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
+    if (res) return res;
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_gicv3_node(libxl__gc *gc, void *fdt)
+{
+    int res;
+    const uint64_t gicd_base = GUEST_GICV3_GICD_BASE;
+    const uint64_t gicd_size = GUEST_GICV3_GICD_SIZE;
+    const uint64_t gicr0_base = GUEST_GICV3_GICR0_BASE;
+    const uint64_t gicr0_size = GUEST_GICV3_GICR0_SIZE;
+    const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
+
+    res = fdt_begin_node(fdt, name);
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 1, "arm,gic-v3");
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "#interrupt-cells", 3);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "#address-cells", 0);
+    if (res) return res;
+
+    res = fdt_property(fdt, "interrupt-controller", NULL, 0);
+    if (res) return res;
+
+    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+                            2,
+                            gicd_base, gicd_size,
+                            gicr0_base, gicr0_size);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
+    if (res) return res;
+
+    res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
+    if (res) return res;
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_timer_node(libxl__gc *gc, void *fdt,
+                           const struct arch_info *ainfo,
+                           uint32_t frequency)
+{
+    int res;
+    gic_interrupt ints[3];
+
+    res = fdt_begin_node(fdt, "timer");
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 1, ainfo->timer_compat);
+    if (res) return res;
+
+    set_interrupt(ints[0], GUEST_TIMER_PHYS_S_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+    set_interrupt(ints[1], GUEST_TIMER_PHYS_NS_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+    set_interrupt(ints[2], GUEST_TIMER_VIRT_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+
+    res = fdt_property_interrupts(gc, fdt, ints, 3);
+    if (res) return res;
+
+    if ( frequency )
+        fdt_property_u32(fdt, "clock-frequency", frequency);
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_hypervisor_node(libxl__gc *gc, void *fdt,
+                                const libxl_version_info *vers)
+{
+    int res;
+    gic_interrupt intr;
+
+    /* See linux Documentation/devicetree/bindings/arm/xen.txt */
+    res = fdt_begin_node(fdt, "hypervisor");
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 2,
+                              GCSPRINTF("xen,xen-%d.%d",
+                                        vers->xen_version_major,
+                                        vers->xen_version_minor),
+                              "xen,xen");
+    if (res) return res;
+
+    /* reg 0 is grant table space */
+    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+                            1,GUEST_GNTTAB_BASE, GUEST_GNTTAB_SIZE);
+    if (res) return res;
+
+    /*
+     * interrupts is evtchn upcall:
+     *  - Active-low level-sensitive
+     *  - All cpus
+     */
+    set_interrupt(intr, GUEST_EVTCHN_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
+
+    res = fdt_property_interrupts(gc, fdt, &intr, 1);
+    if (res) return res;
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static int make_vpl011_uart_node(libxl__gc *gc, void *fdt,
+                                 const struct arch_info *ainfo,
+                                 struct xc_dom_image *dom)
+{
+    int res;
+    gic_interrupt intr;
+
+    res = fdt_begin_node(fdt, "sbsa-pl011");
+    if (res) return res;
+
+    res = fdt_property_compat(gc, fdt, 1, "arm,sbsa-uart");
+    if (res) return res;
+
+    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+                            1,
+                            GUEST_PL011_BASE, GUEST_PL011_SIZE);
+    if (res) return res;
+
+    set_interrupt(intr, GUEST_VPL011_SPI, 0xf, DT_IRQ_TYPE_LEVEL_HIGH);
+
+    res = fdt_property_interrupts(gc, fdt, &intr, 1);
+    if (res) return res;
+
+    /* Use a default baud rate of 115200. */
+    fdt_property_u32(fdt, "current-speed", 115200);
+
+    res = fdt_end_node(fdt);
+    if (res) return res;
+
+    return 0;
+}
+
+static const struct arch_info *get_arch_info(libxl__gc *gc,
+                                             const struct xc_dom_image *dom)
+{
+    int i;
+
+    for (i=0; i < ARRAY_SIZE(arch_info); i++) {
+        const struct arch_info *info = &arch_info[i];
+        if (!strcmp(dom->guest_type, info->guest_type))
+            return info;
+    }
+    LOG(ERROR, "Unable to find arch FDT info for %s", dom->guest_type);
+    return NULL;
+}
+
+static void debug_dump_fdt(libxl__gc *gc, void *fdt)
+{
+    int fd = -1, rc, r;
+
+    const char *dtb = getenv("LIBXL_DEBUG_DUMP_DTB");
+
+    if (!dtb) goto out;
+
+    fd = open(dtb, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+    if (fd < 0) {
+        LOGE(DEBUG, "cannot open %s for LIBXL_DEBUG_DUMP_DTB", dtb);
+        goto out;
+    }
+
+    rc = libxl_write_exactly(CTX, fd, fdt, fdt_totalsize(fdt), dtb, "dtb");
+    if (rc < 0) goto out;
+
+out:
+    if (fd >= 0) {
+        r = close(fd);
+        if (r < 0) LOGE(DEBUG, "failed to close DTB debug dump output");
+    }
+}
+
+#ifdef ENABLE_PARTIAL_DEVICE_TREE
+
+static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
+{
+    int r;
+
+    if (fdt_magic(fdt) != FDT_MAGIC) {
+        LOG(ERROR, "Partial FDT is not a valid Flat Device Tree");
+        return ERROR_FAIL;
+    }
+
+    r = fdt_check_header(fdt);
+    if (r) {
+        LOG(ERROR, "Failed to check the partial FDT (%d)", r);
+        return ERROR_FAIL;
+    }
+
+    if (fdt_totalsize(fdt) > size) {
+        LOG(ERROR, "Partial FDT totalsize is too big");
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+static int copy_properties(libxl__gc *gc, void *fdt, void *pfdt,
+                           int nodeoff)
+{
+    int propoff, nameoff, r;
+    const struct fdt_property *prop;
+
+    for (propoff = fdt_first_property_offset(pfdt, nodeoff);
+         propoff >= 0;
+         propoff = fdt_next_property_offset(pfdt, propoff)) {
+
+        if (!(prop = fdt_get_property_by_offset(pfdt, propoff, NULL))) {
+            return -FDT_ERR_INTERNAL;
+        }
+
+        nameoff = fdt32_to_cpu(prop->nameoff);
+        r = fdt_property(fdt, fdt_string(pfdt, nameoff),
+                         prop->data, fdt32_to_cpu(prop->len));
+        if (r) return r;
+    }
+
+    /* FDT_ERR_NOTFOUND => There is no more properties for this node */
+    return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
+}
+
+/* Copy a node from the partial device tree to the guest device tree */
+static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
+                     int nodeoff, int depth)
+{
+    int r;
+
+    r = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
+    if (r) return r;
+
+    r = copy_properties(gc, fdt, pfdt, nodeoff);
+    if (r) return r;
+
+    for (nodeoff = fdt_first_subnode(pfdt, nodeoff);
+         nodeoff >= 0;
+         nodeoff = fdt_next_subnode(pfdt, nodeoff)) {
+        r = copy_node(gc, fdt, pfdt, nodeoff, depth + 1);
+        if (r) return r;
+    }
+
+    if (nodeoff != -FDT_ERR_NOTFOUND)
+        return nodeoff;
+
+    r = fdt_end_node(fdt);
+    if (r) return r;
+
+    return 0;
+}
+
+static int copy_node_by_path(libxl__gc *gc, const char *path,
+                             void *fdt, void *pfdt)
+{
+    int nodeoff, r;
+    const char *name = strrchr(path, '/');
+
+    if (!name)
+        return -FDT_ERR_INTERNAL;
+
+    name++;
+
+    /*
+     * The FDT function to look at a node doesn't take into account the
+     * unit (i.e anything after @) when search by name. Check if the
+     * name exactly matches.
+     */
+    nodeoff = fdt_path_offset(pfdt, path);
+    if (nodeoff < 0)
+        return nodeoff;
+
+    if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
+        return -FDT_ERR_NOTFOUND;
+
+    r = copy_node(gc, fdt, pfdt, nodeoff, 0);
+    if (r) return r;
+
+    return 0;
+}
+
+/*
+ * The partial device tree is not copied entirely. Only the relevant bits are
+ * copied to the guest device tree:
+ *  - /passthrough node
+ *  - /aliases node
+ */
+static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
+{
+    int r;
+
+    r = copy_node_by_path(gc, "/passthrough", fdt, pfdt);
+    if (r < 0) {
+        LOG(ERROR, "Can't copy the node \"/passthrough\" from the partial FDT");
+        return r;
+    }
+
+    r = copy_node_by_path(gc, "/aliases", fdt, pfdt);
+    if (r < 0 && r != -FDT_ERR_NOTFOUND) {
+        LOG(ERROR, "Can't copy the node \"/aliases\" from the partial FDT");
+        return r;
+    }
+
+    return 0;
+}
+
+#else
+
+static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
+{
+    LOG(ERROR, "partial device tree not supported");
+
+    return ERROR_FAIL;
+}
+
+static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
+{
+    /*
+     * We should never be here when the partial device tree is not
+     * supported.
+     * */
+    return -FDT_ERR_INTERNAL;
+}
+
+#endif /* ENABLE_PARTIAL_DEVICE_TREE */
+
+#define FDT_MAX_SIZE (1<<20)
+
+static int libxl__prepare_dtb(libxl__gc *gc, libxl_domain_build_info *info,
+                              libxl__domain_build_state *state,
+                              struct xc_dom_image *dom)
+{
+    void *fdt = NULL;
+    void *pfdt = NULL;
+    int rc, res;
+    size_t fdt_size = 0;
+    int pfdt_size = 0;
+
+    const libxl_version_info *vers;
+    const struct arch_info *ainfo;
+
+    vers = libxl_get_version_info(CTX);
+    if (vers == NULL) return ERROR_FAIL;
+
+    ainfo = get_arch_info(gc, dom);
+    if (ainfo == NULL) return ERROR_FAIL;
+
+    LOG(DEBUG, "constructing DTB for Xen version %d.%d guest",
+        vers->xen_version_major, vers->xen_version_minor);
+    LOG(DEBUG, " - vGIC version: %s",
+        gicv_to_string(info->arch_arm.gic_version));
+
+    if (info->device_tree) {
+        LOG(DEBUG, " - Partial device tree provided: %s", info->device_tree);
+
+        rc = libxl_read_file_contents(CTX, info->device_tree,
+                                      &pfdt, &pfdt_size);
+        if (rc) {
+            LOGEV(ERROR, rc, "failed to read the partial device file %s",
+                  info->device_tree);
+            return ERROR_FAIL;
+        }
+        libxl__ptr_add(gc, pfdt);
+
+        if (check_partial_fdt(gc, pfdt, pfdt_size))
+            return ERROR_FAIL;
+    }
+
+/*
+ * Call "call" handling FDT_ERR_*. Will either:
+ * - loop back to retry_resize
+ * - set rc and goto out
+ * - fall through successfully
+ *
+ * On FDT_ERR_NOSPACE we start again from scratch rather than
+ * realloc+libfdt_open_into because "call" may have failed half way
+ * through a series of steps leaving the partial tree in an
+ * inconsistent state, e.g. leaving a node open.
+ */
+#define FDT( call ) do {                                        \
+    int fdt_res = (call);                                       \
+    if (fdt_res == -FDT_ERR_NOSPACE && fdt_size < FDT_MAX_SIZE) \
+        goto next_resize;                                       \
+    else if (fdt_res < 0) {                                     \
+        LOG(ERROR, "FDT: %s failed: %d = %s",                   \
+            #call, fdt_res, fdt_strerror(fdt_res));             \
+        rc = ERROR_FAIL;                                        \
+        goto out;                                               \
+    }                                                           \
+} while(0)
+
+    for (;;) {
+next_resize:
+        if (fdt_size) {
+            fdt_size <<= 1;
+            LOG(DEBUG, "Increasing FDT size to %zd and retrying", fdt_size);
+        } else {
+            fdt_size = 4096;
+        }
+
+        fdt = libxl__realloc(gc, fdt, fdt_size);
+
+        FDT( fdt_create(fdt, fdt_size) );
+
+        FDT( fdt_finish_reservemap(fdt) );
+
+        FDT( fdt_begin_node(fdt, "") );
+
+        FDT( make_root_properties(gc, vers, fdt) );
+        FDT( make_chosen_node(gc, fdt, !!dom->modules[0].blob, state, info) );
+        FDT( make_cpus_node(gc, fdt, info->max_vcpus, ainfo) );
+        FDT( make_psci_node(gc, fdt) );
+
+        FDT( make_memory_nodes(gc, fdt, dom) );
+
+        switch (info->arch_arm.gic_version) {
+        case LIBXL_GIC_VERSION_V2:
+            FDT( make_gicv2_node(gc, fdt,
+                                 GUEST_GICD_BASE, GUEST_GICD_SIZE,
+                                 GUEST_GICC_BASE, GUEST_GICC_SIZE) );
+            break;
+        case LIBXL_GIC_VERSION_V3:
+            FDT( make_gicv3_node(gc, fdt) );
+            break;
+        default:
+            LOG(ERROR, "Unknown GIC version %s",
+                gicv_to_string(info->arch_arm.gic_version));
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        FDT( make_timer_node(gc, fdt, ainfo, state->clock_frequency) );
+        FDT( make_hypervisor_node(gc, fdt, vers) );
+
+        if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
+            FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
+
+        if (info->tee == LIBXL_TEE_TYPE_OPTEE)
+            FDT( make_optee_node(gc, fdt) );
+
+        if (pfdt)
+            FDT( copy_partial_fdt(gc, fdt, pfdt) );
+
+        FDT( fdt_end_node(fdt) );
+
+        FDT( fdt_finish(fdt) );
+        break;
+    }
+#undef FDT
+
+    LOG(DEBUG, "fdt total size %d", fdt_totalsize(fdt));
+
+    res = xc_dom_devicetree_mem(dom, fdt, fdt_totalsize(fdt));
+    if (res) {
+        LOGE(ERROR, "xc_dom_devicetree_mem failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+int libxl__arch_domain_init_hw_description(libxl__gc *gc,
+                                           libxl_domain_build_info *info,
+                                           libxl__domain_build_state *state,
+                                           struct xc_dom_image *dom)
+{
+    int rc;
+    uint64_t val;
+
+    if (info->type != LIBXL_DOMAIN_TYPE_PVH) {
+        LOG(ERROR, "Unsupported Arm guest type %s",
+            libxl_domain_type_to_string(info->type));
+        return ERROR_INVAL;
+    }
+
+    /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ. */
+    val = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI,
+                    HVM_PARAM_CALLBACK_IRQ_TYPE_MASK);
+    /* Active-low level-sensitive  */
+    val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL,
+                     HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK);
+    val |= GUEST_EVTCHN_PPI;
+    rc = xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CALLBACK_IRQ,
+                          val);
+    if (rc)
+        return rc;
+
+    rc = libxl__prepare_dtb(gc, info, state, dom);
+    if (rc) goto out;
+
+    if (!libxl_defbool_val(info->acpi)) {
+        LOG(DEBUG, "Generating ACPI tables is disabled by user.");
+        rc = 0;
+        goto out;
+    }
+
+    if (strcmp(dom->guest_type, "xen-3.0-aarch64")) {
+        /* ACPI is only supported for 64-bit guest currently. */
+        LOG(ERROR, "Can not enable libxl option 'acpi' for %s", dom->guest_type);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__prepare_acpi(gc, info, dom);
+
+out:
+    return rc;
+}
+
+static void finalise_one_node(libxl__gc *gc, void *fdt, const char *uname,
+                              uint64_t base, uint64_t size)
+{
+    int node, res;
+    const char *name = GCSPRINTF("%s@%"PRIx64, uname, base);
+
+    node = fdt_path_offset(fdt, name);
+    assert(node > 0);
+
+    if (size == 0) {
+        LOG(DEBUG, "Nopping out placeholder node %s", name);
+        fdt_nop_node(fdt, node);
+    } else {
+        uint32_t regs[GUEST_ROOT_ADDRESS_CELLS+GUEST_ROOT_SIZE_CELLS];
+        be32 *cells = &regs[0];
+
+        LOG(DEBUG, "Populating placeholder node %s", name);
+
+        set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, base, size);
+
+        res = fdt_setprop_inplace(fdt, node, "reg", regs, sizeof(regs));
+        assert(!res);
+    }
+}
+
+int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
+                                               uint32_t domid,
+                                               libxl_domain_config *d_config,
+                                               struct xc_dom_image *dom)
+{
+    void *fdt = dom->devicetree_blob;
+    int i;
+    const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
+
+    const struct xc_dom_seg *ramdisk = dom->modules[0].blob ?
+        &dom->modules[0].seg : NULL;
+
+    if (ramdisk) {
+        int chosen, res;
+        uint64_t val;
+
+        /* Neither the fdt_path_offset() nor either of the
+         * fdt_setprop_inplace() calls can fail. If they do then
+         * make_chosen_node() (see above) has got something very
+         * wrong.
+         */
+        chosen = fdt_path_offset(fdt, "/chosen");
+        assert(chosen > 0);
+
+        LOG(DEBUG, "/chosen updating initrd properties to cover "
+            "%"PRIx64"-%"PRIx64,
+            ramdisk->vstart, ramdisk->vend);
+
+        val = cpu_to_fdt64(ramdisk->vstart);
+        res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_START,
+                                  &val, sizeof(val));
+        assert(!res);
+
+        val = cpu_to_fdt64(ramdisk->vend);
+        res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_END,
+                                  &val, sizeof(val));
+        assert(!res);
+
+    }
+
+    for (i = 0; i < GUEST_RAM_BANKS; i++) {
+        const uint64_t size = (uint64_t)dom->rambank_size[i] << XC_PAGE_SHIFT;
+
+        finalise_one_node(gc, fdt, "/memory", bankbase[i], size);
+    }
+
+    if (dom->acpi_modules[0].data) {
+        finalise_one_node(gc, fdt, "/chosen/module", GUEST_ACPI_BASE,
+                          dom->acpi_modules[0].length);
+    }
+
+    debug_dump_fdt(gc, fdt);
+
+    return 0;
+}
+
+int libxl__arch_build_dom_finish(libxl__gc *gc,
+                                 libxl_domain_build_info *info,
+                                 struct xc_dom_image *dom,
+                                 libxl__domain_build_state *state)
+{
+    int rc = 0, ret;
+
+    if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
+        rc = 0;
+        goto out;
+    }
+
+    ret = xc_dom_vuart_init(CTX->xch,
+                            XEN_DOMCTL_VUART_TYPE_VPL011,
+                            dom->guest_domid,
+                            dom->console_domid,
+                            dom->vuart_gfn,
+                            &state->vuart_port);
+    if (ret < 0) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "xc_dom_vuart_init failed\n");
+    }
+
+out:
+    return rc;
+}
+
+int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
+                                      uint32_t domid,
+                                      libxl_domain_build_info *info,
+                                      libxl__domain_build_state *state)
+{
+    return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, info, state);
+}
+
+int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
+{
+    return xc_domain_bind_pt_spi_irq(CTX->xch, domid, irq, irq);
+}
+
+void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
+                                               libxl_domain_create_info *c_info)
+{
+    /*
+     * Arm guest are now considered as PVH by the toolstack. To allow
+     * compatibility with previous toolstack, PV guest are automatically
+     * converted to PVH.
+     */
+    if (c_info->type == LIBXL_DOMAIN_TYPE_PV) {
+        LOG(WARN, "Converting PV guest to PVH.");
+        LOG(WARN, "Arm guest are now PVH.");
+        LOG(WARN, "Please fix your configuration file/toolstack.");
+
+        c_info->type = LIBXL_DOMAIN_TYPE_PVH;
+        /* All other fields can remain untouched */
+    }
+}
+
+void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
+                                              libxl_domain_build_info *b_info)
+{
+    /* ACPI is disabled by default */
+    libxl_defbool_setdefault(&b_info->acpi, false);
+
+    if (b_info->type != LIBXL_DOMAIN_TYPE_PV)
+        return;
+
+    LOG(DEBUG, "Converting build_info to PVH");
+
+    /* Re-initialize type to PVH and all associated fields to defaults. */
+    memset(&b_info->u, '\0', sizeof(b_info->u));
+    b_info->type = LIBXL_DOMAIN_TYPE_INVALID;
+    libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_PVH);
+}
+
+int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
+                                            uint32_t domid,
+                                            libxl_domain_config *d_config,
+                                            const libxl_physinfo *physinfo)
+{
+    int rc;
+    libxl_domain_create_info *const c_info = &d_config->c_info;
+
+    if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
+        c_info->passthrough = LIBXL_PASSTHROUGH_SHARE_PT;
+    }
+
+    switch (c_info->passthrough) {
+    case LIBXL_PASSTHROUGH_DISABLED:
+    case LIBXL_PASSTHROUGH_SHARE_PT:
+        break;
+
+    default:
+        LOGD(ERROR, domid,
+             "passthrough=\"%s\" not supported on ARM\n",
+             libxl_passthrough_to_string(c_info->passthrough));
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    rc = 0;
+ out:
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_arm.h b/tools/libs/light/libxl_arm.h
new file mode 100644 (file)
index 0000000..52c2ab5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016      Linaro Ltd.
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+
+_hidden
+int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
+                        struct xc_dom_image *dom);
+
+_hidden
+int libxl__get_acpi_size(libxl__gc *gc,
+                         const libxl_domain_build_info *info,
+                         uint64_t *out);
+
+static inline uint64_t libxl__compute_mpdir(unsigned int cpuid)
+{
+    /*
+     * According to ARM CPUs bindings, the reg field should match
+     * the MPIDR's affinity bits. We will use AFF0 and AFF1 when
+     * constructing the reg value of the guest at the moment, for it
+     * is enough for the current max vcpu number.
+     */
+    return (cpuid & 0x0f) | (((cpuid >> 4) & 0xff) << 8);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_arm_acpi.c b/tools/libs/light/libxl_arm_acpi.c
new file mode 100644 (file)
index 0000000..ba874c3
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * ARM DomU ACPI generation
+ *
+ * Copyright (C) 2016      Linaro Ltd.
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_arm.h"
+
+#include <stdint.h>
+
+/* Below typedefs are useful for the headers under acpi/ */
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+#include <acpi/acconfig.h>
+#include <acpi/actbl.h>
+
+#ifndef BITS_PER_LONG
+#ifdef _LP64
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif
+#endif
+#define ACPI_MACHINE_WIDTH BITS_PER_LONG
+#define COMPILER_DEPENDENT_INT64 int64_t
+#define COMPILER_DEPENDENT_UINT64 uint64_t
+
+#include <acpi/actypes.h>
+
+_hidden
+extern const unsigned char dsdt_anycpu_arm[];
+_hidden
+extern const int dsdt_anycpu_arm_len;
+
+#define ACPI_OEM_ID "Xen\0\0"
+#define ACPI_OEM_TABLE_ID "ARM\0\0\0\0"
+#define ACPI_ASL_COMPILER_ID "XL\0"
+
+enum {
+    RSDP,
+    XSDT,
+    GTDT,
+    MADT,
+    FADT,
+    DSDT,
+    MAX_TABLE_NUMS,
+};
+
+struct acpitable {
+    uint64_t addr;
+    size_t size;
+};
+
+static int libxl__estimate_madt_size(libxl__gc *gc,
+                                     const libxl_domain_build_info *info,
+                                     size_t *size)
+{
+    int rc = 0;
+
+    switch (info->arch_arm.gic_version) {
+    case LIBXL_GIC_VERSION_V2:
+        *size = sizeof(struct acpi_table_madt) +
+                ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
+                sizeof(struct acpi_madt_generic_distributor);
+        break;
+    case LIBXL_GIC_VERSION_V3:
+        *size = sizeof(struct acpi_table_madt) +
+                ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
+                sizeof(struct acpi_madt_generic_distributor) +
+                sizeof(struct acpi_madt_generic_redistributor);
+        break;
+    default:
+        LOG(ERROR, "Unknown GIC version");
+        *size = 0;
+        rc = ERROR_FAIL;
+        break;
+    }
+
+    return rc;
+}
+
+int libxl__get_acpi_size(libxl__gc *gc,
+                         const libxl_domain_build_info *info,
+                         uint64_t *out)
+{
+    uint64_t size;
+    int rc = 0;
+
+
+    rc = libxl__estimate_madt_size(gc, info, &size);
+    if (rc < 0)
+        goto out;
+
+    *out = ROUNDUP(size, 3) +
+           ROUNDUP(sizeof(struct acpi_table_rsdp), 3) +
+           ROUNDUP(sizeof(struct acpi_table_xsdt), 3) +
+           ROUNDUP(sizeof(struct acpi_table_gtdt), 3) +
+           ROUNDUP(sizeof(struct acpi_table_fadt), 3) +
+           ROUNDUP(sizeof(dsdt_anycpu_arm_len), 3);
+
+out:
+    return rc;
+}
+
+static int libxl__allocate_acpi_tables(libxl__gc *gc,
+                                       libxl_domain_build_info *info,
+                                       struct xc_dom_image *dom,
+                                       struct acpitable acpitables[])
+{
+    int rc;
+    size_t size;
+
+    acpitables[RSDP].addr = GUEST_ACPI_BASE;
+    acpitables[RSDP].size = sizeof(struct acpi_table_rsdp);
+    dom->acpi_modules[0].length += ROUNDUP(acpitables[RSDP].size, 3);
+
+    acpitables[XSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+    /*
+     * Currently only 3 tables(GTDT, FADT, MADT) are pointed by XSDT. Alloc
+     * entries for them.
+     */
+    acpitables[XSDT].size = sizeof(struct acpi_table_xsdt) +
+                            sizeof(uint64_t) * 2;
+    dom->acpi_modules[0].length += ROUNDUP(acpitables[XSDT].size, 3);
+
+    acpitables[GTDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+    acpitables[GTDT].size = sizeof(struct acpi_table_gtdt);
+    dom->acpi_modules[0].length += ROUNDUP(acpitables[GTDT].size, 3);
+
+    acpitables[MADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+
+    rc = libxl__estimate_madt_size(gc, info, &size);
+    if (rc < 0)
+        goto out;
+
+    acpitables[MADT].size = size;
+    dom->acpi_modules[0].length += ROUNDUP(acpitables[MADT].size, 3);
+
+    acpitables[FADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+    acpitables[FADT].size = sizeof(struct acpi_table_fadt);
+    dom->acpi_modules[0].length += ROUNDUP(acpitables[FADT].size, 3);
+
+    acpitables[DSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
+    acpitables[DSDT].size = dsdt_anycpu_arm_len;
+    dom->acpi_modules[0].length += ROUNDUP(acpitables[DSDT].size, 3);
+
+    assert(dom->acpi_modules[0].length <= GUEST_ACPI_SIZE);
+    dom->acpi_modules[0].data = libxl__zalloc(gc, dom->acpi_modules[0].length);
+
+    rc = 0;
+out:
+    return rc;
+}
+
+static void calculate_checksum(void *table, uint32_t checksum_offset,
+                               uint32_t length)
+{
+    uint8_t *p, sum = 0;
+
+    p = table;
+    p[checksum_offset] = 0;
+
+    while (length--)
+        sum = sum + *p++;
+
+    p = table;
+    p[checksum_offset] = -sum;
+}
+
+static void make_acpi_rsdp(libxl__gc *gc, struct xc_dom_image *dom,
+                           struct acpitable acpitables[])
+{
+    uint64_t offset = acpitables[RSDP].addr - GUEST_ACPI_BASE;
+    struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset;
+
+    memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
+    BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(rsdp->oem_id));
+    memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id));
+    rsdp->length = acpitables[RSDP].size;
+    rsdp->revision = 0x02;
+    rsdp->xsdt_physical_address = acpitables[XSDT].addr;
+    calculate_checksum(rsdp,
+                       offsetof(struct acpi_table_rsdp, extended_checksum),
+                       acpitables[RSDP].size);
+}
+
+static void make_acpi_header(struct acpi_table_header *h, const char *sig,
+                             size_t len, uint8_t rev)
+{
+    memcpy(h->signature, sig, 4);
+    h->length = len;
+    h->revision = rev;
+    BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(h->oem_id));
+    memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id));
+    BUILD_BUG_ON(sizeof(ACPI_OEM_TABLE_ID) != sizeof(h->oem_table_id));
+    memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id));
+    h->oem_revision = 0;
+    BUILD_BUG_ON(sizeof(ACPI_ASL_COMPILER_ID) != sizeof(h->asl_compiler_id));
+    memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID,
+           sizeof(h->asl_compiler_id));
+    h->asl_compiler_revision = 0;
+    h->checksum = 0;
+}
+
+static void make_acpi_xsdt(libxl__gc *gc, struct xc_dom_image *dom,
+                           struct acpitable acpitables[])
+{
+    uint64_t offset = acpitables[XSDT].addr - GUEST_ACPI_BASE;
+    struct acpi_table_xsdt *xsdt = (void *)dom->acpi_modules[0].data + offset;
+
+    xsdt->table_offset_entry[0] = acpitables[MADT].addr;
+    xsdt->table_offset_entry[1] = acpitables[GTDT].addr;
+    xsdt->table_offset_entry[2] = acpitables[FADT].addr;
+    make_acpi_header(&xsdt->header, "XSDT", acpitables[XSDT].size, 1);
+    calculate_checksum(xsdt, offsetof(struct acpi_table_header, checksum),
+                       acpitables[XSDT].size);
+}
+
+static void make_acpi_gtdt(libxl__gc *gc, struct xc_dom_image *dom,
+                           struct acpitable acpitables[])
+{
+    uint64_t offset = acpitables[GTDT].addr - GUEST_ACPI_BASE;
+    struct acpi_table_gtdt *gtdt = (void *)dom->acpi_modules[0].data + offset;
+
+    gtdt->non_secure_el1_interrupt = GUEST_TIMER_PHYS_NS_PPI;
+    gtdt->non_secure_el1_flags =
+                             (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
+                             |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
+    gtdt->virtual_timer_interrupt = GUEST_TIMER_VIRT_PPI;
+    gtdt->virtual_timer_flags =
+                             (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
+                             |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
+
+    gtdt->counter_block_addresss = ~((uint64_t)0);
+    gtdt->counter_read_block_address = ~((uint64_t)0);
+
+    make_acpi_header(&gtdt->header, "GTDT", acpitables[GTDT].size, 2);
+    calculate_checksum(gtdt, offsetof(struct acpi_table_header, checksum),
+                       acpitables[GTDT].size);
+}
+
+static void make_acpi_madt_gicc(void *table, int nr_cpus, uint64_t gicc_base)
+{
+    int i;
+    struct acpi_madt_generic_interrupt *gicc = table;
+
+    for (i = 0; i < nr_cpus; i++) {
+        gicc->header.type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
+        gicc->header.length = ACPI_MADT_GICC_SIZE_v5;
+        gicc->base_address = gicc_base;
+        gicc->cpu_interface_number = i;
+        gicc->arm_mpidr = libxl__compute_mpdir(i);
+        gicc->uid = i;
+        gicc->flags = ACPI_MADT_ENABLED;
+        gicc = table + ACPI_MADT_GICC_SIZE_v5;
+    }
+}
+
+static void make_acpi_madt_gicd(void *table, uint64_t gicd_base,
+                                uint8_t gic_version)
+{
+    struct acpi_madt_generic_distributor *gicd = table;
+
+    gicd->header.type = ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR;
+    gicd->header.length = sizeof(*gicd);
+    gicd->base_address = gicd_base;
+    /* This version field has no meaning before ACPI 5.1 errata. */
+    gicd->version = gic_version;
+}
+
+static void make_acpi_madt_gicr(void *table, uint64_t gicr_base,
+                                uint64_t gicr_size)
+{
+    struct acpi_madt_generic_redistributor *gicr = table;
+
+    gicr->header.type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
+    gicr->header.length = sizeof(*gicr);
+    gicr->base_address = gicr_base;
+    gicr->length = gicr_size;
+}
+
+static int make_acpi_madt(libxl__gc *gc, struct xc_dom_image *dom,
+                          libxl_domain_build_info *info,
+                          struct acpitable acpitables[])
+{
+    uint64_t offset = acpitables[MADT].addr - GUEST_ACPI_BASE;
+    void *table = dom->acpi_modules[0].data + offset;
+    struct acpi_table_madt *madt = table;
+    int rc = 0;
+
+    switch (info->arch_arm.gic_version) {
+    case LIBXL_GIC_VERSION_V2:
+        table += sizeof(struct acpi_table_madt);
+        make_acpi_madt_gicc(table, info->max_vcpus, GUEST_GICC_BASE);
+
+        table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
+        make_acpi_madt_gicd(table, GUEST_GICD_BASE, ACPI_MADT_GIC_VERSION_V2);
+        break;
+    case LIBXL_GIC_VERSION_V3:
+        table += sizeof(struct acpi_table_madt);
+        make_acpi_madt_gicc(table, info->max_vcpus, 0);
+
+        table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
+        make_acpi_madt_gicd(table, GUEST_GICV3_GICD_BASE,
+                            ACPI_MADT_GIC_VERSION_V3);
+
+        table += sizeof(struct acpi_madt_generic_distributor);
+        make_acpi_madt_gicr(table, GUEST_GICV3_GICR0_BASE,
+                            GUEST_GICV3_GICR0_SIZE);
+        break;
+    default:
+        LOG(ERROR, "Unknown GIC version");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    make_acpi_header(&madt->header, "APIC", acpitables[MADT].size, 3);
+    calculate_checksum(madt, offsetof(struct acpi_table_header, checksum),
+                       acpitables[MADT].size);
+
+out:
+    return rc;
+}
+
+static void make_acpi_fadt(libxl__gc *gc, struct xc_dom_image *dom,
+                           struct acpitable acpitables[])
+{
+    uint64_t offset = acpitables[FADT].addr - GUEST_ACPI_BASE;
+    struct acpi_table_fadt *fadt = (void *)dom->acpi_modules[0].data + offset;
+
+    /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
+    fadt->flags = ACPI_FADT_HW_REDUCED;
+    fadt->arm_boot_flags = ACPI_FADT_PSCI_COMPLIANT | ACPI_FADT_PSCI_USE_HVC;
+
+    /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
+    fadt->minor_revision = 0x1;
+    fadt->dsdt = acpitables[DSDT].addr;
+
+    make_acpi_header(&fadt->header, "FACP", acpitables[FADT].size, 5);
+    calculate_checksum(fadt, offsetof(struct acpi_table_header, checksum),
+                       acpitables[FADT].size);
+}
+
+static void make_acpi_dsdt(libxl__gc *gc, struct xc_dom_image *dom,
+                           struct acpitable acpitables[])
+{
+    uint64_t offset = acpitables[DSDT].addr - GUEST_ACPI_BASE;
+    void *dsdt = dom->acpi_modules[0].data + offset;
+
+    memcpy(dsdt, dsdt_anycpu_arm, dsdt_anycpu_arm_len);
+}
+
+int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
+                        struct xc_dom_image *dom)
+{
+    const libxl_version_info *vers;
+    int rc = 0;
+    struct acpitable acpitables[MAX_TABLE_NUMS];
+
+    vers = libxl_get_version_info(CTX);
+    if (vers == NULL) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    LOG(DEBUG, "constructing ACPI tables for Xen version %d.%d guest",
+        vers->xen_version_major, vers->xen_version_minor);
+
+    dom->acpi_modules[0].data = NULL;
+    dom->acpi_modules[0].length = 0;
+    dom->acpi_modules[0].guest_addr_out = GUEST_ACPI_BASE;
+
+    rc = libxl__allocate_acpi_tables(gc, info, dom, acpitables);
+    if (rc)
+        goto out;
+
+    make_acpi_rsdp(gc, dom, acpitables);
+    make_acpi_xsdt(gc, dom, acpitables);
+    make_acpi_gtdt(gc, dom, acpitables);
+    rc = make_acpi_madt(gc, dom, info, acpitables);
+    if (rc)
+        goto out;
+
+    make_acpi_fadt(gc, dom, acpitables);
+    make_acpi_dsdt(gc, dom, acpitables);
+
+out:
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_arm_no_acpi.c b/tools/libs/light/libxl_arm_no_acpi.c
new file mode 100644 (file)
index 0000000..5dde0cd
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * ARM DomU ACPI generation
+ *
+ * Copyright (C) 2016      Linaro Ltd.
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_arm.h"
+
+int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
+                        struct xc_dom_image *dom)
+{
+    return ERROR_FAIL;
+}
+
+int libxl__get_acpi_size(libxl__gc *gc,
+                         const libxl_domain_build_info *info,
+                         uint64_t *out)
+{
+    return ERROR_FAIL;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c
new file mode 100644 (file)
index 0000000..18e9ebd
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) 2010      Citrix Ltd.
+ * Author Ian Campbell <ian.campbell@citrix.com>
+ *
+ * This program 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; version 2.1 only.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <termios.h>
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+
+#ifdef INCLUDE_LIBUTIL_H
+#include INCLUDE_LIBUTIL_H
+#endif
+
+#include "libxl_internal.h"
+
+#define BOOTLOADER_BUF_OUT 65536
+#define BOOTLOADER_BUF_IN   4096
+
+static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op);
+static void bootloader_keystrokes_copyfail(libxl__egc *egc,
+       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
+static void bootloader_display_copyfail(libxl__egc *egc,
+       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
+static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc,
+                                   int rc);
+static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
+                                pid_t pid, int status);
+
+/*----- bootloader arguments -----*/
+
+static void bootloader_arg(libxl__bootloader_state *bl, const char *arg)
+{
+    assert(bl->nargs < bl->argsspace);
+    bl->args[bl->nargs++] = arg;
+}
+
+static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
+                                 const char *bootloader_path)
+{
+    const libxl_domain_build_info *info = bl->info;
+
+    bl->argsspace = 9 + libxl_string_list_length(&info->bootloader_args);
+
+    GCNEW_ARRAY(bl->args, bl->argsspace);
+
+#define ARG(arg) bootloader_arg(bl, (arg))
+
+    ARG(bootloader_path);
+
+    if (info->kernel)
+        ARG(GCSPRINTF("--kernel=%s", info->kernel));
+    if (info->ramdisk)
+        ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk));
+    if (info->cmdline && *info->cmdline != '\0')
+        ARG(GCSPRINTF("--args=%s", info->cmdline));
+
+    ARG(GCSPRINTF("--output=%s", bl->outputpath));
+    ARG("--output-format=simple0");
+    ARG(GCSPRINTF("--output-directory=%s", bl->outputdir));
+
+    if (info->bootloader_args) {
+        char **p = info->bootloader_args;
+        while (*p) {
+            ARG(*p);
+            p++;
+        }
+    }
+
+    ARG(bl->dls.diskpath);
+
+    /* Sentinel for execv */
+    ARG(NULL);
+
+#undef ARG
+}
+
+/*----- synchronous subroutines -----*/
+
+static int setup_xenconsoled_pty(libxl__egc *egc, libxl__bootloader_state *bl,
+                                 char *slave_path, size_t slave_path_len)
+{
+    STATE_AO_GC(bl->ao);
+    struct termios termattr;
+    int r, rc;
+    int slave = libxl__carefd_fd(bl->ptys[1].slave);
+    int master = libxl__carefd_fd(bl->ptys[1].master);
+
+    r = ttyname_r(slave, slave_path, slave_path_len);
+    if (r == -1) {
+        LOGED(ERROR, bl->domid, "ttyname_r failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /*
+     * On Solaris, the pty master side will get cranky if we try
+     * to write to it while there is no slave. To work around this,
+     * keep the slave descriptor open until we're done. Set it
+     * to raw terminal parameters, otherwise it will echo back
+     * characters, which will confuse the I/O loop below.
+     * Furthermore, a raw master pty device has no terminal
+     * semantics on Solaris, so don't try to set any attributes
+     * for it.
+     */
+    tcgetattr(master, &termattr);
+    cfmakeraw(&termattr);
+    tcsetattr(master, TCSANOW, &termattr);
+
+    return 0;
+
+ out:
+    return rc;
+}
+
+static const char *bootloader_result_command(libxl__gc *gc, const char *buf,
+                         const char *prefix, size_t prefixlen, uint32_t domid) {
+    if (strncmp(buf, prefix, prefixlen))
+        return 0;
+
+    const char *rhs = buf + prefixlen;
+    if (!CTYPE(isspace,*rhs))
+        return 0;
+
+    while (CTYPE(isspace,*rhs))
+        rhs++;
+
+    LOGD(DEBUG, domid, "bootloader output contained %s %s", prefix, rhs);
+
+    return rhs;
+}
+
+static int parse_bootloader_result(libxl__egc *egc,
+                                   libxl__bootloader_state *bl)
+{
+    STATE_AO_GC(bl->ao);
+    char buf[PATH_MAX*2];
+    FILE *f = 0;
+    int rc = ERROR_FAIL;
+
+    f = fopen(bl->outputpath, "r");
+    if (!f) {
+        LOGED(ERROR, bl->domid, "open bootloader output file %s",
+              bl->outputpath);
+        goto out;
+    }
+
+    for (;;) {
+        /* Read a nul-terminated "line" and put the result in
+         * buf, and its length (not including the nul) in l */
+        int l = 0, c;
+        while ((c = getc(f)) != EOF && c != '\0') {
+            if (l < sizeof(buf)-1)
+                buf[l] = c;
+            l++;
+        }
+        if (c == EOF) {
+            if (ferror(f)) {
+                LOGED(ERROR, bl->domid, "read bootloader output file %s",
+                      bl->outputpath);
+                goto out;
+            }
+            if (!l)
+                break;
+        }
+        if (l >= sizeof(buf)) {
+            LOGD(WARN, bl->domid, "bootloader output contained"
+                 " overly long item `%.150s...'", buf);
+            continue;
+        }
+        buf[l] = 0;
+
+        const char *rhs;
+#define COMMAND(s) ((rhs = bootloader_result_command(gc, buf, s, sizeof(s)-1, bl->domid)))
+
+        if (COMMAND("kernel")) {
+            bl->kernel->path = libxl__strdup(gc, rhs);
+            libxl__file_reference_map(bl->kernel);
+            unlink(bl->kernel->path);
+        } else if (COMMAND("ramdisk")) {
+            bl->ramdisk->path = libxl__strdup(gc, rhs);
+            libxl__file_reference_map(bl->ramdisk);
+            unlink(bl->ramdisk->path);
+        } else if (COMMAND("args")) {
+            bl->cmdline = libxl__strdup(gc, rhs);
+        } else if (l) {
+            LOGD(WARN, bl->domid,
+                 "unexpected output from bootloader: `%s'", buf);
+        }
+    }
+    rc = 0;
+
+ out:
+    if (f) fclose(f);
+    return rc;
+}
+
+
+/*----- init and cleanup -----*/
+
+void libxl__bootloader_init(libxl__bootloader_state *bl)
+{
+    assert(bl->ao);
+    bl->rc = 0;
+    bl->dls.diskpath = NULL;
+    bl->openpty.ao = bl->ao;
+    bl->dls.ao = bl->ao;
+    bl->ptys[0].master = bl->ptys[0].slave = 0;
+    bl->ptys[1].master = bl->ptys[1].slave = 0;
+    libxl__ev_child_init(&bl->child);
+    libxl__domaindeathcheck_init(&bl->deathcheck);
+    bl->keystrokes.ao = bl->ao;  libxl__datacopier_init(&bl->keystrokes);
+    bl->display.ao = bl->ao;     libxl__datacopier_init(&bl->display);
+    bl->got_pollhup = 0;
+}
+
+static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl)
+{
+    STATE_AO_GC(bl->ao);
+    int i;
+
+    if (bl->outputpath) libxl__remove_file(gc, bl->outputpath);
+    if (bl->outputdir) libxl__remove_directory(gc, bl->outputdir);
+
+    libxl__domaindeathcheck_stop(gc,&bl->deathcheck);
+    libxl__datacopier_kill(&bl->keystrokes);
+    libxl__datacopier_kill(&bl->display);
+    for (i=0; i<2; i++) {
+        libxl__carefd_close(bl->ptys[i].master);
+        libxl__carefd_close(bl->ptys[i].slave);
+    }
+    if (bl->display.log) {
+        fclose(bl->display.log);
+        bl->display.log = NULL;
+    }
+}
+
+static void bootloader_setpaths(libxl__gc *gc, libxl__bootloader_state *bl)
+{
+    uint32_t domid = bl->domid;
+    bl->outputdir = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".d", domid);
+    bl->outputpath = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".out", domid);
+}
+
+/* Callbacks */
+
+static void bootloader_local_detached_cb(libxl__egc *egc,
+                                         libxl__disk_local_state *dls,
+                                         int rc);
+
+static void bootloader_callback(libxl__egc *egc, libxl__bootloader_state *bl,
+                                int rc)
+{
+    if (!bl->rc)
+        bl->rc = rc;
+
+    bootloader_cleanup(egc, bl);
+
+    bl->dls.callback = bootloader_local_detached_cb;
+    libxl__device_disk_local_initiate_detach(egc, &bl->dls);
+}
+
+static void bootloader_local_detached_cb(libxl__egc *egc,
+                                         libxl__disk_local_state *dls,
+                                         int rc)
+{
+    STATE_AO_GC(dls->ao);
+    libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
+
+    if (rc) {
+        LOGD(ERROR, bl->domid,
+             "unable to detach locally attached disk");
+        if (!bl->rc)
+            bl->rc = rc;
+    }
+
+    bl->callback(egc, bl, bl->rc);
+}
+
+/* might be called at any time, provided it's init'd */
+static void bootloader_stop(libxl__egc *egc,
+                             libxl__bootloader_state *bl, int rc)
+{
+    STATE_AO_GC(bl->ao);
+    int r;
+
+    libxl__datacopier_kill(&bl->keystrokes);
+    libxl__datacopier_kill(&bl->display);
+    if (libxl__ev_child_inuse(&bl->child)) {
+        r = kill(bl->child.pid, SIGTERM);
+        if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]",
+                     rc ? "after failure, " : "", (unsigned long)bl->child.pid);
+    }
+    if (!bl->rc)
+        bl->rc = rc;
+}
+
+/*----- main flow of control -----*/
+
+/* Callbacks */
+
+static void bootloader_disk_attached_cb(libxl__egc *egc,
+                                        libxl__disk_local_state *dls,
+                                        int rc);
+
+void libxl__bootloader_run(libxl__egc *egc, libxl__bootloader_state *bl)
+{
+    STATE_AO_GC(bl->ao);
+    const libxl_domain_build_info *info = bl->info;
+    uint32_t domid = bl->domid;
+    char *logfile_tmp = NULL;
+    int rc, r;
+
+    libxl__bootloader_init(bl);
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        LOGD(DEBUG, domid, "not a PV/PVH domain, skipping bootloader");
+        rc = 0;
+        goto out_ok;
+    }
+
+    if (!info->bootloader) {
+        LOGD(DEBUG, domid,
+             "no bootloader configured, using user supplied kernel");
+        bl->kernel->path = bl->info->kernel;
+        bl->ramdisk->path = bl->info->ramdisk;
+        bl->cmdline = bl->info->cmdline;
+        rc = 0;
+        goto out_ok;
+    }
+
+    if (!bl->disk) {
+        LOGD(ERROR, domid, "cannot run bootloader with no boot disk");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    bootloader_setpaths(gc, bl);
+
+    const char *logfile_leaf = GCSPRINTF("bootloader.%"PRIu32, domid);
+    rc = libxl_create_logfile(CTX, logfile_leaf, &logfile_tmp);
+    if (rc) goto out;
+
+    /* Transfer ownership of log filename to bl and the gc */
+    bl->logfile = logfile_tmp;
+    libxl__ptr_add(gc, logfile_tmp);
+    logfile_tmp = NULL;
+
+    bl->display.log = fopen(bl->logfile, "a");
+    if (!bl->display.log) {
+        LOGED(ERROR, domid,
+              "failed to create bootloader logfile %s", bl->logfile);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    for (;;) {
+        r = mkdir(bl->outputdir, 0600);
+        if (!r) break;
+        if (errno == EINTR) continue;
+        if (errno == EEXIST) break;
+        LOGED(ERROR, domid,
+              "failed to create bootloader dir %s", bl->outputdir);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    for (;;) {
+        r = open(bl->outputpath, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+        if (r>=0) { close(r); break; }
+        if (errno == EINTR) continue;
+        LOGED(ERROR, domid,
+              "failed to precreate bootloader output %s", bl->outputpath);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+
+    /* This sets the state of the dls struct from Undefined to Idle */
+    libxl__device_disk_local_init(&bl->dls);
+    bl->dls.ao = ao;
+    bl->dls.in_disk = bl->disk;
+    bl->dls.blkdev_start = info->blkdev_start;
+    bl->dls.callback = bootloader_disk_attached_cb;
+    libxl__device_disk_local_initiate_attach(egc, &bl->dls);
+    return;
+
+ out:
+    assert(rc);
+ out_ok:
+    free(logfile_tmp);
+    bootloader_callback(egc, bl, rc);
+}
+
+static void bootloader_disk_attached_cb(libxl__egc *egc,
+                                        libxl__disk_local_state *dls,
+                                        int rc)
+{
+    STATE_AO_GC(dls->ao);
+    libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
+    const libxl_domain_build_info *info = bl->info;
+    const char *bootloader;
+
+    if (rc) {
+        LOGD(ERROR, bl->domid,
+             "failed to attach local disk for bootloader execution");
+        goto out;
+    }
+
+    LOGD(DEBUG, bl->domid,
+         "Config bootloader value: %s", info->bootloader);
+
+    if ( !strcmp(info->bootloader, "/usr/bin/pygrub") )
+        LOGD(WARN, bl->domid,
+             "bootloader='/usr/bin/pygrub' is deprecated; use " \
+             "bootloader='pygrub' instead");
+
+    bootloader = info->bootloader;
+
+    /* If the full path is not specified, check in the libexec path */
+    if ( bootloader[0] != '/' ) {
+        const char *bltmp;
+        struct stat st;
+
+        bltmp = libxl__abs_path(gc, bootloader, libxl__private_bindir_path());
+        /* Check to see if the file exists in this location; if not,
+         * fall back to checking the path */
+        LOGD(DEBUG, bl->domid,
+             "Checking for bootloader in libexec path: %s", bltmp);
+
+        if ( lstat(bltmp, &st) )
+            LOGD(DEBUG, bl->domid,
+                 "%s doesn't exist, falling back to config path",
+                 bltmp);
+        else
+            bootloader = bltmp;
+    }
+
+    make_bootloader_args(gc, bl, bootloader);
+
+    bl->openpty.ao = ao;
+    bl->openpty.callback = bootloader_gotptys;
+    bl->openpty.count = 2;
+    bl->openpty.results = bl->ptys;
+    rc = libxl__openptys(&bl->openpty, 0,0);
+    if (rc) goto out;
+
+    return;
+
+ out:
+    assert(rc);
+    bootloader_callback(egc, bl, rc);
+}
+
+static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op)
+{
+    libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty);
+    STATE_AO_GC(bl->ao);
+    int rc, r;
+    char *const env[] = { "TERM", "vt100", NULL };
+
+    if (bl->openpty.rc) {
+        rc = bl->openpty.rc;
+        goto out;
+    }
+
+    /*
+     * We need to present the bootloader's tty as a pty slave that xenconsole
+     * can access.  Since the bootloader itself needs a pty slave,
+     * we end up with a connection like this:
+     *
+     * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
+     *
+     * where we copy characters between the two master fds, as well as
+     * listening on the bootloader's fifo for the results.
+     */
+
+    char *dom_console_xs_path;
+    char dom_console_slave_tty_path[PATH_MAX];
+    rc = setup_xenconsoled_pty(egc, bl,
+                               &dom_console_slave_tty_path[0],
+                               sizeof(dom_console_slave_tty_path));
+    if (rc) goto out;
+
+    char *dompath = libxl__xs_get_dompath(gc, bl->domid);
+    if (!dompath) { rc = ERROR_FAIL; goto out; }
+
+    dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath);
+
+    rc = libxl__xs_printf(gc, XBT_NULL, dom_console_xs_path, "%s",
+                          dom_console_slave_tty_path);
+    if (rc) {
+        LOGED(ERROR, bl->domid, "xs write console path %s := %s failed",
+             dom_console_xs_path, dom_console_slave_tty_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    bl->deathcheck.what = "stopping bootloader";
+    bl->deathcheck.domid = bl->domid;
+    bl->deathcheck.callback = bootloader_domaindeath;
+    rc = libxl__domaindeathcheck_start(ao, &bl->deathcheck);
+    if (rc) goto out;
+
+    if (bl->console_available)
+        bl->console_available(egc, bl);
+
+    int bootloader_master = libxl__carefd_fd(bl->ptys[0].master);
+    int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master);
+
+    libxl_fd_set_nonblock(CTX, bootloader_master, 1);
+    libxl_fd_set_nonblock(CTX, xenconsole_master, 1);
+
+    bl->keystrokes.writefd   = bl->display.readfd   = bootloader_master;
+    bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty";
+
+    bl->keystrokes.readfd   = bl->display.writefd   = xenconsole_master;
+    bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty";
+
+    bl->keystrokes.ao = ao;
+    bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT;
+    bl->keystrokes.bytes_to_read = -1;
+    bl->keystrokes.copywhat =
+        GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid);
+    bl->keystrokes.callback =         bootloader_keystrokes_copyfail;
+    bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail;
+        /* pollhup gets called with errnoval==-1 which is not otherwise
+         * possible since errnos are nonnegative, so it's unambiguous */
+    rc = libxl__datacopier_start(&bl->keystrokes);
+    if (rc) goto out;
+
+    bl->display.ao = ao;
+    bl->display.maxsz = BOOTLOADER_BUF_IN;
+    bl->display.bytes_to_read = -1;
+    bl->display.copywhat =
+        GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid);
+    bl->display.callback =         bootloader_display_copyfail;
+    bl->display.callback_pollhup = bootloader_display_copyfail;
+    rc = libxl__datacopier_start(&bl->display);
+    if (rc) goto out;
+
+    LOGD(DEBUG, bl->domid, "executing bootloader: %s", bl->args[0]);
+    for (const char **blarg = bl->args;
+         *blarg;
+         blarg++)
+        LOGD(DEBUG, bl->domid, "  bootloader arg: %s", *blarg);
+
+    struct termios termattr;
+
+    pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished);
+    if (pid == -1) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (!pid) {
+        /* child */
+        r = login_tty(libxl__carefd_fd(bl->ptys[0].slave));
+        if (r) { LOGED(ERROR, bl->domid, "login_tty failed"); exit(-1); }
+        libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env);
+    }
+
+    /* parent */
+
+    /*
+     * On Solaris, the master pty side does not have terminal semantics,
+     * so don't try to set any attributes, as it will fail.
+     */
+#if !defined(__sun__)
+    tcgetattr(bootloader_master, &termattr);
+    cfmakeraw(&termattr);
+    tcsetattr(bootloader_master, TCSANOW, &termattr);
+#endif
+
+    return;
+
+ out:
+    bootloader_callback(egc, bl, rc);
+}
+
+/* perhaps one of these will be called, but perhaps not */
+static void bootloader_copyfail(libxl__egc *egc, const char *which,
+        libxl__bootloader_state *bl, int ondisplay,
+        int rc, int onwrite, int errnoval)
+{
+    STATE_AO_GC(bl->ao);
+
+    if (errnoval==-1) {
+        /* POLLHUP */
+        if (!!ondisplay != !!onwrite) {
+            rc = 0;
+            bl->got_pollhup = 1;
+        } else {
+            LOGD(ERROR, bl->domid, "unexpected POLLHUP on %s", which);
+        }
+    } else if (!rc) {
+        LOGD(ERROR, bl->domid, "unexpected eof copying %s", which);
+        rc = ERROR_FAIL;
+    }
+
+    bootloader_stop(egc, bl, rc);
+}
+static void bootloader_keystrokes_copyfail(libxl__egc *egc,
+       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
+{
+    libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, keystrokes);
+    bootloader_copyfail(egc, "bootloader input", bl, 0, rc,onwrite,errnoval);
+}
+static void bootloader_display_copyfail(libxl__egc *egc,
+       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
+{
+    libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display);
+    bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval);
+}
+
+static void bootloader_domaindeath(libxl__egc *egc,
+                                   libxl__domaindeathcheck *dc,
+                                   int rc)
+{
+    libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, deathcheck);
+    bootloader_stop(egc, bl, rc);
+}
+
+static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
+                                pid_t pid, int status)
+{
+    libxl__bootloader_state *bl = CONTAINER_OF(child, *bl, child);
+    STATE_AO_GC(bl->ao);
+    int rc;
+
+    libxl__datacopier_kill(&bl->keystrokes);
+    libxl__datacopier_kill(&bl->display);
+
+    if (status) {
+        if (bl->got_pollhup && WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)
+            LOGD(ERROR, bl->domid, "got POLLHUP, sent SIGTERM");
+        LOGD(ERROR, bl->domid,
+             "bootloader failed - consult logfile %s", bl->logfile);
+        libxl_report_child_exitstatus(CTX, XTL_ERROR, "bootloader",
+                                      pid, status);
+        rc = ERROR_FAIL;
+        goto out;
+    } else {
+        LOGD(DEBUG, bl->domid, "bootloader completed");
+    }
+
+    if (bl->rc) {
+        /* datacopier went wrong */
+        rc = bl->rc;
+        goto out;
+    }
+
+    rc = parse_bootloader_result(egc, bl);
+    if (rc) goto out;
+
+    rc = 0;
+    LOGD(DEBUG, bl->domid, "bootloader execution successful");
+
+ out:
+    bootloader_callback(egc, bl, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_checkpoint_device.c b/tools/libs/light/libxl_checkpoint_device.c
new file mode 100644 (file)
index 0000000..f6395dc
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2014 FUJITSU LIMITED
+ * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*----- setup() and teardown() -----*/
+
+/* callbacks */
+
+static void all_devices_setup_cb(libxl__egc *egc,
+                                 libxl__multidev *multidev,
+                                 int rc);
+static void device_setup_iterate(libxl__egc *egc,
+                                 libxl__ao_device *aodev);
+static void devices_teardown_cb(libxl__egc *egc,
+                                libxl__multidev *multidev,
+                                int rc);
+
+/* checkpoint device setup and teardown */
+
+static libxl__checkpoint_device* checkpoint_device_init(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds,
+                                        libxl__device_kind kind,
+                                        void *libxl_dev)
+{
+    libxl__checkpoint_device *dev = NULL;
+
+    STATE_AO_GC(cds->ao);
+    GCNEW(dev);
+    dev->backend_dev = libxl_dev;
+    dev->kind = kind;
+    dev->cds = cds;
+
+    return dev;
+}
+
+static void checkpoint_devices_setup(libxl__egc *egc,
+                                     libxl__checkpoint_devices_state *cds);
+
+void libxl__checkpoint_devices_setup(libxl__egc *egc,
+                                     libxl__checkpoint_devices_state *cds)
+{
+    int i;
+
+    STATE_AO_GC(cds->ao);
+
+    cds->num_devices = 0;
+    cds->num_nics = 0;
+    cds->num_disks = 0;
+
+    if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VIF))
+        cds->nics = libxl__device_list(gc, &libxl__nic_devtype, cds->domid,
+                                       &cds->num_nics);
+
+    if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VBD))
+        cds->disks = libxl__device_list(gc, &libxl__disk_devtype, cds->domid,
+                                        &cds->num_disks);
+
+    if (cds->num_nics == 0 && cds->num_disks == 0)
+        goto out;
+
+    GCNEW_ARRAY(cds->devs, cds->num_nics + cds->num_disks);
+
+    for (i = 0; i < cds->num_nics; i++) {
+        cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
+                                                LIBXL__DEVICE_KIND_VIF,
+                                                &cds->nics[i]);
+    }
+
+    for (i = 0; i < cds->num_disks; i++) {
+        cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
+                                                LIBXL__DEVICE_KIND_VBD,
+                                                &cds->disks[i]);
+    }
+
+    checkpoint_devices_setup(egc, cds);
+
+    return;
+
+out:
+    cds->callback(egc, cds, 0);
+}
+
+static void checkpoint_devices_setup(libxl__egc *egc,
+                                     libxl__checkpoint_devices_state *cds)
+{
+    int i, rc;
+
+    STATE_AO_GC(cds->ao);
+
+    libxl__multidev_begin(ao, &cds->multidev);
+    cds->multidev.callback = all_devices_setup_cb;
+    for (i = 0; i < cds->num_devices; i++) {
+        libxl__checkpoint_device *dev = cds->devs[i];
+        dev->ops_index = -1;
+        libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
+
+        dev->aodev.rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
+        dev->aodev.callback = device_setup_iterate;
+        device_setup_iterate(egc,&dev->aodev);
+    }
+
+    rc = 0;
+    libxl__multidev_prepared(egc, &cds->multidev, rc);
+}
+
+
+static void device_setup_iterate(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+    EGC_GC;
+
+    if (aodev->rc != ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED &&
+        aodev->rc != ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH)
+        /* might be success or disaster */
+        goto out;
+
+    do {
+        dev->ops = dev->cds->ops[++dev->ops_index];
+        if (!dev->ops) {
+            libxl_device_nic * nic = NULL;
+            libxl_device_disk * disk = NULL;
+            uint32_t domid = INVALID_DOMID;
+            int devid;
+            if (dev->kind == LIBXL__DEVICE_KIND_VIF) {
+                nic = (libxl_device_nic *)dev->backend_dev;
+                domid = nic->backend_domid;
+                devid = nic->devid;
+            } else if (dev->kind == LIBXL__DEVICE_KIND_VBD) {
+                disk = (libxl_device_disk *)dev->backend_dev;
+                domid = disk->backend_domid;
+                devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+            } else {
+                LOGD(ERROR, domid, "device kind not handled by checkpoint: %s",
+                     libxl__device_kind_to_string(dev->kind));
+                aodev->rc = ERROR_FAIL;
+                goto out;
+            }
+            LOGD(ERROR, domid, "device not handled by checkpoint"
+                 " (device=%s:%"PRId32"/%"PRId32")",
+                 libxl__device_kind_to_string(dev->kind),
+                 domid, devid);
+            aodev->rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
+            goto out;
+        }
+    } while (dev->ops->kind != dev->kind);
+
+    /* found the next ops_index to try */
+    assert(dev->aodev.callback == device_setup_iterate);
+    dev->ops->setup(egc,dev);
+    return;
+
+ out:
+    libxl__multidev_one_callback(egc,aodev);
+}
+
+static void all_devices_setup_cb(libxl__egc *egc,
+                                 libxl__multidev *multidev,
+                                 int rc)
+{
+    STATE_AO_GC(multidev->ao);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds =
+                            CONTAINER_OF(multidev, *cds, multidev);
+
+    cds->callback(egc, cds, rc);
+}
+
+void libxl__checkpoint_devices_teardown(libxl__egc *egc,
+                                   libxl__checkpoint_devices_state *cds)
+{
+    int i;
+    libxl__checkpoint_device *dev;
+
+    STATE_AO_GC(cds->ao);
+
+    libxl__multidev_begin(ao, &cds->multidev);
+    cds->multidev.callback = devices_teardown_cb;
+    for (i = 0; i < cds->num_devices; i++) {
+        dev = cds->devs[i];
+        if (!dev->ops || !dev->matched)
+            continue;
+
+        libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
+        dev->ops->teardown(egc,dev);
+    }
+
+    libxl__multidev_prepared(egc, &cds->multidev, 0);
+}
+
+static void devices_teardown_cb(libxl__egc *egc,
+                                libxl__multidev *multidev,
+                                int rc)
+{
+    STATE_AO_GC(multidev->ao);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds =
+                            CONTAINER_OF(multidev, *cds, multidev);
+
+    /* clean nic */
+    libxl__device_list_free(&libxl__nic_devtype, cds->nics, cds->num_nics);
+    cds->nics = NULL;
+    cds->num_nics = 0;
+
+    /* clean disk */
+    libxl__device_list_free(&libxl__disk_devtype, cds->disks, cds->num_disks);
+    cds->disks = NULL;
+    cds->num_disks = 0;
+
+    cds->callback(egc, cds, rc);
+}
+
+/*----- checkpointing APIs -----*/
+
+/* callbacks */
+
+static void devices_checkpoint_cb(libxl__egc *egc,
+                                  libxl__multidev *multidev,
+                                  int rc);
+
+/* API implementations */
+
+#define define_checkpoint_api(api)                                      \
+void libxl__checkpoint_devices_##api(libxl__egc *egc,                   \
+                                libxl__checkpoint_devices_state *cds)   \
+{                                                                       \
+    int i;                                                              \
+    libxl__checkpoint_device *dev;                                      \
+                                                                        \
+    STATE_AO_GC(cds->ao);                                               \
+                                                                        \
+    libxl__multidev_begin(ao, &cds->multidev);                          \
+    cds->multidev.callback = devices_checkpoint_cb;                     \
+    for (i = 0; i < cds->num_devices; i++) {                            \
+        dev = cds->devs[i];                                             \
+        if (!dev->matched || !dev->ops->api)                            \
+            continue;                                                   \
+        libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);\
+        dev->ops->api(egc,dev);                                         \
+    }                                                                   \
+                                                                        \
+    libxl__multidev_prepared(egc, &cds->multidev, 0);                   \
+}
+
+define_checkpoint_api(postsuspend);
+
+define_checkpoint_api(preresume);
+
+define_checkpoint_api(commit);
+
+static void devices_checkpoint_cb(libxl__egc *egc,
+                                  libxl__multidev *multidev,
+                                  int rc)
+{
+    STATE_AO_GC(multidev->ao);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds =
+                            CONTAINER_OF(multidev, *cds, multidev);
+
+    cds->callback(egc, cds, rc);
+}
diff --git a/tools/libs/light/libxl_colo.h b/tools/libs/light/libxl_colo.h
new file mode 100644 (file)
index 0000000..6c01b55
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef LIBXL_COLO_H
+#define LIBXL_COLO_H
+
+#include "libxl_internal.h"
+
+/* Maximum time(5s) to wait for colo proxy checkpoit */
+#define COLO_PROXY_CHECKPOINT_TIMEOUT 5000000
+
+#define ASYNC_CALL(egc, ao, child, param, func, callback) do {          \
+    int pid = -1;                                                       \
+    STATE_AO_GC(ao);                                                    \
+                                                                        \
+    pid = libxl__ev_child_fork(gc, child, callback);                    \
+    if (pid == -1) {                                                    \
+        LOGD(ERROR, ao->domid, "unable to fork");                       \
+        goto out;                                                       \
+    }                                                                   \
+                                                                        \
+    if (!pid) {                                                         \
+        /* child */                                                     \
+        func(param);                                                    \
+        /* notreached */                                                \
+        abort();                                                        \
+    }                                                                   \
+                                                                        \
+    return;                                                             \
+out:                                                                    \
+    callback(egc, child, -1, 1);                                        \
+} while (0)
+
+enum {
+    LIBXL_COLO_SETUPED,
+    LIBXL_COLO_SUSPENDED,
+    LIBXL_COLO_RESUMED,
+};
+
+struct libxl__colo_device_nic {
+    int devid;
+    const char *vif;
+};
+
+struct libxl__colo_qdisk {
+    bool setuped;
+};
+
+struct libxl__colo_proxy_state {
+    /* set by caller of colo_proxy_setup */
+    struct libxl__ao *ao;
+
+    int sock_fd;
+    int index;
+    /*
+     * Private, True means use userspace colo proxy
+     *          False means use kernel colo proxy.
+     */
+    bool is_userspace_proxy;
+    const char *checkpoint_host;
+    const char *checkpoint_port;
+};
+
+struct libxl__colo_save_state {
+    int send_fd;
+    int recv_fd;
+    char *colo_proxy_script;
+
+    /* private */
+    libxl__stream_read_state srs;
+    void (*callback)(libxl__egc *, libxl__colo_save_state *, int);
+    bool svm_running;
+    bool paused;
+
+    /* private, used by qdisk block replication */
+    bool qdisk_used;
+    bool qdisk_setuped;
+
+    /* private, used by colo-proxy */
+    libxl__colo_proxy_state cps;
+    libxl__ev_child child;
+};
+
+
+typedef void libxl__colo_callback(struct libxl__egc *egc,
+                                  libxl__colo_restore_state *crs, int rc);
+
+struct libxl__colo_restore_state {
+    /* must set by caller of libxl__colo_(setup|teardown) */
+    struct libxl__ao *ao;
+    uint32_t domid;
+    int send_back_fd;
+    int recv_fd;
+    int hvm;
+    libxl__colo_callback *callback;
+    char *colo_proxy_script;
+
+    /* private, colo restore checkpoint state */
+    libxl__domain_create_cb *saved_cb;
+    void *crcs;
+
+    /* private, used by qdisk block replication */
+    bool qdisk_used;
+    bool qdisk_setuped;
+    const char *host;
+    const char *port;
+
+    /* private, used by colo-proxy */
+    libxl__colo_proxy_state cps;
+};
+
+int init_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
+
+void cleanup_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
+
+int init_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
+
+void cleanup_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
+
+extern void libxl__colo_restore_setup(struct libxl__egc *egc,
+                                      libxl__colo_restore_state *crs);
+extern void libxl__colo_restore_teardown(struct libxl__egc *egc, void *dcs_void,
+                                         int ret, int retval, int errnoval);
+extern void libxl__colo_save_setup(struct libxl__egc *egc,
+                                   struct libxl__colo_save_state *css);
+extern void libxl__colo_save_teardown(struct libxl__egc *egc,
+                                      struct libxl__colo_save_state *css,
+                                      int rc);
+extern int colo_proxy_setup(libxl__colo_proxy_state *cps);
+extern void colo_proxy_teardown(libxl__colo_proxy_state *cps);
+extern void colo_proxy_preresume(libxl__colo_proxy_state *cps);
+extern void colo_proxy_postresume(libxl__colo_proxy_state *cps);
+extern int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
+                                 unsigned int timeout_us);
+
+#endif
diff --git a/tools/libs/light/libxl_colo_nic.c b/tools/libs/light/libxl_colo_nic.c
new file mode 100644 (file)
index 0000000..bf5c48f
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+enum {
+    primary,
+    secondary,
+};
+
+/* ========== init() and cleanup() ========== */
+
+int init_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
+{
+    return 0;
+}
+
+void cleanup_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
+{
+}
+
+/* ========== helper functions ========== */
+
+static void colo_save_setup_script_cb(libxl__egc *egc,
+                                     libxl__async_exec_state *aes,
+                                     int rc, int status);
+static void colo_save_teardown_script_cb(libxl__egc *egc,
+                                         libxl__async_exec_state *aes,
+                                         int rc, int status);
+
+/*
+ * If the device has a vifname, then use that instead of
+ * the vifX.Y format.
+ * it must ONLY be used for remus because if driver domains
+ * were in use it would constitute a security vulnerability.
+ */
+static const char *get_vifname(libxl__checkpoint_device *dev,
+                               const libxl_device_nic *nic)
+{
+    const char *vifname = NULL;
+    const char *path;
+    int rc;
+
+    STATE_AO_GC(dev->cds->ao);
+
+    /* Convenience aliases */
+    const uint32_t domid = dev->cds->domid;
+
+    path = GCSPRINTF("%s/vifname",
+                     libxl__domain_device_backend_path(gc, 0,
+                     domid, nic->devid, LIBXL__DEVICE_KIND_VIF));
+    rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
+    if (!rc && !vifname) {
+        vifname = libxl__device_nic_devname(gc, domid,
+                                            nic->devid,
+                                            nic->nictype);
+    }
+
+    return vifname;
+}
+
+/*
+ * the script needs the following env & args
+ * $vifname
+ * $forwarddev
+ * $mode(primary/secondary)
+ * $index
+ * $bridge
+ * setup/teardown as command line arg.
+ */
+static void setup_async_exec(libxl__checkpoint_device *dev, char *op,
+                             libxl__colo_proxy_state *cps, int side,
+                             char *colo_proxy_script)
+{
+    int arraysize, nr = 0;
+    char **env = NULL, **args = NULL;
+    libxl__colo_device_nic *colo_nic = dev->concrete_data;
+    libxl__checkpoint_devices_state *cds = dev->cds;
+    libxl__async_exec_state *aes = &dev->aodev.aes;
+    const libxl_device_nic *nic = dev->backend_dev;
+
+    STATE_AO_GC(cds->ao);
+
+    /* Convenience aliases */
+    const char *const vif = colo_nic->vif;
+
+    arraysize = 11;
+    GCNEW_ARRAY(env, arraysize);
+    env[nr++] = "vifname";
+    env[nr++] = libxl__strdup(gc, vif);
+    env[nr++] = "forwarddev";
+    env[nr++] = libxl__strdup(gc, nic->coloft_forwarddev);
+    env[nr++] = "mode";
+    if (side == primary)
+        env[nr++] = "primary";
+    else
+        env[nr++] = "secondary";
+    env[nr++] = "index";
+    env[nr++] = GCSPRINTF("%d", cps->index);
+    env[nr++] = "bridge";
+    env[nr++] = libxl__strdup(gc, nic->bridge);
+    env[nr++] = NULL;
+    assert(nr == arraysize);
+
+    arraysize = 3; nr = 0;
+    GCNEW_ARRAY(args, arraysize);
+    args[nr++] = colo_proxy_script;
+    args[nr++] = op;
+    args[nr++] = NULL;
+    assert(nr == arraysize);
+
+    aes->ao = dev->cds->ao;
+    aes->what = GCSPRINTF("%s %s", args[0], args[1]);
+    aes->env = env;
+    aes->args = args;
+    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+    aes->stdfds[0] = -1;
+    aes->stdfds[1] = -1;
+    aes->stdfds[2] = -1;
+
+    if (!strcmp(op, "teardown"))
+        aes->callback = colo_save_teardown_script_cb;
+    else
+        aes->callback = colo_save_setup_script_cb;
+}
+
+/* ========== setup() and teardown() ========== */
+
+static void colo_nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
+                           libxl__colo_proxy_state *cps, int side,
+                           char *colo_proxy_script)
+{
+    int rc;
+    libxl__colo_device_nic *colo_nic;
+    const libxl_device_nic *nic = dev->backend_dev;
+
+    STATE_AO_GC(dev->cds->ao);
+
+    /*
+     * thers's no subkind of nic devices, so nic ops is always matched
+     * with nic devices, we begin to setup the nic device
+     */
+    dev->matched = 1;
+
+    if (!nic->coloft_forwarddev) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    GCNEW(colo_nic);
+    dev->concrete_data = colo_nic;
+    colo_nic->devid = nic->devid;
+    colo_nic->vif = get_vifname(dev, nic);
+    if (!colo_nic->vif) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    setup_async_exec(dev, "setup", cps, side, colo_proxy_script);
+    rc = libxl__async_exec_start(&dev->aodev.aes);
+    if (rc)
+        goto out;
+
+    return;
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void colo_save_setup_script_cb(libxl__egc *egc,
+                                      libxl__async_exec_state *aes,
+                                      int rc, int status)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+    libxl__colo_device_nic *colo_nic = dev->concrete_data;
+    libxl__checkpoint_devices_state *cds = dev->cds;
+    const char *out_path_base, *hotplug_error = NULL;
+
+    EGC_GC;
+
+    /* Convenience aliases */
+    const uint32_t domid = cds->domid;
+    const int devid = colo_nic->devid;
+    const char *const vif = colo_nic->vif;
+
+    if (status && !rc)
+        rc = ERROR_FAIL;
+    if (rc)
+        goto out;
+
+    out_path_base = GCSPRINTF("%s/colo_proxy/%d",
+                              libxl__xs_libxl_path(gc, domid), devid);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/hotplug-error", out_path_base),
+                                &hotplug_error);
+    if (rc)
+        goto out;
+
+    if (hotplug_error) {
+        LOGD(ERROR, domid, "colo_proxy script %s setup failed for vif %s: %s",
+            aes->args[0], vif, hotplug_error);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+static void colo_nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
+                              libxl__colo_proxy_state *cps, int side,
+                              char *colo_proxy_script)
+{
+    int rc;
+    libxl__colo_device_nic *colo_nic = dev->concrete_data;
+
+    if (!colo_nic || !colo_nic->vif) {
+        /* colo nic has not yet been set up, just return */
+        rc = 0;
+        goto out;
+    }
+
+    setup_async_exec(dev, "teardown", cps, side, colo_proxy_script);
+
+    rc = libxl__async_exec_start(&dev->aodev.aes);
+    if (rc)
+        goto out;
+
+    return;
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void colo_save_teardown_script_cb(libxl__egc *egc,
+                                         libxl__async_exec_state *aes,
+                                         int rc, int status)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+
+    if (status && !rc)
+        rc = ERROR_FAIL;
+    else
+        rc = 0;
+
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+/* ======== primary ======== */
+
+static void colo_nic_save_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    libxl__colo_save_state *css = dev->cds->concrete_data;
+
+    colo_nic_setup(egc, dev, &css->cps, primary, css->colo_proxy_script);
+}
+
+static void colo_nic_save_teardown(libxl__egc *egc,
+                                   libxl__checkpoint_device *dev)
+{
+    libxl__colo_save_state *css = dev->cds->concrete_data;
+
+    colo_nic_teardown(egc, dev, &css->cps, primary, css->colo_proxy_script);
+}
+
+const libxl__checkpoint_device_instance_ops colo_save_device_nic = {
+    .kind = LIBXL__DEVICE_KIND_VIF,
+    .setup = colo_nic_save_setup,
+    .teardown = colo_nic_save_teardown,
+};
+
+/* ======== secondary ======== */
+
+static void colo_nic_restore_setup(libxl__egc *egc,
+                                   libxl__checkpoint_device *dev)
+{
+    libxl__colo_restore_state *crs = dev->cds->concrete_data;
+
+    colo_nic_setup(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
+}
+
+static void colo_nic_restore_teardown(libxl__egc *egc,
+                                      libxl__checkpoint_device *dev)
+{
+    libxl__colo_restore_state *crs = dev->cds->concrete_data;
+
+    colo_nic_teardown(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
+}
+
+const libxl__checkpoint_device_instance_ops colo_restore_device_nic = {
+    .kind = LIBXL__DEVICE_KIND_VIF,
+    .setup = colo_nic_restore_setup,
+    .teardown = colo_nic_restore_teardown,
+};
diff --git a/tools/libs/light/libxl_colo_proxy.c b/tools/libs/light/libxl_colo_proxy.c
new file mode 100644 (file)
index 0000000..5475f7e
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <netlink/netlink.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* Consistent with the new COLO netlink channel in kernel side */
+#define NETLINK_COLO 28
+#define COLO_DEFAULT_WAIT_TIME 500000
+
+enum colo_netlink_op {
+    COLO_QUERY_CHECKPOINT = (NLMSG_MIN_TYPE + 1),
+    COLO_CHECKPOINT,
+    COLO_FAILOVER,
+    COLO_PROXY_INIT,
+    COLO_PROXY_RESET, /* UNUSED, will be used for continuous FT */
+};
+
+/* ========= colo-proxy: helper functions ========== */
+
+static int colo_proxy_send(libxl__colo_proxy_state *cps, uint8_t *buff,
+                           uint64_t size, int type)
+{
+    struct sockaddr_nl sa;
+    struct nlmsghdr msg;
+    struct iovec iov;
+    struct msghdr mh;
+    int ret;
+
+    STATE_AO_GC(cps->ao);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.nl_family = AF_NETLINK;
+    sa.nl_pid = 0;
+    sa.nl_groups = 0;
+
+    msg.nlmsg_len = NLMSG_SPACE(0);
+    msg.nlmsg_flags = NLM_F_REQUEST;
+    if (type == COLO_PROXY_INIT)
+        msg.nlmsg_flags |= NLM_F_ACK;
+    msg.nlmsg_seq = 0;
+    msg.nlmsg_pid = cps->index;
+    msg.nlmsg_type = type;
+
+    iov.iov_base = &msg;
+    iov.iov_len = msg.nlmsg_len;
+
+    mh.msg_name = &sa;
+    mh.msg_namelen = sizeof(sa);
+    mh.msg_iov = &iov;
+    mh.msg_iovlen = 1;
+    mh.msg_control = NULL;
+    mh.msg_controllen = 0;
+    mh.msg_flags = 0;
+
+    ret = sendmsg(cps->sock_fd, &mh, 0);
+    if (ret <= 0) {
+        LOGD(ERROR, ao->domid, "can't send msg to kernel by netlink: %s",
+            strerror(errno));
+    }
+
+    return ret;
+}
+
+static int colo_userspace_proxy_send(libxl__colo_proxy_state *cps,
+                                     uint8_t *buff,
+                                     uint32_t size)
+{
+    int ret = 0;
+    uint32_t len = 0;
+
+    len = htonl(size);
+    ret = send(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
+    if (ret != sizeof(len)) {
+        goto err;
+    }
+
+    ret = send(cps->sock_fd, (uint8_t *)buff, size, 0);
+    if (ret != size) {
+        goto err;
+    }
+
+err:
+    return ret;
+}
+
+static int colo_userspace_proxy_recv(libxl__colo_proxy_state *cps,
+                                     char *buff,
+                                     unsigned int timeout_us)
+{
+    struct timeval tv;
+    int ret;
+    uint32_t len = 0;
+    uint32_t size = 0;
+
+    STATE_AO_GC(cps->ao);
+
+    if (timeout_us) {
+        tv.tv_sec = timeout_us / 1000000;
+        tv.tv_usec = timeout_us % 1000000;
+        ret = setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv,
+                         sizeof(tv));
+        if (ret < 0) {
+            LOGD(ERROR, ao->domid,
+                 "colo_userspace_proxy_recv setsockopt error: %s",
+                 strerror(errno));
+        }
+    }
+
+    ret = recv(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
+    if (ret < 0) {
+        goto err;
+    }
+
+    size = ntohl(len);
+    ret = recv(cps->sock_fd, buff, size, 0);
+
+err:
+    return ret;
+}
+
+/* error: return -1, otherwise return 0 */
+static int64_t colo_proxy_recv(libxl__colo_proxy_state *cps, uint8_t **buff,
+                               unsigned int timeout_us)
+{
+    struct sockaddr_nl sa;
+    struct iovec iov;
+    struct msghdr mh = {
+        .msg_name = &sa,
+        .msg_namelen = sizeof(sa),
+        .msg_iov = &iov,
+        .msg_iovlen = 1,
+    };
+    struct timeval tv;
+    uint32_t size = 16384;
+    int64_t len = 0;
+    int ret;
+
+    STATE_AO_GC(cps->ao);
+    uint8_t *tmp = libxl__malloc(NOGC, size);
+
+    if (timeout_us) {
+        tv.tv_sec = timeout_us / 1000000;
+        tv.tv_usec = timeout_us % 1000000;
+        setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+    }
+
+    iov.iov_base = tmp;
+    iov.iov_len = size;
+next:
+    ret = recvmsg(cps->sock_fd, &mh, 0);
+    if (ret <= 0) {
+        if (errno != EAGAIN && errno != EWOULDBLOCK)
+            LOGED(ERROR, ao->domid, "can't recv msg from kernel by netlink");
+        goto err;
+    }
+
+    len += ret;
+    if (mh.msg_flags & MSG_TRUNC) {
+        size += 16384;
+        tmp = libxl__realloc(NOGC, tmp, size);
+        iov.iov_base = tmp + len;
+        iov.iov_len = size - len;
+        goto next;
+    }
+
+    *buff = tmp;
+    ret = len;
+    goto out;
+
+err:
+    free(tmp);
+    *buff = NULL;
+
+out:
+    if (timeout_us) {
+        tv.tv_sec = 0;
+        tv.tv_usec = 0;
+        setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+    }
+    return ret;
+}
+
+/* ========= colo-proxy: setup and teardown ========== */
+
+int colo_proxy_setup(libxl__colo_proxy_state *cps)
+{
+    int skfd = 0;
+    struct sockaddr_nl sa;
+    struct nlmsghdr *h;
+    int i = 1;
+    int ret = ERROR_FAIL;
+    uint8_t *buff = NULL;
+    int64_t size;
+
+    STATE_AO_GC(cps->ao);
+
+    /* If enable userspace proxy mode, we don't need setup kernel proxy */
+    if (cps->is_userspace_proxy) {
+        struct sockaddr_in addr;
+        int port;
+        char recvbuff[1024];
+        const char sendbuf[] = "COLO_USERSPACE_PROXY_INIT";
+
+        memset(&addr, 0, sizeof(addr));
+        port = atoi(cps->checkpoint_port);
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(port);
+        addr.sin_addr.s_addr = inet_addr(cps->checkpoint_host);
+
+        skfd = socket(AF_INET, SOCK_STREAM, 0);
+        if (skfd < 0) {
+            LOGD(ERROR, ao->domid, "can not create a TCP socket: %s",
+                 strerror(errno));
+            goto out;
+        }
+
+        cps->sock_fd = skfd;
+
+        if (connect(skfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+            LOGD(ERROR, ao->domid, "connect error");
+            goto out;
+        }
+
+        ret = colo_userspace_proxy_send(cps, (uint8_t *)sendbuf, strlen(sendbuf));
+        if (ret < 0)
+            goto out;
+
+        ret = colo_userspace_proxy_recv(cps, recvbuff, COLO_DEFAULT_WAIT_TIME);
+        if (ret < 0) {
+            LOGD(ERROR, ao->domid, "Can't recv msg from qemu colo-compare: %s",
+                 strerror(errno));
+            goto out;
+        }
+
+        return 0;
+    }
+
+    skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_COLO);
+    if (skfd < 0) {
+        LOGD(ERROR, ao->domid, "can not create a netlink socket: %s", strerror(errno));
+        goto out;
+    }
+    cps->sock_fd = skfd;
+    memset(&sa, 0, sizeof(sa));
+    sa.nl_family = AF_NETLINK;
+    sa.nl_groups = 0;
+retry:
+    sa.nl_pid = i++;
+
+    if (i > 10) {
+        LOGD(ERROR, ao->domid, "netlink bind error");
+        goto out;
+    }
+
+    ret = bind(skfd, (struct sockaddr *)&sa, sizeof(sa));
+    if (ret < 0 && errno == EADDRINUSE) {
+        LOGD(ERROR, ao->domid, "colo index %d has already in used", sa.nl_pid);
+        goto retry;
+    } else if (ret < 0) {
+        LOGD(ERROR, ao->domid, "netlink bind error");
+        goto out;
+    }
+
+    cps->index = sa.nl_pid;
+    ret = colo_proxy_send(cps, NULL, 0, COLO_PROXY_INIT);
+    if (ret < 0)
+        goto out;
+
+    /* receive ack */
+    size = colo_proxy_recv(cps, &buff, 500000);
+    if (size < 0) {
+        LOGD(ERROR, ao->domid, "Can't recv msg from kernel by netlink: %s",
+             strerror(errno));
+        goto out;
+    }
+
+    if (size) {
+        h = (struct nlmsghdr *)buff;
+        if (h->nlmsg_type == NLMSG_ERROR) {
+            /* ack's type is NLMSG_ERROR */
+            struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
+
+            if (size - sizeof(*h) < sizeof(*err)) {
+                LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
+                goto out;
+            }
+
+            if (err->error) {
+                LOGD(ERROR, ao->domid, "NLMSG_ERROR contains error %d", err->error);
+                goto out;
+            }
+        }
+    }
+
+    ret = 0;
+
+out:
+    free(buff);
+    if (ret) {
+        close(cps->sock_fd);
+        cps->sock_fd = -1;
+    }
+    return ret;
+}
+
+void colo_proxy_teardown(libxl__colo_proxy_state *cps)
+{
+    /*
+     * If enable userspace proxy mode,
+     * we don't need teardown kernel proxy
+     */
+    if (cps->is_userspace_proxy)
+        return;
+
+    if (cps->sock_fd >= 0) {
+        close(cps->sock_fd);
+        cps->sock_fd = -1;
+    }
+}
+
+/* ========= colo-proxy: preresume, postresume and checkpoint ========== */
+
+void colo_proxy_preresume(libxl__colo_proxy_state *cps)
+{
+    /*
+     * If enable userspace proxy mode,
+     * we don't need preresume kernel proxy
+     */
+    if (cps->is_userspace_proxy) {
+        const char sendbuf[] = "COLO_CHECKPOINT";
+        colo_userspace_proxy_send(cps,
+                                  (uint8_t *)sendbuf,
+                                  strlen(sendbuf));
+        return;
+    }
+
+    colo_proxy_send(cps, NULL, 0, COLO_CHECKPOINT);
+    /* TODO: need to handle if the call fails... */
+}
+
+void colo_proxy_postresume(libxl__colo_proxy_state *cps)
+{
+    /* nothing to do... */
+}
+
+typedef struct colo_msg {
+    bool is_checkpoint;
+} colo_msg;
+
+/*
+ * Return value:
+ * -1: error
+ *  0: no checkpoint event is received before timeout
+ *  1: do checkpoint
+ */
+int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
+                          unsigned int timeout_us)
+{
+    uint8_t *buff = NULL;
+    int64_t size;
+    struct nlmsghdr *h;
+    struct colo_msg *m;
+    int ret = -1;
+    char recvbuff[1024];
+
+    STATE_AO_GC(cps->ao);
+
+    /*
+     * Enable userspace proxy to periodical checkpoint mode,
+     * sleeping temporarily for colo userspace proxy mode.
+     * then we will use socket recv instead of this usleep.
+     * In other words, we use socket communicate with Qemu
+     * Proxy part(colo-compare), for example, notify checkpoint
+     * event.
+     */
+    if (cps->is_userspace_proxy) {
+        ret = colo_userspace_proxy_recv(cps, recvbuff, timeout_us);
+        if (ret <= 0) {
+            ret = 0;
+            goto out;
+        }
+
+        if (!strcmp(recvbuff, "DO_CHECKPOINT")) {
+            ret = 1;
+        } else {
+            LOGD(ERROR, ao->domid, "receive qemu colo-compare checkpoint error");
+            ret = 0;
+        }
+        goto out;
+    }
+
+    size = colo_proxy_recv(cps, &buff, timeout_us);
+
+    /* timeout, return no checkpoint message. */
+    if (size <= 0) {
+        ret = 0;
+        goto out;
+    }
+
+    h = (struct nlmsghdr *) buff;
+
+    if (h->nlmsg_type == NLMSG_ERROR) {
+        LOGD(ERROR, ao->domid, "receive NLMSG_ERROR");
+        goto out;
+    }
+
+    if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*m))) {
+        LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
+        goto out;
+    }
+
+    m = NLMSG_DATA(h);
+
+    ret = m->is_checkpoint ? 1 : 0;
+
+out:
+    free(buff);
+    return ret;
+}
diff --git a/tools/libs/light/libxl_colo_qdisk.c b/tools/libs/light/libxl_colo_qdisk.c
new file mode 100644 (file)
index 0000000..4c017ca
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/* ========== init() and cleanup() ========== */
+
+int init_subkind_qdisk(libxl__checkpoint_devices_state *cds)
+{
+    /*
+     * We don't know if we use qemu block replication, so
+     * we cannot start block replication here.
+     */
+    return 0;
+}
+
+void cleanup_subkind_qdisk(libxl__checkpoint_devices_state *cds)
+{
+}
+
+/* ========== setup() and teardown() ========== */
+
+static void colo_qdisk_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
+                             bool primary)
+{
+    const libxl_device_disk *disk = dev->backend_dev;
+    int ret, rc = 0;
+    libxl__colo_qdisk *colo_qdisk = NULL;
+    char port[32];
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = dev->cds;
+    const char *host = disk->colo_host;
+    const char *export_name = disk->colo_export;
+    const int domid = cds->domid;
+
+    STATE_AO_GC(dev->cds->ao);
+
+    if (disk->backend != LIBXL_DISK_BACKEND_QDISK ||
+        !libxl_defbool_val(disk->colo_enable) ||
+        !host || !export_name || (disk->colo_port <= 0) ||
+        !disk->active_disk || !disk->hidden_disk) {
+        rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
+        goto out;
+    }
+
+    dev->matched = true;
+
+    GCNEW(colo_qdisk);
+    dev->concrete_data = colo_qdisk;
+
+    if (primary) {
+        libxl__colo_save_state *css = cds->concrete_data;
+
+        css->qdisk_used = true;
+        /* NBD server is not ready, so we cannot start block replication now */
+        goto out;
+    } else {
+        libxl__colo_restore_state *crs = cds->concrete_data;
+        sprintf(port, "%d", disk->colo_port);
+
+        if (!crs->qdisk_used) {
+            /* start nbd server */
+            ret = libxl__qmp_nbd_server_start(gc, domid, host, port);
+            if (ret) {
+                rc = ERROR_FAIL;
+                goto out;
+            }
+            crs->host = host;
+            crs->port = port;
+        } else {
+            if (strcmp(crs->host, host) || strcmp(crs->port, port)) {
+                LOGD(ERROR, domid, "The host and port of all disks must be the same");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+
+        crs->qdisk_used = true;
+
+        ret = libxl__qmp_nbd_server_add(gc, domid, export_name);
+        if (ret)
+            rc = ERROR_FAIL;
+
+        colo_qdisk->setuped = true;
+    }
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void colo_qdisk_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
+                                bool primary)
+{
+    int ret, rc = 0;
+    const libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
+    const libxl_device_disk *disk = dev->backend_dev;
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = dev->cds;
+    const int domid = cds->domid;
+    const char *export_name = disk->colo_export;
+
+    EGC_GC;
+
+    if (primary) {
+        if (!colo_qdisk->setuped)
+            goto out;
+
+        /*
+         * There is no way to get the child name, but we know it is children.1
+         */
+        ret = libxl__qmp_x_blockdev_change(gc, domid, export_name,
+                                           "children.1", NULL);
+        if (ret)
+            rc = ERROR_FAIL;
+    } else {
+        libxl__colo_restore_state *crs = cds->concrete_data;
+
+        if (crs->qdisk_used) {
+            ret = libxl__qmp_nbd_server_stop(gc, domid);
+            if (ret)
+                rc = ERROR_FAIL;
+        }
+    }
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+/* ========== checkpointing APIs ========== */
+
+static void colo_qdisk_save_preresume(libxl__egc *egc,
+                                      libxl__checkpoint_device *dev)
+{
+    libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
+    const libxl_device_disk *disk = dev->backend_dev;
+    int ret, rc = 0;
+    char *node = NULL;
+    char *cmd = NULL;
+
+    /* Convenience aliases */
+    const int domid = dev->cds->domid;
+    const char *host = disk->colo_host;
+    int port = disk->colo_port;
+    const char *export_name = disk->colo_export;
+
+    EGC_GC;
+
+    if (colo_qdisk->setuped)
+        goto out;
+
+    /* qmp command doesn't support the driver "nbd" */
+    node = GCSPRINTF("colo_node%d",
+                     libxl__device_disk_dev_number(disk->vdev, NULL, NULL));
+    cmd = GCSPRINTF("drive_add -n buddy driver=replication,mode=primary,"
+                    "file.driver=nbd,file.host=%s,file.port=%d,"
+                    "file.export=%s,node-name=%s",
+                    host, port, export_name, node);
+    ret = libxl__qmp_hmp(gc, domid, cmd, NULL);
+    if (ret)
+        rc = ERROR_FAIL;
+
+    ret = libxl__qmp_x_blockdev_change(gc, domid, export_name, NULL, node);
+    if (ret)
+        rc = ERROR_FAIL;
+
+    colo_qdisk->setuped = true;
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+/* ======== primary ======== */
+
+static void colo_qdisk_save_setup(libxl__egc *egc,
+                                  libxl__checkpoint_device *dev)
+{
+    colo_qdisk_setup(egc, dev, true);
+}
+
+static void colo_qdisk_save_teardown(libxl__egc *egc,
+                                   libxl__checkpoint_device *dev)
+{
+    colo_qdisk_teardown(egc, dev, true);
+}
+
+const libxl__checkpoint_device_instance_ops colo_save_device_qdisk = {
+    .kind = LIBXL__DEVICE_KIND_VBD,
+    .setup = colo_qdisk_save_setup,
+    .teardown = colo_qdisk_save_teardown,
+    .preresume = colo_qdisk_save_preresume,
+};
+
+/* ======== secondary ======== */
+
+static void colo_qdisk_restore_setup(libxl__egc *egc,
+                                     libxl__checkpoint_device *dev)
+{
+    colo_qdisk_setup(egc, dev, false);
+}
+
+static void colo_qdisk_restore_teardown(libxl__egc *egc,
+                                      libxl__checkpoint_device *dev)
+{
+    colo_qdisk_teardown(egc, dev, false);
+}
+
+const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk = {
+    .kind = LIBXL__DEVICE_KIND_VBD,
+    .setup = colo_qdisk_restore_setup,
+    .teardown = colo_qdisk_restore_teardown,
+};
diff --git a/tools/libs/light/libxl_colo_restore.c b/tools/libs/light/libxl_colo_restore.c
new file mode 100644 (file)
index 0000000..aa36567
--- /dev/null
@@ -0,0 +1,1093 @@
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *         Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+#include "libxl_sr_stream_format.h"
+
+typedef struct libxl__colo_restore_checkpoint_state libxl__colo_restore_checkpoint_state;
+struct libxl__colo_restore_checkpoint_state {
+    libxl__domain_suspend_state dsps;
+    libxl__logdirty_switch lds;
+    libxl__colo_restore_state *crs;
+    libxl__stream_write_state sws;
+    int status;
+    bool preresume;
+    /* used for teardown */
+    int teardown_devices;
+    int saved_rc;
+    char *state_file;
+
+    void (*callback)(libxl__egc *,
+                     libxl__colo_restore_checkpoint_state *,
+                     int);
+};
+
+extern const libxl__checkpoint_device_instance_ops colo_restore_device_nic;
+extern const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk;
+
+static const libxl__checkpoint_device_instance_ops *colo_restore_ops[] = {
+    &colo_restore_device_nic,
+    &colo_restore_device_qdisk,
+    NULL,
+};
+
+/* ===================== colo: common functions ===================== */
+
+static void colo_enable_logdirty(libxl__colo_restore_state *crs, libxl__egc *egc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+    /* Convenience aliases */
+    const uint32_t domid = crs->domid;
+    libxl__logdirty_switch *const lds = &crcs->lds;
+
+    EGC_GC;
+
+    /* we need to know which pages are dirty to restore the guest */
+    if (xc_shadow_control(CTX->xch, domid,
+                          XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
+                          NULL, 0, NULL, 0, NULL) < 0) {
+        LOGD(ERROR, domid, "cannot enable secondary vm's logdirty");
+        lds->callback(egc, lds, ERROR_FAIL);
+        return;
+    }
+
+    if (crs->hvm) {
+        libxl__domain_common_switch_qemu_logdirty(egc, domid, 1, lds);
+        return;
+    }
+
+    lds->callback(egc, lds, 0);
+}
+
+static void colo_disable_logdirty(libxl__colo_restore_state *crs,
+                                  libxl__egc *egc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+    /* Convenience aliases */
+    const uint32_t domid = crs->domid;
+    libxl__logdirty_switch *const lds = &crcs->lds;
+
+    EGC_GC;
+
+    /* we need to know which pages are dirty to restore the guest */
+    if (xc_shadow_control(CTX->xch, domid, XEN_DOMCTL_SHADOW_OP_OFF,
+                          NULL, 0, NULL, 0, NULL) < 0)
+        LOGD(WARN, domid, "cannot disable secondary vm's logdirty");
+
+    if (crs->hvm) {
+        libxl__domain_common_switch_qemu_logdirty(egc, domid, 0, lds);
+        return;
+    }
+
+    lds->callback(egc, lds, 0);
+}
+
+static void colo_resume_vm(libxl__egc *egc,
+                           libxl__colo_restore_checkpoint_state *crcs,
+                           int restore_device_model)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+    int rc;
+
+    /* Convenience aliases */
+    libxl__colo_restore_state *const crs = crcs->crs;
+
+    EGC_GC;
+
+    if (!crs->saved_cb) {
+        /* TODO: sync mmu for hvm? */
+        if (restore_device_model) {
+            rc = libxl__qmp_restore(gc, crs->domid, crcs->state_file);
+            if (rc) {
+                LOGD(ERROR, crs->domid,
+                     "cannot restore device model for secondary vm");
+                crcs->callback(egc, crcs, rc);
+                return;
+            }
+        }
+        rc = libxl__domain_resume_deprecated(gc, crs->domid, 0);
+        if (rc)
+            LOGD(ERROR, crs->domid, "cannot resume secondary vm");
+
+        crcs->callback(egc, crcs, rc);
+        return;
+    }
+
+    libxl__xc_domain_restore_done(egc, dcs, 0, 0, 0);
+
+    return;
+}
+
+static int init_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+    /* init device subkind-specific state in the libxl ctx */
+    int rc;
+    STATE_AO_GC(cds->ao);
+
+    rc = init_subkind_colo_nic(cds);
+    if (rc) goto out;
+
+    rc = init_subkind_qdisk(cds);
+    if (rc) {
+        cleanup_subkind_colo_nic(cds);
+        goto out;
+    }
+
+    rc = 0;
+out:
+    return rc;
+}
+
+static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+    /* cleanup device subkind-specific state in the libxl ctx */
+    STATE_AO_GC(cds->ao);
+
+    cleanup_subkind_colo_nic(cds);
+    cleanup_subkind_qdisk(cds);
+}
+
+/* ================ colo: setup restore environment ================ */
+
+static void libxl__colo_domain_create_cb(libxl__egc *egc,
+                                         libxl__domain_create_state *dcs,
+                                         int rc, uint32_t domid);
+
+static int init_dsps(libxl__domain_suspend_state *dsps)
+{
+    int rc = ERROR_FAIL;
+    libxl_domain_type type;
+
+    STATE_AO_GC(dsps->ao);
+
+    libxl__xswait_init(&dsps->pvcontrol);
+    libxl__ev_evtchn_init(&dsps->guest_evtchn);
+    libxl__ev_xswatch_init(&dsps->guest_watch);
+    libxl__ev_time_init(&dsps->guest_timeout);
+
+    type = libxl__domain_type(gc, dsps->domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID)
+        goto out;
+
+    dsps->type = type;
+
+    dsps->guest_evtchn.port = -1;
+    dsps->guest_evtchn_lockfd = -1;
+    dsps->guest_responded = 0;
+    dsps->dm_savefile = libxl__device_model_savefile(gc, dsps->domid);
+
+    /* Secondary vm is not created, so we cannot get evtchn port */
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/*
+ * checkpoint callbacks are called in the following order:
+ * 1. resume
+ * 2. wait checkpoint
+ * 3. suspend
+ * 4. checkpoint
+ */
+static void libxl__colo_restore_domain_resume_callback(void *data);
+static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data);
+static void libxl__colo_restore_domain_suspend_callback(void *data);
+static void libxl__colo_restore_domain_checkpoint_callback(void *data);
+
+void libxl__colo_restore_setup(libxl__egc *egc,
+                               libxl__colo_restore_state *crs)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs;
+    int rc = ERROR_FAIL;
+
+    /* Convenience aliases */
+    libxl__srm_restore_autogen_callbacks *const callbacks =
+        &dcs->srs.shs.callbacks.restore.a;
+    const int domid = crs->domid;
+
+    STATE_AO_GC(crs->ao);
+
+    GCNEW(crcs);
+    crs->crcs = crcs;
+    crcs->crs = crs;
+    crs->qdisk_setuped = false;
+    crs->qdisk_used = false;
+    if (dcs->colo_proxy_script)
+        crs->colo_proxy_script = libxl__strdup(gc, dcs->colo_proxy_script);
+    else
+        crs->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
+                                           libxl__xen_script_dir_path());
+
+    /* setup dsps */
+    crcs->dsps.ao = ao;
+    crcs->dsps.domid = domid;
+    if (init_dsps(&crcs->dsps))
+        goto out;
+
+    callbacks->postcopy = libxl__colo_restore_domain_resume_callback;
+    callbacks->wait_checkpoint = libxl__colo_restore_domain_wait_checkpoint_callback;
+    callbacks->suspend = libxl__colo_restore_domain_suspend_callback;
+    callbacks->checkpoint = libxl__colo_restore_domain_checkpoint_callback;
+
+    /*
+     * Secondary vm is running in colo mode, so we need to call
+     * libxl__xc_domain_restore_done() to create secondary vm.
+     * But we will exit in domain_create_cb(). So replace the
+     * callback here.
+     */
+    crs->saved_cb = dcs->callback;
+    dcs->callback = libxl__colo_domain_create_cb;
+    crcs->state_file = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
+    crcs->status = LIBXL_COLO_SETUPED;
+
+    libxl__logdirty_init(&crcs->lds);
+    crcs->lds.ao = ao;
+
+    crcs->sws.fd = crs->send_back_fd;
+    crcs->sws.ao = ao;
+    crcs->sws.back_channel = true;
+
+    dcs->cds.concrete_data = crs;
+
+    libxl__stream_write_start(egc, &crcs->sws);
+
+    rc = 0;
+
+out:
+    crs->callback(egc, crs, rc);
+    return;
+}
+
+static void libxl__colo_domain_create_cb(libxl__egc *egc,
+                                         libxl__domain_create_state *dcs,
+                                         int rc, uint32_t domid)
+{
+    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+    crcs->callback(egc, crcs, rc);
+}
+
+/* ================ colo: teardown restore environment ================ */
+
+static void colo_restore_teardown_devices_done(libxl__egc *egc,
+    libxl__checkpoint_devices_state *cds, int rc);
+static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs);
+static void do_failover_done(libxl__egc *egc,
+                             libxl__colo_restore_checkpoint_state* crcs,
+                             int rc);
+static void colo_disable_logdirty_done(libxl__egc *egc,
+                                       libxl__logdirty_switch *lds,
+                                       int rc);
+static void libxl__colo_restore_teardown_done(libxl__egc *egc,
+                                              libxl__colo_restore_state *crs,
+                                              int rc);
+
+void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
+                                  int ret, int retval, int errnoval)
+{
+    libxl__domain_create_state *dcs = dcs_void;
+    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+    int rc = 1;
+
+    /* convenience aliases */
+    libxl__colo_restore_state *const crs = &dcs->crs;
+    EGC_GC;
+
+    if (ret == 0 && retval == 0)
+        rc = 0;
+
+    LOGD(INFO, crs->domid, "%s", rc ? "colo fails" : "failover");
+
+    libxl__stream_write_abort(egc, &crcs->sws, 1);
+    if (crs->saved_cb) {
+        /* crcs->status is LIBXL_COLO_SETUPED */
+        dcs->srs.completion_callback = NULL;
+    }
+    libxl__xc_domain_restore_done(egc, dcs, ret, retval, errnoval);
+
+    if (crs->qdisk_setuped) {
+        libxl__qmp_stop_replication(gc, crs->domid, false);
+        crs->qdisk_setuped = false;
+    }
+
+    crcs->saved_rc = rc;
+    if (!crcs->teardown_devices) {
+        colo_restore_teardown_devices_done(egc, &dcs->cds, 0);
+        return;
+    }
+
+    dcs->cds.callback = colo_restore_teardown_devices_done;
+    libxl__checkpoint_devices_teardown(egc, &dcs->cds);
+}
+
+static void colo_restore_teardown_devices_done(libxl__egc *egc,
+    libxl__checkpoint_devices_state *cds, int rc)
+{
+    libxl__colo_restore_state *crs = cds->concrete_data;
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+
+    EGC_GC;
+
+    if (rc)
+        LOGD(ERROR, cds->domid, "COLO: failed to teardown device for guest,"
+            " rc %d", rc);
+
+    if (crcs->teardown_devices)
+        cleanup_device_subkind(cds);
+
+    colo_proxy_teardown(&crs->cps);
+
+    rc = crcs->saved_rc;
+    if (!rc) {
+        crcs->callback = do_failover_done;
+        do_failover(egc, crs);
+        return;
+    }
+
+    libxl__colo_restore_teardown_done(egc, crs, rc);
+}
+
+static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs)
+{
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+    /* Convenience aliases */
+    const int status = crcs->status;
+    libxl__logdirty_switch *const lds = &crcs->lds;
+
+    EGC_GC;
+
+    switch(status) {
+    case LIBXL_COLO_SETUPED:
+        /*
+         * We will come here only when reading emulator xenstore data or
+         * emulator context fails, and libxl__xc_domain_restore_done()
+         * is not called. In this case, the migration is not finished,
+         * so we cannot do failover.
+         */
+        LOGD(ERROR, crs->domid, "migration fails");
+        crcs->callback(egc, crcs, ERROR_FAIL);
+        return;
+    case LIBXL_COLO_SUSPENDED:
+    case LIBXL_COLO_RESUMED:
+        /* disable logdirty first */
+        lds->callback = colo_disable_logdirty_done;
+        colo_disable_logdirty(crs, egc);
+        return;
+    default:
+        LOGD(ERROR, crs->domid, "invalid status: %d", status);
+        crcs->callback(egc, crcs, ERROR_FAIL);
+    }
+}
+
+static void do_failover_done(libxl__egc *egc,
+                             libxl__colo_restore_checkpoint_state* crcs,
+                             int rc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+    /* Convenience aliases */
+    libxl__colo_restore_state *const crs = crcs->crs;
+
+    EGC_GC;
+
+    if (rc)
+        LOGD(ERROR, crs->domid, "cannot do failover");
+
+    libxl__colo_restore_teardown_done(egc, crs, rc);
+}
+
+static void colo_disable_logdirty_done(libxl__egc *egc,
+                                       libxl__logdirty_switch *lds,
+                                       int rc)
+{
+    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+
+    EGC_GC;
+
+    if (rc)
+        LOGD(WARN, crcs->crs->domid, "cannot disable logdirty");
+
+    if (crcs->status == LIBXL_COLO_SUSPENDED) {
+        /*
+         * failover when reading state from master, so no need to
+         * call libxl__qmp_restore().
+         */
+        colo_resume_vm(egc, crcs, 0);
+        return;
+    }
+
+    /* If we cannot disable logdirty, we still can do failover */
+    crcs->callback(egc, crcs, 0);
+}
+
+static void libxl__colo_restore_teardown_done(libxl__egc *egc,
+                                              libxl__colo_restore_state *crs,
+                                              int rc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    EGC_GC;
+
+    /* convenience aliases */
+    const int domid = crs->domid;
+    const libxl_ctx *const ctx = libxl__gc_owner(gc);
+    xc_interface *const xch = ctx->xch;
+
+    if (!rc)
+        /* failover, no need to destroy the secondary vm */
+        goto out;
+
+    xc_domain_destroy(xch, domid);
+
+out:
+    if (crs->saved_cb) {
+        dcs->callback = crs->saved_cb;
+        crs->saved_cb = NULL;
+    }
+
+    dcs->callback(egc, dcs, rc, crs->domid);
+}
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+                                          libxl__stream_write_state *stream,
+                                          int rc);
+static void colo_common_read_stream_done(libxl__egc *egc,
+                                         libxl__stream_read_state *stream,
+                                         int rc);
+
+/* ======================== colo: checkpoint ======================= */
+
+/*
+ * Do the following things when resuming secondary vm:
+ *  1. read emulator xenstore data
+ *  2. read emulator context
+ *  3. REC_TYPE_CHECKPOINT_END
+ */
+static void libxl__colo_restore_domain_checkpoint_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+    crcs->callback = NULL;
+    dcs->srs.checkpoint_callback = colo_common_read_stream_done;
+    libxl__stream_read_start_checkpoint(shs->egc, &dcs->srs);
+}
+
+/* ===================== colo: resume secondary vm ===================== */
+
+/*
+ * Do the following things when resuming secondary vm the first time:
+ *  1. resume secondary vm
+ *  2. enable log dirty
+ *  3. setup checkpoint devices
+ *  4. write CHECKPOINT_SVM_READY
+ *  5. unpause secondary vm
+ *  6. write CHECKPOINT_SVM_RESUMED
+ *
+ * Do the following things when resuming secondary vm:
+ *  1. write CHECKPOINT_SVM_READY
+ *  2. resume secondary vm
+ *  3. write CHECKPOINT_SVM_RESUMED
+ */
+static void colo_send_svm_ready(libxl__egc *egc,
+                                libxl__colo_restore_checkpoint_state *crcs);
+static void colo_send_svm_ready_done(libxl__egc *egc,
+                                     libxl__colo_restore_checkpoint_state *crcs,
+                                     int rc);
+static void colo_restore_preresume_cb(libxl__egc *egc,
+                                      libxl__checkpoint_devices_state *cds,
+                                      int rc);
+static void colo_restore_resume_vm(libxl__egc *egc,
+                                   libxl__colo_restore_checkpoint_state *crcs);
+static void colo_resume_vm_done(libxl__egc *egc,
+                                libxl__colo_restore_checkpoint_state *crcs,
+                                int rc);
+static void colo_write_svm_resumed(libxl__egc *egc,
+                                   libxl__colo_restore_checkpoint_state *crcs);
+static void colo_enable_logdirty_done(libxl__egc *egc,
+                                      libxl__logdirty_switch *lds,
+                                      int retval);
+static void colo_reenable_logdirty(libxl__egc *egc,
+                                   libxl__logdirty_switch *lds,
+                                   int rc);
+static void colo_reenable_logdirty_done(libxl__egc *egc,
+                                        libxl__logdirty_switch *lds,
+                                        int rc);
+static void colo_setup_checkpoint_devices(libxl__egc *egc,
+                                          libxl__colo_restore_state *crs);
+static void colo_restore_setup_cds_done(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds,
+                                        int rc);
+static void colo_unpause_svm(libxl__egc *egc,
+                             libxl__colo_restore_checkpoint_state *crcs);
+
+static void libxl__colo_restore_domain_resume_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+    if (crcs->teardown_devices)
+        colo_send_svm_ready(shs->egc, crcs);
+    else
+        colo_restore_resume_vm(shs->egc, crcs);
+}
+
+static void colo_send_svm_ready(libxl__egc *egc,
+                               libxl__colo_restore_checkpoint_state *crcs)
+{
+    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_READY };
+
+    crcs->callback = colo_send_svm_ready_done;
+    crcs->sws.checkpoint_callback = colo_common_write_stream_done;
+    libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
+}
+
+static void colo_send_svm_ready_done(libxl__egc *egc,
+                                     libxl__colo_restore_checkpoint_state *crcs,
+                                     int rc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *cds = &dcs->cds;
+
+    if (!crcs->preresume) {
+        crcs->preresume = true;
+        colo_unpause_svm(egc, crcs);
+        return;
+    }
+
+    cds->callback = colo_restore_preresume_cb;
+    libxl__checkpoint_devices_preresume(egc, cds);
+}
+
+static void colo_restore_preresume_cb(libxl__egc *egc,
+                                      libxl__checkpoint_devices_state *cds,
+                                      int rc)
+{
+    libxl__colo_restore_state *crs = cds->concrete_data;
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+    /* Convenience aliases */
+    libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, crs->domid, "preresume fails");
+        goto out;
+    }
+
+    if (crs->qdisk_setuped) {
+        if (libxl__qmp_colo_do_checkpoint(gc, crs->domid)) {
+            LOGD(ERROR, crs->domid, "doing checkpoint fails");
+            goto out;
+        }
+    }
+
+    if (!crs->cps.is_userspace_proxy)
+        colo_proxy_preresume(&crs->cps);
+
+    colo_restore_resume_vm(egc, crcs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_restore_resume_vm(libxl__egc *egc,
+                                   libxl__colo_restore_checkpoint_state *crcs)
+{
+
+    crcs->callback = colo_resume_vm_done;
+    colo_resume_vm(egc, crcs, 1);
+}
+
+static void colo_resume_vm_done(libxl__egc *egc,
+                                libxl__colo_restore_checkpoint_state *crcs,
+                                int rc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+    /* Convenience aliases */
+    libxl__colo_restore_state *const crs = crcs->crs;
+    libxl__logdirty_switch *const lds = &crcs->lds;
+    libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, crs->domid, "cannot resume secondary vm");
+        goto out;
+    }
+
+    crcs->status = LIBXL_COLO_RESUMED;
+
+    colo_proxy_postresume(&crs->cps);
+
+    /* avoid calling stream->completion_callback() more than once */
+    if (crs->saved_cb) {
+        dcs->callback = crs->saved_cb;
+        crs->saved_cb = NULL;
+
+        dcs->srs.completion_callback = NULL;
+
+        lds->callback = colo_enable_logdirty_done;
+        colo_enable_logdirty(crs, egc);
+        return;
+    }
+
+    colo_write_svm_resumed(egc, crcs);
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_write_svm_resumed(libxl__egc *egc,
+                                   libxl__colo_restore_checkpoint_state *crcs)
+{
+    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_RESUMED };
+
+    crcs->callback = NULL;
+    crcs->sws.checkpoint_callback = colo_common_write_stream_done;
+    libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
+}
+
+static void colo_enable_logdirty_done(libxl__egc *egc,
+                                      libxl__logdirty_switch *lds,
+                                      int rc)
+{
+    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+
+    /* Convenience aliases */
+    libxl__colo_restore_state *const crs = crcs->crs;
+
+    EGC_GC;
+
+    if (rc) {
+        /*
+         * log-dirty already enabled? There's no test op,
+         * so attempt to disable then reenable it
+         */
+        lds->callback = colo_reenable_logdirty;
+        colo_disable_logdirty(crs, egc);
+        return;
+    }
+
+    colo_setup_checkpoint_devices(egc, crs);
+}
+
+static void colo_reenable_logdirty(libxl__egc *egc,
+                                   libxl__logdirty_switch *lds,
+                                   int rc)
+{
+    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+    /* Convenience aliases */
+    libxl__colo_restore_state *const crs = crcs->crs;
+    libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, crs->domid, "cannot enable logdirty");
+        goto out;
+    }
+
+    lds->callback = colo_reenable_logdirty_done;
+    colo_enable_logdirty(crs, egc);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_reenable_logdirty_done(libxl__egc *egc,
+                                        libxl__logdirty_switch *lds,
+                                        int rc)
+{
+    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+
+    /* Convenience aliases */
+    libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, crcs->crs->domid, "cannot enable logdirty");
+        goto out;
+    }
+
+    colo_setup_checkpoint_devices(egc, crcs->crs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+/*
+ * We cannot setup checkpoint devices in libxl__colo_restore_setup(),
+ * because the guest is not ready.
+ */
+static void colo_setup_checkpoint_devices(libxl__egc *egc,
+                                          libxl__colo_restore_state *crs)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *cds = &dcs->cds;
+    libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+    STATE_AO_GC(crs->ao);
+
+    if (crs->cps.is_userspace_proxy)
+        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
+    else
+        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
+                                 (1 << LIBXL__DEVICE_KIND_VBD);
+
+    cds->callback = colo_restore_setup_cds_done;
+    cds->ao = ao;
+    cds->domid = crs->domid;
+    cds->ops = colo_restore_ops;
+
+    crs->cps.ao = ao;
+    if (!crs->cps.is_userspace_proxy) {
+        if (colo_proxy_setup(&crs->cps)) {
+            LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
+            goto out;
+        }
+    }
+
+    if (init_device_subkind(cds))
+        goto out;
+
+    crcs->teardown_devices = 1;
+
+    libxl__checkpoint_devices_setup(egc, cds);
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_restore_setup_cds_done(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds,
+                                        int rc)
+{
+    libxl__colo_restore_state *crs = cds->concrete_data;
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+    /* Convenience aliases */
+    libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, cds->domid, "COLO: failed to setup device for guest");
+        goto out;
+    }
+
+    if (crs->qdisk_used && !crs->qdisk_setuped) {
+        if (libxl__qmp_start_replication(gc, crs->domid, false)) {
+            LOGD(ERROR, cds->domid, "starting replication fails");
+            goto out;
+        }
+        crs->qdisk_setuped = true;
+    }
+
+    colo_send_svm_ready(egc, crcs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+static void colo_unpause_svm(libxl__egc *egc,
+                             libxl__colo_restore_checkpoint_state *crcs)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+    int rc;
+
+    /* Convenience aliases */
+    const uint32_t domid = crcs->crs->domid;
+    libxl__save_helper_state *const shs = &dcs->srs.shs;
+
+    EGC_GC;
+
+    /* We have enabled secondary vm's logdirty, so we can unpause it now */
+    rc = libxl__domain_unpause_deprecated(gc, domid);
+    if (rc) {
+        LOGD(ERROR, domid, "cannot unpause secondary vm");
+        goto out;
+    }
+
+    colo_write_svm_resumed(egc, crcs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
+}
+
+/* ===================== colo: wait new checkpoint ===================== */
+
+static void colo_restore_commit_cb(libxl__egc *egc,
+                                   libxl__checkpoint_devices_state *cds,
+                                   int rc);
+static void colo_stream_read_done(libxl__egc *egc,
+                                  libxl__colo_restore_checkpoint_state *crcs,
+                                  int real_size);
+
+static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *cds = &dcs->cds;
+
+    cds->callback = colo_restore_commit_cb;
+    libxl__checkpoint_devices_commit(shs->egc, cds);
+}
+
+static void colo_restore_commit_cb(libxl__egc *egc,
+                                   libxl__checkpoint_devices_state *cds,
+                                   int rc)
+{
+    libxl__colo_restore_state *crs = cds->concrete_data;
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, crs->domid, "commit fails");
+        goto out;
+    }
+
+    crcs->callback = colo_stream_read_done;
+    dcs->srs.checkpoint_callback = colo_common_read_stream_done;
+    libxl__stream_read_checkpoint_state(egc, &dcs->srs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
+}
+
+static void colo_stream_read_done(libxl__egc *egc,
+                                  libxl__colo_restore_checkpoint_state *crcs,
+                                  int id)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+    int ok = 0;
+
+    EGC_GC;
+
+    if (id != CHECKPOINT_NEW) {
+        LOGD(ERROR, crcs->crs->domid, "invalid section: %d", id);
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
+}
+
+/* ===================== colo: suspend secondary vm ===================== */
+
+/*
+ * Do the following things when resuming secondary vm:
+ *  1. suspend secondary vm
+ *  2. send CHECKPOINT_SVM_SUSPENDED
+ */
+static void colo_suspend_vm_done(libxl__egc *egc,
+                                 libxl__domain_suspend_state *dsps,
+                                 int ok);
+static void colo_restore_postsuspend_cb(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds,
+                                        int rc);
+
+static void libxl__colo_restore_domain_suspend_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
+    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
+    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+
+    STATE_AO_GC(dcs->ao);
+
+    /* Convenience aliases */
+    libxl__domain_suspend_state *const dsps = &crcs->dsps;
+
+    /* suspend secondary vm */
+    dsps->callback_common_done = colo_suspend_vm_done;
+
+    libxl__domain_suspend(shs->egc, dsps);
+}
+
+static void colo_suspend_vm_done(libxl__egc *egc,
+                                 libxl__domain_suspend_state *dsps,
+                                 int rc)
+{
+    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(dsps, *crcs, dsps);
+    libxl__colo_restore_state *crs = crcs->crs;
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *cds = &dcs->cds;
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, crs->domid, "cannot suspend secondary vm");
+        goto out;
+    }
+
+    crcs->status = LIBXL_COLO_SUSPENDED;
+
+    if (libxl__qmp_query_xen_replication_status(gc, crs->domid)) {
+        LOGD(ERROR, crs->domid, "replication error occurs when secondary vm is running");
+        goto out;
+    }
+
+    cds->callback = colo_restore_postsuspend_cb;
+    libxl__checkpoint_devices_postsuspend(egc, cds);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
+}
+
+static void colo_restore_postsuspend_cb(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds,
+                                        int rc)
+{
+    libxl__colo_restore_state *crs = cds->concrete_data;
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
+    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_SUSPENDED };
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, crs->domid, "postsuspend fails");
+        goto out;
+    }
+
+    crcs->callback = NULL;
+    crcs->sws.checkpoint_callback = colo_common_write_stream_done;
+    libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, !rc);
+}
+
+/* ===================== colo: common callback ===================== */
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+                                          libxl__stream_write_state *stream,
+                                          int rc)
+{
+    libxl__colo_restore_checkpoint_state *crcs =
+        CONTAINER_OF(stream, *crcs, sws);
+    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
+    int ok;
+
+    EGC_GC;
+
+    if (rc < 0) {
+        /* TODO: it may be a internal error, but we don't know */
+        LOGD(ERROR, crcs->crs->domid, "sending data fails");
+        ok = 2;
+        goto out;
+    }
+
+    if (!crcs->callback) {
+        /* Everythins is OK */
+        ok = 1;
+        goto out;
+    }
+
+    crcs->callback(egc, crcs, 0);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
+}
+
+static void colo_common_read_stream_done(libxl__egc *egc,
+                                         libxl__stream_read_state *stream,
+                                         int rc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(stream, *dcs, srs);
+    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
+    int ok;
+
+    EGC_GC;
+
+    if (rc < 0) {
+        /* TODO: it may be a internal error, but we don't know */
+        LOGD(ERROR, crcs->crs->domid, "reading data fails");
+        ok = 2;
+        goto out;
+    }
+
+    if (!crcs->callback) {
+        /* Everythins is OK */
+        ok = 1;
+        goto out;
+    }
+
+    /* rc contains the id */
+    crcs->callback(egc, crcs, rc);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
+}
diff --git a/tools/libs/light/libxl_colo_save.c b/tools/libs/light/libxl_colo_save.c
new file mode 100644 (file)
index 0000000..b47f038
--- /dev/null
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2016 FUJITSU LIMITED
+ * Author: Wen Congyang <wency@cn.fujitsu.com>
+ *         Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+extern const libxl__checkpoint_device_instance_ops colo_save_device_nic;
+extern const libxl__checkpoint_device_instance_ops colo_save_device_qdisk;
+
+static const libxl__checkpoint_device_instance_ops *colo_ops[] = {
+    &colo_save_device_nic,
+    &colo_save_device_qdisk,
+    NULL,
+};
+
+/* ================= helper functions ================= */
+
+static int init_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+    /* init device subkind-specific state in the libxl ctx */
+    int rc;
+    STATE_AO_GC(cds->ao);
+
+    rc = init_subkind_colo_nic(cds);
+    if (rc) goto out;
+
+    rc = init_subkind_qdisk(cds);
+    if (rc) {
+        cleanup_subkind_colo_nic(cds);
+        goto out;
+    }
+
+    rc = 0;
+out:
+    return rc;
+}
+
+static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+    /* cleanup device subkind-specific state in the libxl ctx */
+    STATE_AO_GC(cds->ao);
+
+    cleanup_subkind_colo_nic(cds);
+    cleanup_subkind_qdisk(cds);
+}
+
+/* ================= colo: setup save environment ================= */
+
+static void colo_save_setup_done(libxl__egc *egc,
+                                 libxl__checkpoint_devices_state *cds,
+                                 int rc);
+static void colo_save_setup_failed(libxl__egc *egc,
+                                   libxl__checkpoint_devices_state *cds,
+                                   int rc);
+/*
+ * checkpoint callbacks are called in the following order:
+ * 1. suspend
+ * 2. checkpoint
+ * 3. resume
+ * 4. wait checkpoint
+ */
+static void libxl__colo_save_domain_suspend_callback(void *data);
+static void libxl__colo_save_domain_checkpoint_callback(void *data);
+static void libxl__colo_save_domain_resume_callback(void *data);
+static void libxl__colo_save_domain_wait_checkpoint_callback(void *data);
+
+void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+    libxl__srm_save_autogen_callbacks *const callbacks =
+        &dss->sws.shs.callbacks.save.a;
+
+    STATE_AO_GC(dss->ao);
+
+    if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
+        LOGD(ERROR, dss->domid, "COLO only supports hvm now");
+        goto out;
+    }
+
+    css->send_fd = dss->fd;
+    css->recv_fd = dss->recv_fd;
+    css->svm_running = false;
+    css->paused = true;
+    css->qdisk_setuped = false;
+    css->qdisk_used = false;
+    libxl__ev_child_init(&css->child);
+    css->cps.is_userspace_proxy =
+        libxl_defbool_val(dss->remus->userspace_colo_proxy);
+
+    if (dss->remus->netbufscript)
+        css->colo_proxy_script = libxl__strdup(gc, dss->remus->netbufscript);
+    else
+        css->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
+                                           libxl__xen_script_dir_path());
+
+    cds->ops = colo_ops;
+    cds->callback = colo_save_setup_done;
+    cds->ao = ao;
+    cds->domid = dss->domid;
+    cds->concrete_data = css;
+
+    /* If enable userspace proxy mode, we don't need VIF */
+    if (css->cps.is_userspace_proxy) {
+        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
+
+        /* Use this args we can connect to qemu colo-compare */
+        cds->nics = libxl__device_list(gc, &libxl__nic_devtype,
+                                       cds->domid, &cds->num_nics);
+        if (cds->num_nics > 0) {
+            css->cps.checkpoint_host = cds->nics[0].colo_checkpoint_host;
+            css->cps.checkpoint_port = cds->nics[0].colo_checkpoint_port;
+        }
+    } else {
+        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
+                                 (1 << LIBXL__DEVICE_KIND_VBD);
+    }
+
+    css->srs.ao = ao;
+    css->srs.fd = css->recv_fd;
+    css->srs.back_channel = true;
+    libxl__stream_read_start(egc, &css->srs);
+    css->cps.ao = ao;
+    if (colo_proxy_setup(&css->cps)) {
+        LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
+        goto out;
+    }
+
+    if (init_device_subkind(cds))
+        goto out;
+
+    callbacks->suspend = libxl__colo_save_domain_suspend_callback;
+    callbacks->checkpoint = libxl__colo_save_domain_checkpoint_callback;
+    callbacks->postcopy = libxl__colo_save_domain_resume_callback;
+    callbacks->wait_checkpoint = libxl__colo_save_domain_wait_checkpoint_callback;
+
+    libxl__checkpoint_devices_setup(egc, &dss->cds);
+
+    return;
+
+out:
+    dss->callback(egc, dss, ERROR_FAIL);
+}
+
+static void colo_save_setup_done(libxl__egc *egc,
+                                 libxl__checkpoint_devices_state *cds,
+                                 int rc)
+{
+    libxl__colo_save_state *css = cds->concrete_data;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+    EGC_GC;
+
+    if (!rc) {
+        libxl__domain_save(egc, dss);
+        return;
+    }
+
+    LOGD(ERROR, dss->domid, "COLO: failed to setup device for guest");
+    cds->callback = colo_save_setup_failed;
+    libxl__checkpoint_devices_teardown(egc, cds);
+}
+
+static void colo_save_setup_failed(libxl__egc *egc,
+                                   libxl__checkpoint_devices_state *cds,
+                                   int rc)
+{
+    libxl__colo_save_state *css = cds->concrete_data;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+    STATE_AO_GC(cds->ao);
+
+    if (rc)
+        LOGD(ERROR, cds->domid,
+             "COLO: failed to teardown device after setup failed"
+             " for guest, rc %d", rc);
+
+    cleanup_device_subkind(cds);
+    dss->callback(egc, dss, rc);
+}
+
+/* ================= colo: teardown save environment ================= */
+
+static void colo_teardown_done(libxl__egc *egc,
+                               libxl__checkpoint_devices_state *cds,
+                               int rc);
+
+void libxl__colo_save_teardown(libxl__egc *egc,
+                               libxl__colo_save_state *css,
+                               int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    LOGD(WARN, dss->domid,
+         "COLO: Domain suspend terminated with rc %d,"
+         " teardown COLO devices...", rc);
+
+    libxl__stream_read_abort(egc, &css->srs, 1);
+
+    if (css->qdisk_setuped) {
+        libxl__qmp_stop_replication(gc, dss->domid, true);
+        css->qdisk_setuped = false;
+    }
+
+    dss->cds.callback = colo_teardown_done;
+    libxl__checkpoint_devices_teardown(egc, &dss->cds);
+    return;
+}
+
+static void colo_teardown_done(libxl__egc *egc,
+                               libxl__checkpoint_devices_state *cds,
+                               int rc)
+{
+    libxl__colo_save_state *css = cds->concrete_data;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    cleanup_device_subkind(cds);
+    colo_proxy_teardown(&css->cps);
+    dss->callback(egc, dss, rc);
+}
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+                                          libxl__stream_write_state *stream,
+                                          int rc);
+static void colo_common_read_stream_done(libxl__egc *egc,
+                                         libxl__stream_read_state *stream,
+                                         int rc);
+
+/* ===================== colo: suspend primary vm ===================== */
+
+static void colo_read_svm_suspended_done(libxl__egc *egc,
+                                         libxl__colo_save_state *css,
+                                         int id);
+/*
+ * Do the following things when suspending primary vm:
+ * 1. suspend primary vm
+ * 2. do postsuspend
+ * 3. read CHECKPOINT_SVM_SUSPENDED
+ * 4. read secondary vm's dirty pages
+ */
+static void colo_suspend_primary_vm_done(libxl__egc *egc,
+                                         libxl__domain_suspend_state *dsps,
+                                         int ok);
+static void colo_postsuspend_cb(libxl__egc *egc,
+                                libxl__checkpoint_devices_state *cds,
+                                int rc);
+
+static void libxl__colo_save_domain_suspend_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__egc *egc = shs->egc;
+    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+    libxl__domain_save_state *dss = sws->dss;
+
+    /* Convenience aliases */
+    libxl__domain_suspend_state *dsps = &dss->dsps;
+
+    dsps->callback_common_done = colo_suspend_primary_vm_done;
+    libxl__domain_suspend(egc, dsps);
+}
+
+static void colo_suspend_primary_vm_done(libxl__egc *egc,
+                                         libxl__domain_suspend_state *dsps,
+                                         int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, dss->domid, "cannot suspend primary vm");
+        goto out;
+    }
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+    cds->callback = colo_postsuspend_cb;
+    libxl__checkpoint_devices_postsuspend(egc, cds);
+    return;
+
+out:
+    dss->rc = rc;
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void colo_postsuspend_cb(libxl__egc *egc,
+                                libxl__checkpoint_devices_state *cds,
+                                int rc)
+{
+    libxl__colo_save_state *css = cds->concrete_data;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, dss->domid, "postsuspend fails");
+        goto out;
+    }
+
+    if (!css->svm_running) {
+        rc = 0;
+        goto out;
+    }
+
+    /*
+     * read CHECKPOINT_SVM_SUSPENDED
+     */
+    css->callback = colo_read_svm_suspended_done;
+    css->srs.checkpoint_callback = colo_common_read_stream_done;
+    libxl__stream_read_checkpoint_state(egc, &css->srs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void colo_read_svm_suspended_done(libxl__egc *egc,
+                                         libxl__colo_save_state *css,
+                                         int id)
+{
+    int ok = 0;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    if (id != CHECKPOINT_SVM_SUSPENDED) {
+        LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
+            CHECKPOINT_SVM_SUSPENDED);
+        goto out;
+    }
+
+    if (!css->paused &&
+        libxl__qmp_query_xen_replication_status(gc, dss->domid)) {
+        LOGD(ERROR, dss->domid,
+             "replication error occurs when primary vm is running");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
+
+/* ===================== colo: send tailbuf ========================== */
+
+static void libxl__colo_save_domain_checkpoint_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+    libxl__domain_save_state *dss = sws->dss;
+
+    /* Convenience aliases */
+    libxl__colo_save_state *const css = &dss->css;
+
+    /* write emulator xenstore data, emulator context, and checkpoint end */
+    css->callback = NULL;
+    dss->sws.checkpoint_callback = colo_common_write_stream_done;
+    libxl__stream_write_start_checkpoint(shs->egc, &dss->sws);
+}
+
+/* ===================== colo: resume primary vm ===================== */
+
+/*
+ * Do the following things when resuming primary vm:
+ *  1. read CHECKPOINT_SVM_READY
+ *  2. do preresume
+ *  3. resume primary vm
+ *  4. read CHECKPOINT_SVM_RESUMED
+ */
+static void colo_read_svm_ready_done(libxl__egc *egc,
+                                     libxl__colo_save_state *css,
+                                     int id);
+static void colo_preresume_cb(libxl__egc *egc,
+                              libxl__checkpoint_devices_state *cds,
+                              int rc);
+static void colo_read_svm_resumed_done(libxl__egc *egc,
+                                       libxl__colo_save_state *css,
+                                       int id);
+
+static void libxl__colo_save_domain_resume_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__egc *egc = shs->egc;
+    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+    libxl__domain_save_state *dss = sws->dss;
+
+    /* Convenience aliases */
+    libxl__colo_save_state *const css = &dss->css;
+
+    EGC_GC;
+
+    /* read CHECKPOINT_SVM_READY */
+    css->callback = colo_read_svm_ready_done;
+    css->srs.checkpoint_callback = colo_common_read_stream_done;
+    libxl__stream_read_checkpoint_state(egc, &css->srs);
+}
+
+static void colo_read_svm_ready_done(libxl__egc *egc,
+                                     libxl__colo_save_state *css,
+                                     int id)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    if (id != CHECKPOINT_SVM_READY) {
+        LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
+            CHECKPOINT_SVM_READY);
+        goto out;
+    }
+
+    colo_proxy_preresume(&css->cps);
+
+    css->svm_running = true;
+    dss->cds.callback = colo_preresume_cb;
+    libxl__checkpoint_devices_preresume(egc, &dss->cds);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void colo_preresume_cb(libxl__egc *egc,
+                              libxl__checkpoint_devices_state *cds,
+                              int rc)
+{
+    libxl__colo_save_state *css = cds->concrete_data;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, dss->domid, "preresume fails");
+        goto out;
+    }
+
+    if (css->qdisk_used && !css->qdisk_setuped) {
+        if (libxl__qmp_start_replication(gc, dss->domid, true)) {
+            LOGD(ERROR, dss->domid, "starting replication fails");
+            goto out;
+        }
+        css->qdisk_setuped = true;
+    }
+
+    if (!css->paused) {
+        if (libxl__qmp_colo_do_checkpoint(gc, dss->domid)) {
+            LOGD(ERROR, dss->domid, "doing checkpoint fails");
+            goto out;
+        }
+    }
+
+    /* Resumes the domain and the device model */
+    if (libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1)) {
+        LOGD(ERROR, dss->domid, "cannot resume primary vm");
+        goto out;
+    }
+
+    /*
+     * The guest should be paused before doing colo because there is
+     * no disk migration.
+     */
+    if (css->paused) {
+        rc = libxl__domain_unpause_deprecated(gc, dss->domid);
+        if (rc) {
+            LOGD(ERROR, dss->domid, "cannot unpause primary vm");
+            goto out;
+        }
+        css->paused = false;
+    }
+
+    /* read CHECKPOINT_SVM_RESUMED */
+    css->callback = colo_read_svm_resumed_done;
+    css->srs.checkpoint_callback = colo_common_read_stream_done;
+    libxl__stream_read_checkpoint_state(egc, &css->srs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void colo_read_svm_resumed_done(libxl__egc *egc,
+                                       libxl__colo_save_state *css,
+                                       int id)
+{
+    int ok = 0;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    if (id != CHECKPOINT_SVM_RESUMED) {
+        LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
+            CHECKPOINT_SVM_RESUMED);
+        goto out;
+    }
+
+    colo_proxy_postresume(&css->cps);
+
+    ok = 1;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
+
+/* ===================== colo: wait new checkpoint ===================== */
+
+static void colo_start_new_checkpoint(libxl__egc *egc,
+                                      libxl__checkpoint_devices_state *cds,
+                                      int rc);
+static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css);
+static void colo_proxy_async_call_done(libxl__egc *egc,
+                                       libxl__ev_child *child,
+                                       int pid,
+                                       int status);
+
+static void colo_proxy_wait_for_checkpoint(libxl__egc *egc,
+                                           libxl__colo_save_state *css)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    ASYNC_CALL(egc, dss->cds.ao, &css->child, css,
+               colo_proxy_async_wait_for_checkpoint,
+               colo_proxy_async_call_done);
+}
+
+static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css)
+{
+    int req;
+
+    req = colo_proxy_checkpoint(&css->cps, COLO_PROXY_CHECKPOINT_TIMEOUT);
+    if (req < 0) {
+        /* some error happens */
+        _exit(1);
+    } else {
+        /* req == 0: no checkpoint is needed, do a checkpoint every 5s */
+        /* req > 0: net packets is not consistent, we need to start a
+         * checkpoint
+         */
+        _exit(0);
+    }
+}
+
+static void colo_proxy_async_call_done(libxl__egc *egc,
+                                       libxl__ev_child *child,
+                                       int pid,
+                                       int status)
+{
+    libxl__colo_save_state *css = CONTAINER_OF(child, *css, child);
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    if (status) {
+        LOGD(ERROR, dss->domid, "failed to wait for new checkpoint");
+        colo_start_new_checkpoint(egc, &dss->cds, ERROR_FAIL);
+        return;
+    }
+
+    colo_start_new_checkpoint(egc, &dss->cds, 0);
+}
+
+/*
+ * Do the following things:
+ * 1. do commit
+ * 2. wait for a new checkpoint
+ * 3. write CHECKPOINT_NEW
+ */
+static void colo_device_commit_cb(libxl__egc *egc,
+                                  libxl__checkpoint_devices_state *cds,
+                                  int rc);
+
+static void libxl__colo_save_domain_wait_checkpoint_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
+    libxl__domain_save_state *dss = sws->dss;
+    libxl__egc *egc = dss->sws.shs.egc;
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+    cds->callback = colo_device_commit_cb;
+    libxl__checkpoint_devices_commit(egc, cds);
+}
+
+static void colo_device_commit_cb(libxl__egc *egc,
+                                  libxl__checkpoint_devices_state *cds,
+                                  int rc)
+{
+    libxl__colo_save_state *css = cds->concrete_data;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, dss->domid, "commit fails");
+        goto out;
+    }
+
+    colo_proxy_wait_for_checkpoint(egc, css);
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void colo_start_new_checkpoint(libxl__egc *egc,
+                                      libxl__checkpoint_devices_state *cds,
+                                      int rc)
+{
+    libxl__colo_save_state *css = cds->concrete_data;
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_NEW };
+
+    if (rc)
+        goto out;
+
+    /* write CHECKPOINT_NEW */
+    css->callback = NULL;
+    dss->sws.checkpoint_callback = colo_common_write_stream_done;
+    libxl__stream_write_checkpoint_state(egc, &dss->sws, &srcs);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+/* ===================== colo: common callback ===================== */
+
+static void colo_common_write_stream_done(libxl__egc *egc,
+                                          libxl__stream_write_state *stream,
+                                          int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(stream, *dss, sws);
+    int ok;
+
+    /* Convenience aliases */
+    libxl__colo_save_state *const css = &dss->css;
+
+    EGC_GC;
+
+    if (rc < 0) {
+        /* TODO: it may be a internal error, but we don't know */
+        LOGD(ERROR, dss->domid, "sending data fails");
+        ok = 0;
+        goto out;
+    }
+
+    if (!css->callback) {
+        /* Everythins is OK */
+        ok = 1;
+        goto out;
+    }
+
+    css->callback(egc, css, 0);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
+
+static void colo_common_read_stream_done(libxl__egc *egc,
+                                         libxl__stream_read_state *stream,
+                                         int rc)
+{
+    libxl__colo_save_state *css = CONTAINER_OF(stream, *css, srs);
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+    int ok;
+
+    EGC_GC;
+
+    if (rc < 0) {
+        /* TODO: it may be a internal error, but we don't know */
+        LOGD(ERROR, dss->domid, "reading data fails");
+        ok = 0;
+        goto out;
+    }
+
+    if (!css->callback) {
+        /* Everythins is OK */
+        ok = 1;
+        goto out;
+    }
+
+    /* rc contains the id */
+    css->callback(egc, css, rc);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
+}
diff --git a/tools/libs/light/libxl_console.c b/tools/libs/light/libxl_console.c
new file mode 100644 (file)
index 0000000..047d23d
--- /dev/null
@@ -0,0 +1,802 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
+                            libxl_console_type type, char **tty_path)
+{
+    int rc;
+    char *dom_path;
+
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    switch (type) {
+    case LIBXL_CONSOLE_TYPE_SERIAL:
+        *tty_path = GCSPRINTF("%s/serial/%d/tty", dom_path, cons_num);
+        rc = 0;
+        break;
+    case LIBXL_CONSOLE_TYPE_PV:
+        if (cons_num == 0)
+            *tty_path = GCSPRINTF("%s/console/tty", dom_path);
+        else
+            *tty_path = GCSPRINTF("%s/tty",
+                                  libxl__domain_device_frontend_path(gc, domid,
+                                  cons_num, LIBXL__DEVICE_KIND_CONSOLE));
+        rc = 0;
+        break;
+    default:
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+out:
+    return rc;
+}
+
+int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
+                       libxl_console_type type, int notify_fd)
+{
+    GC_INIT(ctx);
+    char *p = GCSPRINTF("%s/xenconsole", libxl__private_bindir_path());
+    char *domid_s = GCSPRINTF("%d", domid);
+    char *cons_num_s = GCSPRINTF("%d", cons_num);
+    char *notify_fd_s;
+    char *cons_type_s;
+
+    switch (type) {
+    case LIBXL_CONSOLE_TYPE_PV:
+        cons_type_s = "pv";
+        break;
+    case LIBXL_CONSOLE_TYPE_SERIAL:
+        cons_type_s = "serial";
+        break;
+    case LIBXL_CONSOLE_TYPE_VUART:
+        cons_type_s = "vuart";
+        break;
+    default:
+        goto out;
+    }
+
+    if (notify_fd != -1) {
+        notify_fd_s = GCSPRINTF("%d", notify_fd);
+        execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
+              "--start-notify-fd", notify_fd_s, (void *)NULL);
+    } else {
+        execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
+              (void *)NULL);
+    }
+
+out:
+    GC_FREE;
+    return ERROR_FAIL;
+}
+
+int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
+                          libxl_console_type type, char **path)
+{
+    GC_INIT(ctx);
+    char *tty_path;
+    char *tty;
+    int rc;
+
+    rc = libxl__console_tty_path(gc, domid, cons_num, type, &tty_path);
+    if (rc) {
+        LOGD(ERROR, domid, "Failed to get tty path\n");
+        goto out;
+    }
+
+    tty = libxl__xs_read(gc, XBT_NULL, tty_path);
+    if (!tty || tty[0] == '\0') {
+       LOGED(ERROR, domid, "Unable to read console tty path `%s'",
+             tty_path);
+       rc = ERROR_FAIL;
+       goto out;
+    }
+
+    *path = libxl__strdup(NOGC, tty);
+    rc = 0;
+out:
+    GC_FREE;
+    return rc;
+}
+
+static int libxl__primary_console_find(libxl_ctx *ctx, uint32_t domid_vm,
+                                       uint32_t *domid, int *cons_num,
+                                       libxl_console_type *type)
+{
+    GC_INIT(ctx);
+    uint32_t stubdomid = libxl_get_stubdom_id(ctx, domid_vm);
+    int rc;
+
+    if (stubdomid) {
+        *domid = stubdomid;
+        *cons_num = STUBDOM_CONSOLE_SERIAL;
+        *type = LIBXL_CONSOLE_TYPE_PV;
+    } else {
+        switch (libxl__domain_type(gc, domid_vm)) {
+        case LIBXL_DOMAIN_TYPE_HVM:
+            *domid = domid_vm;
+            *cons_num = 0;
+            *type = LIBXL_CONSOLE_TYPE_SERIAL;
+            break;
+        case LIBXL_DOMAIN_TYPE_PVH:
+        case LIBXL_DOMAIN_TYPE_PV:
+            *domid = domid_vm;
+            *cons_num = 0;
+            *type = LIBXL_CONSOLE_TYPE_PV;
+            break;
+        case LIBXL_DOMAIN_TYPE_INVALID:
+            rc = ERROR_INVAL;
+            goto out;
+        default: abort();
+        }
+    }
+
+    rc = 0;
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm, int notify_fd)
+{
+    uint32_t domid;
+    int cons_num;
+    libxl_console_type type;
+    int rc;
+
+    rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
+    if ( rc ) return rc;
+    return libxl_console_exec(ctx, domid, cons_num, type, notify_fd);
+}
+
+int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm,
+                                  char **path)
+{
+    uint32_t domid;
+    int cons_num;
+    libxl_console_type type;
+    int rc;
+
+    rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
+    if ( rc ) return rc;
+    return libxl_console_get_tty(ctx, domid, cons_num, type, path);
+}
+
+int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass)
+{
+    GC_INIT(ctx);
+    const char *vnc_port;
+    const char *vnc_listen = NULL, *vnc_pass = NULL;
+    int port = 0, autopass_fd = -1;
+    char *vnc_bin, *args[] = {
+        "vncviewer",
+        NULL, /* hostname:display */
+        NULL, /* -autopass */
+        NULL,
+    };
+
+    vnc_port = libxl__xs_read(gc, XBT_NULL,
+                            GCSPRINTF(
+                            "/local/domain/%d/console/vnc-port", domid));
+    if (!vnc_port) {
+        LOGD(ERROR, domid, "Cannot get vnc-port");
+        goto x_fail;
+    }
+
+    port = atoi(vnc_port) - 5900;
+
+    vnc_listen = libxl__xs_read(gc, XBT_NULL,
+                                GCSPRINTF("/local/domain/%d/console/vnc-listen",
+                                          domid));
+
+    if ( autopass )
+        vnc_pass = libxl__xs_read(gc, XBT_NULL,
+                                  GCSPRINTF("/local/domain/%d/console/vnc-pass",
+                                            domid));
+
+    if ( NULL == vnc_listen )
+        vnc_listen = "localhost";
+
+    if ( (vnc_bin = getenv("VNCVIEWER")) )
+        args[0] = vnc_bin;
+
+    args[1] = GCSPRINTF("%s:%d", vnc_listen, port);
+
+    if ( vnc_pass ) {
+        char tmpname[] = "/tmp/vncautopass.XXXXXX";
+        autopass_fd = mkstemp(tmpname);
+        if ( autopass_fd < 0 ) {
+            LOGED(ERROR, domid, "mkstemp %s failed", tmpname);
+            goto x_fail;
+        }
+
+        if ( unlink(tmpname) ) {
+            /* should never happen */
+            LOGED(ERROR, domid, "unlink %s failed", tmpname);
+            goto x_fail;
+        }
+
+        if ( libxl_write_exactly(ctx, autopass_fd, vnc_pass, strlen(vnc_pass),
+                                    tmpname, "vnc password") )
+            goto x_fail;
+
+        if ( lseek(autopass_fd, SEEK_SET, 0) ) {
+            LOGED(ERROR, domid, "rewind %s (autopass) failed", tmpname);
+            goto x_fail;
+        }
+
+        args[2] = "-autopass";
+    }
+
+    libxl__exec(gc, autopass_fd, -1, -1, args[0], args, NULL);
+
+ x_fail:
+    GC_FREE;
+    return ERROR_FAIL;
+}
+
+int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
+                              libxl__device_console *console,
+                              libxl__domain_build_state *state,
+                              libxl__device *device)
+{
+    flexarray_t *front, *ro_front;
+    flexarray_t *back;
+    int rc;
+
+    if (console->devid && state) {
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    if (!console->devid && (console->name || console->path)) {
+        LOGD(ERROR, domid, "Primary console has invalid configuration");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    front = flexarray_make(gc, 16, 1);
+    ro_front = flexarray_make(gc, 16, 1);
+    back = flexarray_make(gc, 16, 1);
+
+    device->backend_devid = console->devid;
+    device->backend_domid = console->backend_domid;
+    device->backend_kind = LIBXL__DEVICE_KIND_CONSOLE;
+    device->devid = console->devid;
+    device->domid = domid;
+    device->kind = LIBXL__DEVICE_KIND_CONSOLE;
+
+    flexarray_append(back, "frontend-id");
+    flexarray_append(back, GCSPRINTF("%d", domid));
+    flexarray_append(back, "online");
+    flexarray_append(back, "1");
+    flexarray_append(back, "state");
+    flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
+    flexarray_append(back, "protocol");
+    flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
+
+    if (console->name) {
+        flexarray_append(ro_front, "name");
+        flexarray_append(ro_front, console->name);
+        flexarray_append(back, "name");
+        flexarray_append(back, console->name);
+    }
+    if (console->connection) {
+        flexarray_append(back, "connection");
+        flexarray_append(back, console->connection);
+    }
+    if (console->path) {
+        flexarray_append(back, "path");
+        flexarray_append(back, console->path);
+    }
+
+    flexarray_append(front, "backend-id");
+    flexarray_append(front, GCSPRINTF("%d", console->backend_domid));
+
+    flexarray_append(ro_front, "limit");
+    flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
+    flexarray_append(ro_front, "type");
+    if (console->consback == LIBXL__CONSOLE_BACKEND_XENCONSOLED)
+        flexarray_append(ro_front, "xenconsoled");
+    else
+        flexarray_append(ro_front, "ioemu");
+    flexarray_append(ro_front, "output");
+    flexarray_append(ro_front, console->output);
+    flexarray_append(ro_front, "tty");
+    if (state && state->console_tty)
+        flexarray_append(ro_front, state->console_tty);
+    else
+        flexarray_append(ro_front, "");
+
+    if (state) {
+        flexarray_append(ro_front, "port");
+        flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->console_port));
+        flexarray_append(ro_front, "ring-ref");
+        flexarray_append(ro_front, GCSPRINTF("%lu", state->console_mfn));
+    } else {
+        flexarray_append(front, "state");
+        flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
+        flexarray_append(front, "protocol");
+        flexarray_append(front, LIBXL_XENCONSOLE_PROTOCOL);
+    }
+    libxl__device_generic_add(gc, XBT_NULL, device,
+                              libxl__xs_kvs_of_flexarray(gc, back),
+                              libxl__xs_kvs_of_flexarray(gc, front),
+                              libxl__xs_kvs_of_flexarray(gc, ro_front));
+    rc = 0;
+out:
+    return rc;
+}
+
+int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
+                            libxl__device_console *console,
+                            libxl__domain_build_state *state)
+{
+    libxl__device device;
+    flexarray_t *ro_front;
+    flexarray_t *back;
+    int rc;
+
+    ro_front = flexarray_make(gc, 16, 1);
+    back = flexarray_make(gc, 16, 1);
+
+    device.backend_devid = console->devid;
+    device.backend_domid = console->backend_domid;
+    device.backend_kind = LIBXL__DEVICE_KIND_VUART;
+    device.devid = console->devid;
+    device.domid = domid;
+    device.kind = LIBXL__DEVICE_KIND_VUART;
+
+    flexarray_append(back, "frontend-id");
+    flexarray_append(back, GCSPRINTF("%d", domid));
+    flexarray_append(back, "online");
+    flexarray_append(back, "1");
+    flexarray_append(back, "state");
+    flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
+    flexarray_append(back, "protocol");
+    flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
+
+    flexarray_append(ro_front, "port");
+    flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->vuart_port));
+    flexarray_append(ro_front, "ring-ref");
+    flexarray_append(ro_front, GCSPRINTF("%"PRIu_xen_pfn, state->vuart_gfn));
+    flexarray_append(ro_front, "limit");
+    flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
+    flexarray_append(ro_front, "type");
+    flexarray_append(ro_front, "xenconsoled");
+
+    rc = libxl__device_generic_add(gc, XBT_NULL, &device,
+                                   libxl__xs_kvs_of_flexarray(gc, back),
+                                   NULL,
+                                   libxl__xs_kvs_of_flexarray(gc, ro_front));
+    return rc;
+}
+
+int libxl__init_console_from_channel(libxl__gc *gc,
+                                     libxl__device_console *console,
+                                     int dev_num,
+                                     libxl_device_channel *channel)
+{
+    int rc;
+
+    libxl__device_console_init(console);
+
+    /* Perform validation first, allocate second. */
+
+    if (channel->devid == -1)
+        channel->devid = dev_num;
+
+    if (!channel->name) {
+        LOG(ERROR, "channel %d has no name", channel->devid);
+        return ERROR_INVAL;
+    }
+
+    if (channel->backend_domname) {
+        rc = libxl_domain_qualifier_to_domid(CTX, channel->backend_domname,
+                                             &channel->backend_domid);
+        if (rc < 0) return rc;
+    }
+
+    /* The xenstore 'output' node tells the backend what to connect the console
+       to. If the channel has "connection = pty" then the "output" node will be
+       set to "pty". If the channel has "connection = socket" then the "output"
+       node will be set to "chardev:libxl-channel%d". This tells the qemu
+       backend to proxy data between the console ring and the character device
+       with id "libxl-channel%d". These character devices are currently defined
+       on the qemu command-line via "-chardev" options in libxl_dm.c */
+
+    switch (channel->connection) {
+        case LIBXL_CHANNEL_CONNECTION_UNKNOWN:
+            LOG(ERROR, "channel %d has no defined connection; "
+                "to where should it be connected?", channel->devid);
+            return ERROR_INVAL;
+        case LIBXL_CHANNEL_CONNECTION_PTY:
+            console->connection = libxl__strdup(NOGC, "pty");
+            console->output = libxl__sprintf(NOGC, "pty");
+            break;
+        case LIBXL_CHANNEL_CONNECTION_SOCKET:
+            if (!channel->u.socket.path) {
+                LOG(ERROR, "channel %d has no path", channel->devid);
+                return ERROR_INVAL;
+            }
+            console->connection = libxl__strdup(NOGC, "socket");
+            console->path = libxl__strdup(NOGC, channel->u.socket.path);
+            console->output = libxl__sprintf(NOGC, "chardev:libxl-channel%d",
+                                             channel->devid);
+            break;
+        default:
+            /* We've forgotten to add the clause */
+            LOG(ERROR, "%s: missing implementation for channel connection %d",
+                __func__, channel->connection);
+            abort();
+    }
+
+    console->devid = channel->devid;
+    console->consback = LIBXL__CONSOLE_BACKEND_IOEMU;
+    console->backend_domid = channel->backend_domid;
+    console->name = libxl__strdup(NOGC, channel->name);
+
+    return 0;
+}
+
+static int libxl__device_channel_from_xenstore(libxl__gc *gc,
+                                            const char *libxl_path,
+                                            libxl_device_channel *channel)
+{
+    const char *tmp;
+    int rc;
+
+    libxl_device_channel_init(channel);
+
+    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+                                GCSPRINTF("%s/name", libxl_path),
+                                (const char **)(&channel->name));
+    if (rc) goto out;
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/connection", libxl_path), &tmp);
+    if (rc) goto out;
+    if (!strcmp(tmp, "pty")) {
+        channel->connection = LIBXL_CHANNEL_CONNECTION_PTY;
+    } else if (!strcmp(tmp, "socket")) {
+        channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
+        rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+                                    GCSPRINTF("%s/path", libxl_path),
+                                    (const char **)(&channel->u.socket.path));
+        if (rc) goto out;
+    } else {
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    rc = 0;
+ out:
+    return rc;
+}
+
+static int libxl__append_channel_list(libxl__gc *gc,
+                                              uint32_t domid,
+                                              libxl_device_channel **channels,
+                                              int *nchannels)
+{
+    char *libxl_dir_path = NULL;
+    char **dir = NULL;
+    unsigned int n = 0, devid = 0;
+    libxl_device_channel *next = NULL;
+    int rc = 0, i;
+
+    libxl_dir_path = GCSPRINTF("%s/device/%s",
+                               libxl__xs_libxl_path(gc, domid),
+                               libxl__device_kind_to_string(
+                               LIBXL__DEVICE_KIND_CONSOLE));
+    dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n);
+    if (!dir || !n)
+      goto out;
+
+    for (i = 0; i < n; i++) {
+        const char *libxl_path, *name;
+        libxl_device_channel *tmp;
+
+        libxl_path = GCSPRINTF("%s/%s", libxl_dir_path, dir[i]);
+        name = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/name", libxl_path));
+        /* 'channels' are consoles with names, so ignore all consoles
+           without names */
+        if (!name) continue;
+        tmp = realloc(*channels,
+                      sizeof(libxl_device_channel) * (*nchannels + devid + 1));
+        if (!tmp) {
+          rc = ERROR_NOMEM;
+          goto out;
+        }
+        *channels = tmp;
+        next = *channels + *nchannels + devid;
+        rc = libxl__device_channel_from_xenstore(gc, libxl_path, next);
+        if (rc) goto out;
+        next->devid = devid;
+        devid++;
+    }
+    *nchannels += devid;
+    return 0;
+
+ out:
+    return rc;
+}
+
+libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
+                                                uint32_t domid,
+                                                int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_channel *channels = NULL;
+    int rc;
+
+    *num = 0;
+
+    rc = libxl__append_channel_list(gc, domid, &channels, num);
+    if (rc) goto out_err;
+
+    GC_FREE;
+    return channels;
+
+out_err:
+    LOGD(ERROR, domid, "Unable to list channels");
+    while (*num) {
+        (*num)--;
+        libxl_device_channel_dispose(&channels[*num]);
+    }
+    free(channels);
+    return NULL;
+}
+
+int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                 const libxl_device_channel *channel,
+                                 libxl_channelinfo *channelinfo)
+{
+    GC_INIT(ctx);
+    char *fe_path, *libxl_path;
+    char *val;
+    int rc;
+
+    channelinfo->devid = channel->devid;
+
+    fe_path = libxl__domain_device_frontend_path(gc, domid,
+                                                 channelinfo->devid + 1,
+                                                 LIBXL__DEVICE_KIND_CONSOLE);
+    libxl_path = libxl__domain_device_libxl_path(gc, domid,
+                                                 channelinfo->devid + 1,
+                                                 LIBXL__DEVICE_KIND_CONSOLE);
+
+    channelinfo->backend = xs_read(ctx->xsh, XBT_NULL,
+                                   GCSPRINTF("%s/backend", libxl_path), NULL);
+    if (!channelinfo->backend) {
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    rc = libxl__backendpath_parse_domid(gc, channelinfo->backend,
+                                        &channelinfo->backend_id);
+    if (rc) goto out;
+
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
+    channelinfo->state = val ? strtoul(val, NULL, 10) : -1;
+    channelinfo->frontend = libxl__strdup(NOGC, fe_path);
+    channelinfo->frontend_id = domid;
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
+    channelinfo->rref = val ? strtoul(val, NULL, 10) : -1;
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/port", fe_path));
+    channelinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+    channelinfo->connection = channel->connection;
+    switch (channel->connection) {
+         case LIBXL_CHANNEL_CONNECTION_PTY:
+             val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tty", fe_path));
+             /*
+              * It is obviously very wrong for this value to be in the
+              * frontend.  But in XSA-175 we don't want to re-engineer
+              * this because other xenconsole code elsewhere (some
+              * even out of tree, perhaps) expects this node to be
+              * here.
+              *
+              * FE/pty is readonly for the guest.  It always exists if
+              * FE does because libxl__device_console_add
+              * unconditionally creates it and nothing deletes it.
+              *
+              * The guest can delete the whole FE (which it has write
+              * privilege on) but the containing directories
+              * /local/GUEST[/device[/console]] are also RO for the
+              * guest.  So if the guest deletes FE it cannot recreate
+              * it.
+              *
+              * Therefore the guest cannot cause FE/pty to contain bad
+              * data, although it can cause it to not exist.
+              */
+             if (!val) val = "/NO-SUCH-PATH";
+             channelinfo->u.pty.path = strdup(val);
+             break;
+         default:
+             break;
+    }
+    rc = 0;
+ out:
+    GC_FREE;
+    return rc;
+}
+
+static int libxl__device_vfb_setdefault(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_vfb *vfb, bool hotplug)
+{
+    int rc;
+
+    libxl_defbool_setdefault(&vfb->vnc.enable, true);
+    if (libxl_defbool_val(vfb->vnc.enable)) {
+        if (!vfb->vnc.listen) {
+            vfb->vnc.listen = strdup("127.0.0.1");
+            if (!vfb->vnc.listen) return ERROR_NOMEM;
+        }
+
+        libxl_defbool_setdefault(&vfb->vnc.findunused, true);
+    } else {
+        libxl_defbool_setdefault(&vfb->vnc.findunused, false);
+    }
+
+    libxl_defbool_setdefault(&vfb->sdl.enable, false);
+    libxl_defbool_setdefault(&vfb->sdl.opengl, false);
+
+    rc = libxl__resolve_domid(gc, vfb->backend_domname, &vfb->backend_domid);
+    return rc;
+}
+
+int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    rc = libxl__device_add(gc, domid, &libxl__vfb_devtype, vfb);
+    if (rc) {
+        LOGD(ERROR, domid, "Unable to add vfb device");
+        goto out;
+    }
+
+out:
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+static int libxl__set_xenstore_vfb(libxl__gc *gc, uint32_t domid,
+                                   libxl_device_vfb *vfb,
+                                  flexarray_t *back, flexarray_t *front,
+                                  flexarray_t *ro_front)
+{
+    flexarray_append_pair(back, "vnc",
+                          libxl_defbool_val(vfb->vnc.enable) ? "1" : "0");
+    flexarray_append_pair(back, "vnclisten", vfb->vnc.listen);
+    flexarray_append_pair(back, "vncpasswd", vfb->vnc.passwd);
+    flexarray_append_pair(back, "vncdisplay",
+                          GCSPRINTF("%d", vfb->vnc.display));
+    flexarray_append_pair(back, "vncunused",
+                          libxl_defbool_val(vfb->vnc.findunused) ? "1" : "0");
+    flexarray_append_pair(back, "sdl",
+                          libxl_defbool_val(vfb->sdl.enable) ? "1" : "0");
+    flexarray_append_pair(back, "opengl",
+                          libxl_defbool_val(vfb->sdl.opengl) ? "1" : "0");
+    if (vfb->sdl.xauthority) {
+        flexarray_append_pair(back, "xauthority", vfb->sdl.xauthority);
+    }
+    if (vfb->sdl.display) {
+        flexarray_append_pair(back, "display", vfb->sdl.display);
+    }
+
+    return 0;
+}
+
+/* The following functions are defined:
+ * libxl_device_vfb_remove
+ * libxl_device_vfb_destroy
+ */
+
+/* channel/console hotunplug is not implemented. There are 2 possibilities:
+ * 1. add support for secondary consoles to xenconsoled
+ * 2. dynamically add/remove qemu chardevs via qmp messages. */
+
+#define libxl__add_vfbs NULL
+#define libxl_device_vfb_list NULL
+#define libxl_device_vfb_compare NULL
+
+static LIBXL_DEFINE_UPDATE_DEVID(vfb)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(vfb)
+
+/* vfb */
+LIBXL_DEFINE_DEVICE_REMOVE(vfb)
+
+DEFINE_DEVICE_TYPE_STRUCT(vfb, VFB,
+    .skip_attach = 1,
+    .set_xenstore_config = (device_set_xenstore_config_fn_t)
+                           libxl__set_xenstore_vfb,
+);
+
+libxl_xen_console_reader *
+    libxl_xen_console_read_start(libxl_ctx *ctx, int clear)
+{
+    GC_INIT(ctx);
+    libxl_xen_console_reader *cr;
+    unsigned int size = 16384;
+
+    cr = libxl__zalloc(NOGC, sizeof(libxl_xen_console_reader));
+    cr->buffer = libxl__zalloc(NOGC, size);
+    cr->size = size;
+    cr->count = size;
+    cr->clear = clear;
+    cr->incremental = 1;
+
+    GC_FREE;
+    return cr;
+}
+
+/* return values:                                          *line_r
+ *   1          success, whole line obtained from buffer    non-0
+ *   0          no more lines available right now           0
+ *   negative   error code ERROR_*                          0
+ * On success *line_r is updated to point to a nul-terminated
+ * string which is valid until the next call on the same console
+ * reader.  The libxl caller may overwrite parts of the string
+ * if it wishes. */
+int libxl_xen_console_read_line(libxl_ctx *ctx,
+                                libxl_xen_console_reader *cr,
+                                char **line_r)
+{
+    int ret;
+    GC_INIT(ctx);
+
+    memset(cr->buffer, 0, cr->size);
+    ret = xc_readconsolering(ctx->xch, cr->buffer, &cr->count,
+                             cr->clear, cr->incremental, &cr->index);
+    if (ret < 0) {
+        LOGE(ERROR, "reading console ring buffer");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    if (!ret) {
+        if (cr->count) {
+            *line_r = cr->buffer;
+            ret = 1;
+        } else {
+            *line_r = NULL;
+            ret = 0;
+        }
+    }
+
+    GC_FREE;
+    return ret;
+}
+
+void libxl_xen_console_read_finish(libxl_ctx *ctx,
+                                   libxl_xen_console_reader *cr)
+{
+    free(cr->buffer);
+    free(cr);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_convert_callout.c b/tools/libs/light/libxl_convert_callout.c
new file mode 100644 (file)
index 0000000..5e5678b
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/*
+ * Infrastructure for converting a legacy migration stream into a
+ * libxl v2 stream.
+ *
+ * This is done by fork()ing the python conversion script, which takes
+ * in a legacy stream, and puts out a suitably-formatted v2 stream.
+ */
+
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+                          pid_t pid, int status);
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc);
+static void helper_done(libxl__egc *egc,
+                        libxl__conversion_helper_state *chs);
+
+/*----- Entrypoints -----*/
+
+void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
+{
+    assert(chs->ao);
+
+    chs->v2_carefd = NULL;
+    chs->rc = 0;
+    libxl__ao_abortable_init(&chs->abrt);
+    libxl__ev_child_init(&chs->child);
+}
+
+int libxl__convert_legacy_stream(libxl__egc *egc,
+                                 libxl__conversion_helper_state *chs)
+{
+    STATE_AO_GC(chs->ao);
+    libxl__carefd *child_in = NULL, *child_out = NULL;
+    int rc = 0;
+
+    chs->abrt.ao = chs->ao;
+    chs->abrt.callback = helper_stop;
+    rc = libxl__ao_abortable_register(&chs->abrt);
+    if (rc) goto err;
+
+    libxl__carefd_begin();
+    int fds[2];
+    if (libxl_pipe(CTX, fds)) {
+        rc = ERROR_FAIL;
+        libxl__carefd_unlock();
+        goto err;
+    }
+    child_out = libxl__carefd_record(CTX, fds[0]);
+    child_in  = libxl__carefd_record(CTX, fds[1]);
+    libxl__carefd_unlock();
+
+    pid_t pid = libxl__ev_child_fork(gc, &chs->child, helper_exited);
+    if (!pid) {
+        char * const args[] =
+        {
+            getenv("LIBXL_CONVERT_HELPER") ?:
+                LIBEXEC_BIN "/convert-legacy-stream",
+            "--in",     GCSPRINTF("%d", chs->legacy_fd),
+            "--out",    GCSPRINTF("%d", fds[1]),
+            /*
+             * The width calculation is an assumption for the common
+             * case.  The conversion script needs to know the width of
+             * the toolstack which saved the legacy stream.
+             *
+             * In the overwhelming majority of cases, the width of the
+             * saving toolstack will be the same as our current
+             * width.  To avoid extending the libxl API with a
+             * parameter intended to disappear shortly, this option
+             * has not been exposed to the caller.
+             *
+             * If more complicated conversion is required, the
+             * conversion script can be instantiated manually, which
+             * will bypass all of this conversion logic.
+             */
+            "--width",  sizeof(unsigned long) == 8 ? "64" : "32",
+
+            "--guest",  chs->hvm ? "hvm" : "pv",
+            "--format", "libxl",
+            /* "--verbose", */
+            NULL,
+        };
+
+        libxl_fd_set_cloexec(CTX, chs->legacy_fd, 0);
+        libxl_fd_set_cloexec(CTX, libxl__carefd_fd(child_in), 0);
+
+        libxl__exec(gc,
+                    -1, -1, -1,
+                    args[0], args, NULL);
+    }
+
+    libxl__carefd_close(child_in);
+    chs->v2_carefd = child_out;
+
+    assert(!rc);
+    return rc;
+
+ err:
+    libxl__ao_abortable_deregister(&chs->abrt);
+    assert(rc);
+    return rc;
+}
+
+void libxl__conversion_helper_abort(libxl__egc *egc,
+                                    libxl__conversion_helper_state *chs,
+                                    int rc)
+{
+    STATE_AO_GC(chs->ao);
+    assert(rc);
+
+    if (libxl__conversion_helper_inuse(chs)) {
+
+        if (!chs->rc)
+            chs->rc = rc;
+
+        libxl__kill(gc, chs->child.pid, SIGTERM, "conversion helper");
+    }
+}
+
+/*----- State handling -----*/
+
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
+{
+    libxl__conversion_helper_state *chs = CONTAINER_OF(abrt, *chs, abrt);
+    STATE_AO_GC(chs->ao);
+
+    libxl__conversion_helper_abort(egc, chs, rc);
+}
+
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+                          pid_t pid, int status)
+{
+    libxl__conversion_helper_state *chs = CONTAINER_OF(ch, *chs, child);
+    STATE_AO_GC(chs->ao);
+
+    if (status) {
+        libxl_report_child_exitstatus(
+            CTX, chs->rc ? XTL_DEBUG : XTL_ERROR,
+            "conversion helper", pid, status);
+
+        if (!chs->rc)
+            chs->rc = ERROR_FAIL;
+    }
+
+    helper_done(egc, chs);
+}
+
+static void helper_done(libxl__egc *egc,
+                        libxl__conversion_helper_state *chs)
+{
+    STATE_AO_GC(chs->ao);
+
+    assert(!libxl__conversion_helper_inuse(chs));
+
+    libxl__ao_abortable_deregister(&chs->abrt);
+
+    chs->completion_callback(egc, chs, chs->rc);
+}
diff --git a/tools/libs/light/libxl_cpuid.c b/tools/libs/light/libxl_cpuid.c
new file mode 100644 (file)
index 0000000..08e85dc
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
+{
+    return !libxl_cpuid_policy_list_length(pl);
+}
+
+void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
+{
+    int i, j;
+    libxl_cpuid_policy_list cpuid_list = *p_cpuid_list;
+
+    if (cpuid_list == NULL)
+        return;
+    for (i = 0; cpuid_list[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+        for (j = 0; j < 4; j++)
+            if (cpuid_list[i].policy[j] != NULL) {
+                free(cpuid_list[i].policy[j]);
+                cpuid_list[i].policy[j] = NULL;
+            }
+    }
+    free(cpuid_list);
+    *p_cpuid_list = NULL;
+    return;
+}
+
+#define CPUID_REG_INV 0
+#define CPUID_REG_EAX 1
+#define CPUID_REG_EBX 2
+#define CPUID_REG_ECX 3
+#define CPUID_REG_EDX 4
+
+/* mapping CPUID features to names
+ * holds a "name" for each feature, specified by the "leaf" number (and an
+ * optional "subleaf" in ECX), the "reg"ister (EAX-EDX) used and a number of
+ * bits starting with "bit" and being "length" bits long.
+ * Used for the static structure describing all features.
+ */
+struct cpuid_flags {
+    char* name;
+    uint32_t leaf;
+    uint32_t subleaf;
+    int reg;
+    int bit;
+    int length;
+};
+
+/* go through the dynamic array finding the entry for a specified leaf.
+ * if no entry exists, allocate one and return that.
+ */
+static libxl_cpuid_policy_list cpuid_find_match(libxl_cpuid_policy_list *list,
+                                          uint32_t leaf, uint32_t subleaf)
+{
+    int i = 0;
+
+    if (*list != NULL) {
+        for (i = 0; (*list)[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+            if ((*list)[i].input[0] == leaf && (*list)[i].input[1] == subleaf)
+                return *list + i;
+        }
+    }
+    *list = realloc(*list, sizeof((*list)[0]) * (i + 2));
+    (*list)[i].input[0] = leaf;
+    (*list)[i].input[1] = subleaf;
+    memset((*list)[i].policy, 0, 4 * sizeof(char*));
+    (*list)[i + 1].input[0] = XEN_CPUID_INPUT_UNUSED;
+    return *list + i;
+}
+
+/* parse a single key=value pair and translate it into the libxc
+ * used interface using 32-characters strings for each register.
+ * Will overwrite earlier entries and thus can be called multiple
+ * times.
+ */
+int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
+{
+#define NA XEN_CPUID_INPUT_UNUSED
+    static const struct cpuid_flags cpuid_flags[] = {
+        {"maxleaf",      0x00000000, NA, CPUID_REG_EAX,  0, 32},
+      /* the following two entries are subject to tweaking later in the code */
+        {"stepping",     0x00000001, NA, CPUID_REG_EAX,  0,  4},
+        {"model",        0x00000001, NA, CPUID_REG_EAX,  4,  8},
+        {"family",       0x00000001, NA, CPUID_REG_EAX,  8,  8},
+
+        {"brandid",      0x00000001, NA, CPUID_REG_EBX,  0,  8},
+        {"clflush",      0x00000001, NA, CPUID_REG_EBX,  8,  8},
+        {"proccount",    0x00000001, NA, CPUID_REG_EBX, 16,  8},
+        {"localapicid",  0x00000001, NA, CPUID_REG_EBX, 24,  8},
+
+        {"sse3",         0x00000001, NA, CPUID_REG_ECX,  0,  1},
+        {"pclmulqdq",    0x00000001, NA, CPUID_REG_ECX,  1,  1},
+        {"dtes64",       0x00000001, NA, CPUID_REG_ECX,  2,  1},
+        {"monitor",      0x00000001, NA, CPUID_REG_ECX,  3,  1},
+        {"dscpl",        0x00000001, NA, CPUID_REG_ECX,  4,  1},
+        {"vmx",          0x00000001, NA, CPUID_REG_ECX,  5,  1},
+        {"smx",          0x00000001, NA, CPUID_REG_ECX,  6,  1},
+        {"est",          0x00000001, NA, CPUID_REG_ECX,  7,  1},
+        {"tm2",          0x00000001, NA, CPUID_REG_ECX,  8,  1},
+        {"ssse3",        0x00000001, NA, CPUID_REG_ECX,  9,  1},
+        {"cntxid",       0x00000001, NA, CPUID_REG_ECX, 10,  1},
+        {"fma",          0x00000001, NA, CPUID_REG_ECX, 12,  1},
+        {"cmpxchg16",    0x00000001, NA, CPUID_REG_ECX, 13,  1},
+        {"xtpr",         0x00000001, NA, CPUID_REG_ECX, 14,  1},
+        {"pdcm",         0x00000001, NA, CPUID_REG_ECX, 15,  1},
+        {"pcid",         0x00000001, NA, CPUID_REG_ECX, 17,  1},
+        {"dca",          0x00000001, NA, CPUID_REG_ECX, 18,  1},
+        /* Linux uses sse4_{1,2}.  Keep sse4.{1,2} for compatibility */
+        {"sse4_1",       0x00000001, NA, CPUID_REG_ECX, 19,  1},
+        {"sse4.1",       0x00000001, NA, CPUID_REG_ECX, 19,  1},
+        {"sse4_2",       0x00000001, NA, CPUID_REG_ECX, 20,  1},
+        {"sse4.2",       0x00000001, NA, CPUID_REG_ECX, 20,  1},
+        {"x2apic",       0x00000001, NA, CPUID_REG_ECX, 21,  1},
+        {"movbe",        0x00000001, NA, CPUID_REG_ECX, 22,  1},
+        {"popcnt",       0x00000001, NA, CPUID_REG_ECX, 23,  1},
+        {"tsc-deadline", 0x00000001, NA, CPUID_REG_ECX, 24,  1},
+        {"aes",          0x00000001, NA, CPUID_REG_ECX, 25,  1},
+        {"xsave",        0x00000001, NA, CPUID_REG_ECX, 26,  1},
+        {"osxsave",      0x00000001, NA, CPUID_REG_ECX, 27,  1},
+        {"avx",          0x00000001, NA, CPUID_REG_ECX, 28,  1},
+        {"f16c",         0x00000001, NA, CPUID_REG_ECX, 29,  1},
+        {"rdrand",       0x00000001, NA, CPUID_REG_ECX, 30,  1},
+        {"hypervisor",   0x00000001, NA, CPUID_REG_ECX, 31,  1},
+
+        {"fpu",          0x00000001, NA, CPUID_REG_EDX,  0,  1},
+        {"vme",          0x00000001, NA, CPUID_REG_EDX,  1,  1},
+        {"de",           0x00000001, NA, CPUID_REG_EDX,  2,  1},
+        {"pse",          0x00000001, NA, CPUID_REG_EDX,  3,  1},
+        {"tsc",          0x00000001, NA, CPUID_REG_EDX,  4,  1},
+        {"msr",          0x00000001, NA, CPUID_REG_EDX,  5,  1},
+        {"pae",          0x00000001, NA, CPUID_REG_EDX,  6,  1},
+        {"mce",          0x00000001, NA, CPUID_REG_EDX,  7,  1},
+        {"cmpxchg8",     0x00000001, NA, CPUID_REG_EDX,  8,  1},
+        {"apic",         0x00000001, NA, CPUID_REG_EDX,  9,  1},
+        {"sysenter",     0x00000001, NA, CPUID_REG_EDX, 11,  1},
+        {"mtrr",         0x00000001, NA, CPUID_REG_EDX, 12,  1},
+        {"pge",          0x00000001, NA, CPUID_REG_EDX, 13,  1},
+        {"mca",          0x00000001, NA, CPUID_REG_EDX, 14,  1},
+        {"cmov",         0x00000001, NA, CPUID_REG_EDX, 15,  1},
+        {"pat",          0x00000001, NA, CPUID_REG_EDX, 16,  1},
+        {"pse36",        0x00000001, NA, CPUID_REG_EDX, 17,  1},
+        {"psn",          0x00000001, NA, CPUID_REG_EDX, 18,  1},
+        {"clfsh",        0x00000001, NA, CPUID_REG_EDX, 19,  1},
+        {"ds",           0x00000001, NA, CPUID_REG_EDX, 21,  1},
+        {"acpi",         0x00000001, NA, CPUID_REG_EDX, 22,  1},
+        {"mmx",          0x00000001, NA, CPUID_REG_EDX, 23,  1},
+        {"fxsr",         0x00000001, NA, CPUID_REG_EDX, 24,  1},
+        {"sse",          0x00000001, NA, CPUID_REG_EDX, 25,  1},
+        {"sse2",         0x00000001, NA, CPUID_REG_EDX, 26,  1},
+        {"ss",           0x00000001, NA, CPUID_REG_EDX, 27,  1},
+        {"htt",          0x00000001, NA, CPUID_REG_EDX, 28,  1},
+        {"tm",           0x00000001, NA, CPUID_REG_EDX, 29,  1},
+        {"ia64",         0x00000001, NA, CPUID_REG_EDX, 30,  1},
+        {"pbe",          0x00000001, NA, CPUID_REG_EDX, 31,  1},
+
+        {"arat",         0x00000006, NA, CPUID_REG_EAX,  2,  1},
+
+        {"fsgsbase",     0x00000007,  0, CPUID_REG_EBX,  0,  1},
+        {"tsc_adjust",   0x00000007,  0, CPUID_REG_EBX,  1,  1},
+        {"bmi1",         0x00000007,  0, CPUID_REG_EBX,  3,  1},
+        {"hle",          0x00000007,  0, CPUID_REG_EBX,  4,  1},
+        {"avx2",         0x00000007,  0, CPUID_REG_EBX,  5,  1},
+        {"smep",         0x00000007,  0, CPUID_REG_EBX,  7,  1},
+        {"bmi2",         0x00000007,  0, CPUID_REG_EBX,  8,  1},
+        {"erms",         0x00000007,  0, CPUID_REG_EBX,  9,  1},
+        {"invpcid",      0x00000007,  0, CPUID_REG_EBX, 10,  1},
+        {"rtm",          0x00000007,  0, CPUID_REG_EBX, 11,  1},
+        {"cmt",          0x00000007,  0, CPUID_REG_EBX, 12,  1},
+        {"mpx",          0x00000007,  0, CPUID_REG_EBX, 14,  1},
+        {"avx512f",      0x00000007,  0, CPUID_REG_EBX, 16,  1},
+        {"avx512dq",     0x00000007,  0, CPUID_REG_EBX, 17,  1},
+        {"rdseed",       0x00000007,  0, CPUID_REG_EBX, 18,  1},
+        {"adx",          0x00000007,  0, CPUID_REG_EBX, 19,  1},
+        {"smap",         0x00000007,  0, CPUID_REG_EBX, 20,  1},
+        {"avx512-ifma",  0x00000007,  0, CPUID_REG_EBX, 21,  1},
+        {"clflushopt",   0x00000007,  0, CPUID_REG_EBX, 23,  1},
+        {"clwb",         0x00000007,  0, CPUID_REG_EBX, 24,  1},
+        {"avx512pf",     0x00000007,  0, CPUID_REG_EBX, 26,  1},
+        {"avx512er",     0x00000007,  0, CPUID_REG_EBX, 27,  1},
+        {"avx512cd",     0x00000007,  0, CPUID_REG_EBX, 28,  1},
+        {"sha",          0x00000007,  0, CPUID_REG_EBX, 29,  1},
+        {"avx512bw",     0x00000007,  0, CPUID_REG_EBX, 30,  1},
+        {"avx512vl",     0x00000007,  0, CPUID_REG_EBX, 31,  1},
+
+        {"prefetchwt1",  0x00000007,  0, CPUID_REG_ECX,  0,  1},
+        {"avx512-vbmi",  0x00000007,  0, CPUID_REG_ECX,  1,  1},
+        {"umip",         0x00000007,  0, CPUID_REG_ECX,  2,  1},
+        {"pku",          0x00000007,  0, CPUID_REG_ECX,  3,  1},
+        {"ospke",        0x00000007,  0, CPUID_REG_ECX,  4,  1},
+        {"avx512-vbmi2", 0x00000007,  0, CPUID_REG_ECX,  6,  1},
+        {"cet-ss",       0x00000007,  0, CPUID_REG_ECX,  7,  1},
+        {"gfni",         0x00000007,  0, CPUID_REG_ECX,  8,  1},
+        {"vaes",         0x00000007,  0, CPUID_REG_ECX,  9,  1},
+        {"vpclmulqdq",   0x00000007,  0, CPUID_REG_ECX, 10,  1},
+        {"avx512-vnni",  0x00000007,  0, CPUID_REG_ECX, 11,  1},
+        {"avx512-bitalg",0x00000007,  0, CPUID_REG_ECX, 12,  1},
+        {"avx512-vpopcntdq",0x00000007,0,CPUID_REG_ECX, 14,  1},
+        {"tsxldtrk",     0x00000007,  0, CPUID_REG_ECX, 16,  1},
+        {"rdpid",        0x00000007,  0, CPUID_REG_ECX, 22,  1},
+        {"cldemote",     0x00000007,  0, CPUID_REG_ECX, 25,  1},
+
+        {"avx512-4vnniw",0x00000007,  0, CPUID_REG_EDX,  2,  1},
+        {"avx512-4fmaps",0x00000007,  0, CPUID_REG_EDX,  3,  1},
+        {"avx512-vp2intersect",0x00000007,0,CPUID_REG_EDX,8, 1},
+        {"srbds-ctrl",   0x00000007,  0, CPUID_REG_EDX,  9,  1},
+        {"md-clear",     0x00000007,  0, CPUID_REG_EDX, 10,  1},
+        {"serialize",    0x00000007,  0, CPUID_REG_EDX, 14,  1},
+        {"cet-ibt",      0x00000007,  0, CPUID_REG_EDX, 20,  1},
+        {"ibrsb",        0x00000007,  0, CPUID_REG_EDX, 26,  1},
+        {"stibp",        0x00000007,  0, CPUID_REG_EDX, 27,  1},
+        {"l1d-flush",    0x00000007,  0, CPUID_REG_EDX, 28,  1},
+        {"arch-caps",    0x00000007,  0, CPUID_REG_EDX, 29,  1},
+        {"core-caps",    0x00000007,  0, CPUID_REG_EDX, 30,  1},
+        {"ssbd",         0x00000007,  0, CPUID_REG_EDX, 31,  1},
+
+        {"avx512-bf16",  0x00000007,  1, CPUID_REG_EAX,  5,  1},
+
+        {"lahfsahf",     0x80000001, NA, CPUID_REG_ECX,  0,  1},
+        {"cmplegacy",    0x80000001, NA, CPUID_REG_ECX,  1,  1},
+        {"svm",          0x80000001, NA, CPUID_REG_ECX,  2,  1},
+        {"extapic",      0x80000001, NA, CPUID_REG_ECX,  3,  1},
+        {"altmovcr8",    0x80000001, NA, CPUID_REG_ECX,  4,  1},
+        {"abm",          0x80000001, NA, CPUID_REG_ECX,  5,  1},
+        {"sse4a",        0x80000001, NA, CPUID_REG_ECX,  6,  1},
+        {"misalignsse",  0x80000001, NA, CPUID_REG_ECX,  7,  1},
+        {"3dnowprefetch",0x80000001, NA, CPUID_REG_ECX,  8,  1},
+        {"osvw",         0x80000001, NA, CPUID_REG_ECX,  9,  1},
+        {"ibs",          0x80000001, NA, CPUID_REG_ECX, 10,  1},
+        {"xop",          0x80000001, NA, CPUID_REG_ECX, 11,  1},
+        {"skinit",       0x80000001, NA, CPUID_REG_ECX, 12,  1},
+        {"wdt",          0x80000001, NA, CPUID_REG_ECX, 13,  1},
+        {"lwp",          0x80000001, NA, CPUID_REG_ECX, 15,  1},
+        {"fma4",         0x80000001, NA, CPUID_REG_ECX, 16,  1},
+        {"nodeid",       0x80000001, NA, CPUID_REG_ECX, 19,  1},
+        {"tbm",          0x80000001, NA, CPUID_REG_ECX, 21,  1},
+        {"topoext",      0x80000001, NA, CPUID_REG_ECX, 22,  1},
+        {"perfctr_core", 0x80000001, NA, CPUID_REG_ECX, 23,  1},
+        {"perfctr_nb",   0x80000001, NA, CPUID_REG_ECX, 24,  1},
+
+        {"syscall",      0x80000001, NA, CPUID_REG_EDX, 11,  1},
+        {"nx",           0x80000001, NA, CPUID_REG_EDX, 20,  1},
+        {"mmxext",       0x80000001, NA, CPUID_REG_EDX, 22,  1},
+        {"ffxsr",        0x80000001, NA, CPUID_REG_EDX, 25,  1},
+        {"page1gb",      0x80000001, NA, CPUID_REG_EDX, 26,  1},
+        {"rdtscp",       0x80000001, NA, CPUID_REG_EDX, 27,  1},
+        {"lm",           0x80000001, NA, CPUID_REG_EDX, 29,  1},
+        {"3dnowext",     0x80000001, NA, CPUID_REG_EDX, 30,  1},
+        {"3dnow",        0x80000001, NA, CPUID_REG_EDX, 31,  1},
+
+        {"procpkg",      0x00000004,  0, CPUID_REG_EAX, 26,  6},
+
+        {"invtsc",       0x80000007, NA, CPUID_REG_EDX,  8,  1},
+
+        {"clzero",       0x80000008, NA, CPUID_REG_EBX,  0,  1},
+        {"rstr-fp-err-ptrs", 0x80000008, NA, CPUID_REG_EBX, 2, 1},
+        {"wbnoinvd",     0x80000008, NA, CPUID_REG_EBX,  9,  1},
+        {"ibpb",         0x80000008, NA, CPUID_REG_EBX, 12,  1},
+        {"ppin",         0x80000008, NA, CPUID_REG_EBX, 23,  1},
+
+        {"nc",           0x80000008, NA, CPUID_REG_ECX,  0,  8},
+        {"apicidsize",   0x80000008, NA, CPUID_REG_ECX, 12,  4},
+
+        {"svm_npt",      0x8000000a, NA, CPUID_REG_EDX,  0,  1},
+        {"svm_lbrv",     0x8000000a, NA, CPUID_REG_EDX,  1,  1},
+        {"svm_nrips",    0x8000000a, NA, CPUID_REG_EDX,  3,  1},
+        {"svm_tscrate",  0x8000000a, NA, CPUID_REG_EDX,  4,  1},
+        {"svm_vmcbclean",0x8000000a, NA, CPUID_REG_EDX,  5,  1},
+        {"svm_decode",   0x8000000a, NA, CPUID_REG_EDX,  7,  1},
+        {"svm_pausefilt",0x8000000a, NA, CPUID_REG_EDX, 10,  1},
+
+        {"maxhvleaf",    0x40000000, NA, CPUID_REG_EAX,  0,  8},
+
+        {NULL, 0, NA, CPUID_REG_INV, 0, 0}
+    };
+#undef NA
+    char *sep, *val, *endptr;
+    int i;
+    const struct cpuid_flags *flag;
+    struct xc_xend_cpuid *entry;
+    unsigned long num;
+    char flags[33], *resstr;
+
+    sep = strchr(str, '=');
+    if (sep == NULL) {
+        return 1;
+    } else {
+        val = sep + 1;
+    }
+    for (flag = cpuid_flags; flag->name != NULL; flag++) {
+        if(!strncmp(str, flag->name, sep - str) && flag->name[sep - str] == 0)
+            break;
+    }
+    if (flag->name == NULL) {
+        return 2;
+    }
+    entry = cpuid_find_match(cpuid, flag->leaf, flag->subleaf);
+    resstr = entry->policy[flag->reg - 1];
+    num = strtoull(val, &endptr, 0);
+    flags[flag->length] = 0;
+    if (endptr != val) {
+        /* if this was a valid number, write the binary form into the string */
+        for (i = 0; i < flag->length; i++) {
+            flags[flag->length - 1 - i] = "01"[!!(num & (1 << i))];
+        }
+    } else {
+        switch(val[0]) {
+        case 'x': case 'k': case 's':
+            memset(flags, val[0], flag->length);
+            break;
+        default:
+            return 3;
+        }
+    }
+
+    if (resstr == NULL) {
+        resstr = strdup("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+    }
+
+    /* the family and model entry is potentially split up across
+     * two fields in Fn0000_0001_EAX, so handle them here separately.
+     */
+    if (!strncmp(str, "family", sep - str)) {
+        if (num < 16) {
+            memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
+            memcpy(resstr + (32 - 8) - 20, "00000000", 8);
+        } else {
+            num -= 15;
+            memcpy(resstr + (32 - 4) - flag->bit, "1111", 4);
+            for (i = 0; i < 7; i++) {
+                flags[7 - i] = "01"[num & 1];
+                num >>= 1;
+            }
+            memcpy(resstr + (32 - 8) - 20, flags, 8);
+        }
+    } else if (!strncmp(str, "model", sep - str)) {
+        memcpy(resstr + (32 - 4) - 16, flags, 4);
+        memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
+    } else {
+        memcpy(resstr + (32 - flag->length) - flag->bit, flags,
+               flag->length);
+    }
+    entry->policy[flag->reg - 1] = resstr;
+
+    return 0;
+}
+
+/* parse a single list item from the legacy Python xend syntax, where
+ * the strings for each register were directly exposed to the user.
+ * Used for maintaining compatibility with older config files
+ */
+int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
+                                  const char* str)
+{
+    char *endptr;
+    unsigned long value;
+    uint32_t leaf, subleaf = XEN_CPUID_INPUT_UNUSED;
+    struct xc_xend_cpuid *entry;
+
+    /* parse the leaf number */
+    value = strtoul(str, &endptr, 0);
+    if (str == endptr) {
+        return 1;
+    }
+    leaf = value;
+    /* check for an optional subleaf number */
+    if (*endptr == ',') {
+        str = endptr + 1;
+        value = strtoul(str, &endptr, 0);
+        if (str == endptr) {
+            return 2;
+        }
+        subleaf = value;
+    }
+    if (*endptr != ':') {
+        return 3;
+    }
+    str = endptr + 1;
+    entry = cpuid_find_match(cpuid, leaf, subleaf);
+    for (str = endptr + 1; *str != 0;) {
+        if (str[0] != 'e' || str[2] != 'x') {
+            return 4;
+        }
+        value = str[1] - 'a';
+        endptr = strchr(str, '=');
+        if (value > 3 || endptr == NULL) {
+            return 4;
+        }
+        str = endptr + 1;
+        endptr = strchr(str, ',');
+        if (endptr == NULL) {
+            endptr = strchr(str, 0);
+        }
+        if (endptr - str != 32) {
+            return 5;
+        }
+        entry->policy[value] = calloc(32 + 1, 1);
+        strncpy(entry->policy[value], str, 32);
+        entry->policy[value][32] = 0;
+        if (*endptr == 0) {
+            break;
+        }
+        for (str = endptr + 1; *str == ' ' || *str == '\n'; str++);
+    }
+    return 0;
+}
+
+void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
+                         libxl_domain_build_info *info)
+{
+    bool pae = true;
+    bool itsc;
+    bool nested_virt = libxl_defbool_val(info->nested_hvm);
+
+    /*
+     * For PV guests, PAE is Xen-controlled (it is the 'p' that differentiates
+     * the xen-3.0-x86_32 and xen-3.0-x86_32p ABIs).  It is mandatory as Xen
+     * is 64bit only these days.
+     *
+     * For PVH guests, there is no top-level PAE control in the domain config,
+     * so is treated as always available.
+     *
+     * HVM guests get a top-level choice of whether PAE is available.
+     */
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM)
+        pae = libxl_defbool_val(info->u.hvm.pae);
+
+    /*
+     * Advertising Invariant TSC to a guest means that the TSC frequency won't
+     * change at any point in the future.
+     *
+     * We do not have enough information about potential migration
+     * destinations to know whether advertising ITSC is safe, but if the guest
+     * isn't going to migrate, then the current hardware is all that matters.
+     *
+     * Alternatively, an internal property of vTSC is that the values read are
+     * invariant.  Advertise ITSC when we know the domain will have emualted
+     * TSC everywhere it goes.
+     */
+    itsc = (libxl_defbool_val(info->disable_migrate) ||
+            info->tsc_mode == LIBXL_TSC_MODE_ALWAYS_EMULATE);
+
+    xc_cpuid_apply_policy(ctx->xch, domid, restore, NULL, 0,
+                          pae, itsc, nested_virt, info->cpuid);
+}
+
+static const char *input_names[2] = { "leaf", "subleaf" };
+static const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" };
+/*
+ * Aiming for:
+ * [
+ *     { 'leaf':    'val-eax',
+ *       'subleaf': 'val-ecx',
+ *       'eax':     'filter',
+ *       'ebx':     'filter',
+ *       'ecx':     'filter',
+ *       'edx':     'filter' },
+ *     { 'leaf':    'val-eax', ..., 'eax': 'filter', ... },
+ *     ... etc ...
+ * ]
+ */
+
+yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
+                                libxl_cpuid_policy_list *pcpuid)
+{
+    libxl_cpuid_policy_list cpuid = *pcpuid;
+    yajl_gen_status s;
+    int i, j;
+
+    s = yajl_gen_array_open(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    if (cpuid == NULL) goto empty;
+
+    for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+        s = yajl_gen_map_open(hand);
+        if (s != yajl_gen_status_ok) goto out;
+
+        for (j = 0; j < 2; j++) {
+            if (cpuid[i].input[j] != XEN_CPUID_INPUT_UNUSED) {
+                s = libxl__yajl_gen_asciiz(hand, input_names[j]);
+                if (s != yajl_gen_status_ok) goto out;
+                s = yajl_gen_integer(hand, cpuid[i].input[j]);
+                if (s != yajl_gen_status_ok) goto out;
+            }
+        }
+
+        for (j = 0; j < 4; j++) {
+            if (cpuid[i].policy[j] != NULL) {
+                s = libxl__yajl_gen_asciiz(hand, policy_names[j]);
+                if (s != yajl_gen_status_ok) goto out;
+                s = yajl_gen_string(hand,
+                               (const unsigned char *)cpuid[i].policy[j], 32);
+                if (s != yajl_gen_status_ok) goto out;
+            }
+        }
+        s = yajl_gen_map_close(hand);
+        if (s != yajl_gen_status_ok) goto out;
+    }
+
+empty:
+    s = yajl_gen_array_close(hand);
+out:
+    return s;
+}
+
+int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
+                                        const libxl__json_object *o,
+                                        libxl_cpuid_policy_list *p)
+{
+    int i, size;
+    libxl_cpuid_policy_list l;
+    flexarray_t *array;
+
+    if (!libxl__json_object_is_array(o))
+        return ERROR_FAIL;
+
+    array = libxl__json_object_get_array(o);
+    if (!array->count)
+        return 0;
+
+    size = array->count;
+    /* need one extra slot as sentinel */
+    l = *p = libxl__calloc(NOGC, size + 1, sizeof(libxl_cpuid_policy));
+
+    l[size].input[0] = XEN_CPUID_INPUT_UNUSED;
+    l[size].input[1] = XEN_CPUID_INPUT_UNUSED;
+
+    for (i = 0; i < size; i++) {
+        const libxl__json_object *t;
+        int j;
+
+        if (flexarray_get(array, i, (void**)&t) != 0)
+            return ERROR_FAIL;
+
+        if (!libxl__json_object_is_map(t))
+            return ERROR_FAIL;
+
+        for (j = 0; j < ARRAY_SIZE(l[0].input); j++) {
+            const libxl__json_object *r;
+
+            r = libxl__json_map_get(input_names[j], t, JSON_INTEGER);
+            if (!r)
+                l[i].input[j] = XEN_CPUID_INPUT_UNUSED;
+            else
+                l[i].input[j] = libxl__json_object_get_integer(r);
+        }
+
+        for (j = 0; j < ARRAY_SIZE(l[0].policy); j++) {
+            const libxl__json_object *r;
+
+            r = libxl__json_map_get(policy_names[j], t, JSON_STRING);
+            if (!r)
+                l[i].policy[j] = NULL;
+            else
+                l[i].policy[j] =
+                    libxl__strdup(NOGC, libxl__json_object_get_string(r));
+        }
+    }
+
+    return 0;
+}
+
+int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *pl)
+{
+    int i = 0;
+    libxl_cpuid_policy_list l = *pl;
+
+    if (l) {
+        while (l[i].input[0] != XEN_CPUID_INPUT_UNUSED)
+            i++;
+    }
+
+    return i;
+}
+
+void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
+                                  libxl_cpuid_policy_list *dst,
+                                  const libxl_cpuid_policy_list *src)
+{
+    GC_INIT(ctx);
+    int i, j, len;
+
+    if (*src == NULL) {
+        *dst = NULL;
+        goto out;
+    }
+
+    len = libxl_cpuid_policy_list_length(src);
+    /* one extra slot for sentinel */
+    *dst = libxl__calloc(NOGC, len + 1, sizeof(libxl_cpuid_policy));
+    (*dst)[len].input[0] = XEN_CPUID_INPUT_UNUSED;
+    (*dst)[len].input[1] = XEN_CPUID_INPUT_UNUSED;
+
+    for (i = 0; i < len; i++) {
+        for (j = 0; j < 2; j++)
+            (*dst)[i].input[j] = (*src)[i].input[j];
+        for (j = 0; j < 4; j++)
+            if ((*src)[i].policy[j])
+                (*dst)[i].policy[j] =
+                    libxl__strdup(NOGC, (*src)[i].policy[j]);
+            else
+                (*dst)[i].policy[j] = NULL;
+    }
+
+out:
+    GC_FREE;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_cpupool.c b/tools/libs/light/libxl_cpupool.c
new file mode 100644 (file)
index 0000000..85b0688
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/* Returns:
+ *   0 - success
+ *   ERROR_FAIL + errno == ENOENT - no entry found
+ *   ERROR_$FOO + errno != ENOENT - other failure
+ */
+static int cpupool_info(libxl__gc *gc,
+                        libxl_cpupoolinfo *info,
+                        uint32_t poolid,
+                        bool exact /* exactly poolid or >= poolid */)
+{
+    xc_cpupoolinfo_t *xcinfo;
+    int rc = ERROR_FAIL;
+
+    xcinfo = xc_cpupool_getinfo(CTX->xch, poolid);
+    if (xcinfo == NULL)
+    {
+        if (exact || errno != ENOENT)
+            LOGE(ERROR, "failed to get info for cpupool%d", poolid);
+        return ERROR_FAIL;
+    }
+
+    if (exact && xcinfo->cpupool_id != poolid)
+    {
+        LOG(ERROR, "got info for cpupool%d, wanted cpupool%d\n",
+            xcinfo->cpupool_id, poolid);
+        goto out;
+    }
+
+    info->poolid = xcinfo->cpupool_id;
+    info->pool_name = libxl_cpupoolid_to_name(CTX, info->poolid);
+    if (!info->pool_name) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    info->sched = xcinfo->sched_id;
+    info->n_dom = xcinfo->n_dom;
+    rc = libxl_cpu_bitmap_alloc(CTX, &info->cpumap, 0);
+    if (rc)
+        goto out;
+
+    memcpy(info->cpumap.map, xcinfo->cpumap, info->cpumap.size);
+
+    rc = 0;
+out:
+    xc_cpupool_infofree(CTX->xch, xcinfo);
+    return rc;
+}
+
+int libxl_cpupool_info(libxl_ctx *ctx,
+                       libxl_cpupoolinfo *info, uint32_t poolid)
+{
+    GC_INIT(ctx);
+    int rc = cpupool_info(gc, info, poolid, true);
+    GC_FREE;
+    return rc;
+}
+
+libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx *ctx, int *nb_pool_out)
+{
+    GC_INIT(ctx);
+    libxl_cpupoolinfo info, *ptr;
+
+    int i;
+    uint32_t poolid;
+
+    ptr = NULL;
+
+    poolid = 0;
+    for (i = 0;; i++) {
+        libxl_cpupoolinfo_init(&info);
+        if (cpupool_info(gc, &info, poolid, false)) {
+            libxl_cpupoolinfo_dispose(&info);
+            if (errno != ENOENT) goto out;
+            break;
+        }
+
+        ptr = libxl__realloc(NOGC, ptr, (i+1) * sizeof(libxl_cpupoolinfo));
+        ptr[i] = info;
+        poolid = info.poolid + 1;
+        /* Don't dispose of info because it will be returned to caller */
+    }
+
+    *nb_pool_out = i;
+
+    GC_FREE;
+    return ptr;
+
+out:
+    libxl_cpupoolinfo_list_free(ptr, i);
+    *nb_pool_out = 0;
+    GC_FREE;
+    return NULL;
+}
+
+int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap)
+{
+    int ncpus;
+
+    ncpus = libxl_get_max_cpus(ctx);
+    if (ncpus < 0)
+        return ncpus;
+
+    cpumap->map = xc_cpupool_freeinfo(ctx->xch);
+    if (cpumap->map == NULL)
+        return ERROR_FAIL;
+
+    cpumap->size = (ncpus + 7) / 8;
+
+    return 0;
+}
+
+int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
+                         libxl_scheduler sched,
+                         libxl_bitmap cpumap, libxl_uuid *uuid,
+                         uint32_t *poolid)
+{
+    GC_INIT(ctx);
+    int rc;
+    int i;
+    xs_transaction_t t;
+    char *uuid_string;
+    uint32_t xcpoolid;
+
+    /* Accept '0' as 'any poolid' for backwards compatibility */
+    if ( *poolid == LIBXL_CPUPOOL_POOLID_ANY
+         || *poolid == 0 )
+        xcpoolid = XC_CPUPOOL_POOLID_ANY;
+    else
+        xcpoolid = *poolid;
+
+    uuid_string = libxl__uuid2string(gc, *uuid);
+    if (!uuid_string) {
+        GC_FREE;
+        return ERROR_NOMEM;
+    }
+
+    rc = xc_cpupool_create(ctx->xch, &xcpoolid, sched);
+    if (rc) {
+        LOGEV(ERROR, rc, "Could not create cpupool");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    *poolid = xcpoolid;
+
+    libxl_for_each_bit(i, cpumap)
+        if (libxl_bitmap_test(&cpumap, i)) {
+            rc = xc_cpupool_addcpu(ctx->xch, *poolid, i);
+            if (rc) {
+                LOGEV(ERROR, rc, "Error moving cpu to cpupool");
+                libxl_cpupool_destroy(ctx, *poolid);
+                GC_FREE;
+                return ERROR_FAIL;
+            }
+        }
+
+    for (;;) {
+        t = xs_transaction_start(ctx->xsh);
+
+        xs_mkdir(ctx->xsh, t, GCSPRINTF("/local/pool/%d", *poolid));
+        libxl__xs_printf(gc, t,
+                         GCSPRINTF("/local/pool/%d/uuid", *poolid),
+                         "%s", uuid_string);
+        libxl__xs_printf(gc, t,
+                         GCSPRINTF("/local/pool/%d/name", *poolid),
+                         "%s", name);
+
+        if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN)) {
+            GC_FREE;
+            return 0;
+        }
+    }
+}
+
+int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid)
+{
+    GC_INIT(ctx);
+    int rc, i;
+    xc_cpupoolinfo_t *info;
+    xs_transaction_t t;
+    libxl_bitmap cpumap;
+
+    info = xc_cpupool_getinfo(ctx->xch, poolid);
+    if (info == NULL) {
+        GC_FREE;
+        return ERROR_NOMEM;
+    }
+
+    rc = ERROR_INVAL;
+    if ((info->cpupool_id != poolid) || (info->n_dom))
+        goto out;
+
+    rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, 0);
+    if (rc)
+        goto out;
+
+    memcpy(cpumap.map, info->cpumap, cpumap.size);
+    libxl_for_each_bit(i, cpumap)
+        if (libxl_bitmap_test(&cpumap, i)) {
+            rc = xc_cpupool_removecpu(ctx->xch, poolid, i);
+            if (rc) {
+                LOGEV(ERROR, rc, "Error removing cpu from cpupool");
+                rc = ERROR_FAIL;
+                goto out1;
+            }
+        }
+
+    rc = xc_cpupool_destroy(ctx->xch, poolid);
+    if (rc) {
+        LOGEV(ERROR, rc, "Could not destroy cpupool");
+        rc = ERROR_FAIL;
+        goto out1;
+    }
+
+    for (;;) {
+        t = xs_transaction_start(ctx->xsh);
+
+        xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF("/local/pool/%d", poolid));
+
+        if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN))
+            break;
+    }
+
+    rc = 0;
+
+out1:
+    libxl_bitmap_dispose(&cpumap);
+out:
+    xc_cpupool_infofree(ctx->xch, info);
+    GC_FREE;
+
+    return rc;
+}
+
+int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid)
+{
+    GC_INIT(ctx);
+    xs_transaction_t t;
+    xc_cpupoolinfo_t *info;
+    int rc;
+
+    info = xc_cpupool_getinfo(ctx->xch, poolid);
+    if (info == NULL) {
+        GC_FREE;
+        return ERROR_NOMEM;
+    }
+
+    rc = ERROR_INVAL;
+    if (info->cpupool_id != poolid)
+        goto out;
+
+    rc = 0;
+
+    for (;;) {
+        t = xs_transaction_start(ctx->xsh);
+
+        libxl__xs_printf(gc, t,
+                         GCSPRINTF("/local/pool/%d/name", poolid),
+                         "%s", name);
+
+        if (xs_transaction_end(ctx->xsh, t, 0))
+            break;
+
+        if (errno == EAGAIN)
+            continue;
+
+        rc = ERROR_FAIL;
+        break;
+    }
+
+out:
+    xc_cpupool_infofree(ctx->xch, info);
+    GC_FREE;
+
+    return rc;
+}
+
+int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu)
+{
+    GC_INIT(ctx);
+    int rc = 0;
+
+    rc = xc_cpupool_addcpu(ctx->xch, poolid, cpu);
+    if (rc) {
+        LOGE(ERROR, "Error moving cpu %d to cpupool", cpu);
+        rc = ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
+                                const libxl_bitmap *cpumap)
+{
+    int c, ncpus = 0, rc = 0;
+
+    libxl_for_each_set_bit(c, *cpumap) {
+        if (!libxl_cpupool_cpuadd(ctx, poolid, c))
+            ncpus++;
+    }
+
+    if (ncpus != libxl_bitmap_count_set(cpumap))
+        rc = ERROR_FAIL;
+
+    return rc;
+}
+
+int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
+{
+    int rc = 0;
+    int cpu, nr;
+    libxl_bitmap freemap;
+    libxl_cputopology *topology;
+
+    if (libxl_get_freecpus(ctx, &freemap)) {
+        return ERROR_FAIL;
+    }
+
+    topology = libxl_get_cpu_topology(ctx, &nr);
+    if (!topology) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    *cpus = 0;
+    for (cpu = 0; cpu < nr; cpu++) {
+        if (libxl_bitmap_test(&freemap, cpu) && (topology[cpu].node == node) &&
+            !libxl_cpupool_cpuadd(ctx, poolid, cpu)) {
+                (*cpus)++;
+        }
+        libxl_cputopology_dispose(&topology[cpu]);
+    }
+
+    free(topology);
+out:
+    libxl_bitmap_dispose(&freemap);
+    return rc;
+}
+
+int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu)
+{
+    GC_INIT(ctx);
+    int rc = 0;
+
+    rc = xc_cpupool_removecpu(ctx->xch, poolid, cpu);
+    if (rc) {
+        LOGE(ERROR, "Error removing cpu %d from cpupool", cpu);
+        rc = ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
+                                   const libxl_bitmap *cpumap)
+{
+    int c, ncpus = 0, rc = 0;
+
+    libxl_for_each_set_bit(c, *cpumap) {
+        if (!libxl_cpupool_cpuremove(ctx, poolid, c))
+            ncpus++;
+    }
+
+    if (ncpus != libxl_bitmap_count_set(cpumap))
+        rc = ERROR_FAIL;
+
+    return rc;
+}
+
+int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
+{
+    int ret = 0;
+    int n_pools;
+    int p;
+    int cpu, nr_cpus;
+    libxl_cputopology *topology;
+    libxl_cpupoolinfo *poolinfo;
+
+    poolinfo = libxl_list_cpupool(ctx, &n_pools);
+    if (!poolinfo) {
+        return ERROR_NOMEM;
+    }
+
+    topology = libxl_get_cpu_topology(ctx, &nr_cpus);
+    if (!topology) {
+        ret = ERROR_FAIL;
+        goto out;
+    }
+
+    *cpus = 0;
+    for (p = 0; p < n_pools; p++) {
+        if (poolinfo[p].poolid == poolid) {
+            for (cpu = 0; cpu < nr_cpus; cpu++) {
+                if ((topology[cpu].node == node) &&
+                    libxl_bitmap_test(&poolinfo[p].cpumap, cpu) &&
+                    !libxl_cpupool_cpuremove(ctx, poolid, cpu)) {
+                        (*cpus)++;
+                }
+            }
+        }
+    }
+
+    libxl_cputopology_list_free(topology, nr_cpus);
+
+out:
+    libxl_cpupoolinfo_list_free(poolinfo, n_pools);
+
+    return ret;
+}
+
+int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = xc_cpupool_movedomain(ctx->xch, poolid, domid);
+    if (rc) {
+        LOGEVD(ERROR, rc, domid, "Error moving domain to cpupool");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_create.c b/tools/libs/light/libxl_create.c
new file mode 100644 (file)
index 0000000..1031b75
--- /dev/null
@@ -0,0 +1,2310 @@
+/*
+ * Copyright (C) 2010      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ * Author Gianni Tedesco <gianni.tedesco@citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+#include <xenguest.h>
+#include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/e820.h>
+
+#include <xen-xsm/flask/flask.h>
+
+int libxl__domain_create_info_setdefault(libxl__gc *gc,
+                                         libxl_domain_create_info *c_info,
+                                         const libxl_physinfo *info)
+{
+    if (!c_info->type) {
+        LOG(ERROR, "domain type unspecified");
+        return ERROR_INVAL;
+    }
+
+    libxl__arch_domain_create_info_setdefault(gc, c_info);
+
+    if (c_info->type != LIBXL_DOMAIN_TYPE_PV) {
+        if (info->cap_hap) {
+            libxl_defbool_setdefault(&c_info->hap, true);
+        } else if (info->cap_shadow) {
+            libxl_defbool_setdefault(&c_info->hap, false);
+        } else {
+            LOG(ERROR, "neither hap nor shadow paging available");
+            return ERROR_INVAL;
+        }
+
+        libxl_defbool_setdefault(&c_info->oos, true);
+    }
+
+    libxl_defbool_setdefault(&c_info->run_hotplug_scripts, true);
+    libxl_defbool_setdefault(&c_info->driver_domain, false);
+
+    if (!c_info->ssidref)
+        c_info->ssidref = SECINITSID_DOMU;
+
+    libxl_defbool_setdefault(&c_info->xend_suspend_evtchn_compat, false);
+
+    return 0;
+}
+
+void libxl__rdm_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info)
+{
+    if (b_info->u.hvm.rdm.policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
+        b_info->u.hvm.rdm.policy = LIBXL_RDM_RESERVE_POLICY_RELAXED;
+
+    if (b_info->u.hvm.rdm_mem_boundary_memkb == LIBXL_MEMKB_DEFAULT)
+        b_info->u.hvm.rdm_mem_boundary_memkb =
+                            LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT;
+}
+
+int libxl__domain_build_info_setdefault(libxl__gc *gc,
+                                        libxl_domain_build_info *b_info)
+{
+    int i, rc;
+
+    if (b_info->type != LIBXL_DOMAIN_TYPE_HVM &&
+        b_info->type != LIBXL_DOMAIN_TYPE_PV &&
+        b_info->type != LIBXL_DOMAIN_TYPE_PVH) {
+        LOG(ERROR, "invalid domain type");
+        return ERROR_INVAL;
+    }
+
+    /* Copy deprecated options to it's new position. */
+    rc = libxl__domain_build_info_copy_deprecated(CTX, b_info);
+    if (rc) {
+        LOG(ERROR, "Unable to copy deprecated fields");
+        return rc;
+    }
+
+    libxl_defbool_setdefault(&b_info->device_model_stubdomain, false);
+
+    if (libxl_defbool_val(b_info->device_model_stubdomain) &&
+        !b_info->device_model_ssidref)
+        b_info->device_model_ssidref = SECINITSID_DOMDM;
+
+    if (!b_info->device_model_version) {
+        if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+            if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+                b_info->device_model_version =
+                    LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+            } else {
+                b_info->device_model_version = libxl__default_device_model(gc);
+            }
+        } else {
+            b_info->device_model_version =
+                LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+        }
+        if (b_info->device_model_version
+                == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+            const char *dm;
+
+            dm = libxl__domain_device_model(gc, b_info);
+            rc = access(dm, X_OK);
+            if (rc < 0) {
+                /* qemu-xen unavailable, use qemu-xen-traditional */
+                if (errno == ENOENT) {
+                    LOGE(INFO, "qemu-xen is unavailable"
+                         ", using qemu-xen-traditional instead");
+                    b_info->device_model_version =
+                        LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+                } else {
+                    LOGE(ERROR, "qemu-xen access error");
+                    return ERROR_FAIL;
+                }
+            }
+        }
+    }
+
+    if (b_info->blkdev_start == NULL)
+        b_info->blkdev_start = libxl__strdup(NOGC, "xvda");
+
+    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        if (!b_info->u.hvm.bios)
+            switch (b_info->device_model_version) {
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+                b_info->u.hvm.bios = LIBXL_BIOS_TYPE_ROMBIOS; break;
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+                b_info->u.hvm.bios = LIBXL_BIOS_TYPE_SEABIOS; break;
+            default:
+                LOG(ERROR, "unknown device model version");
+                return ERROR_INVAL;
+            }
+
+        /* Enforce BIOS<->Device Model version relationship */
+        switch (b_info->device_model_version) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            if (b_info->u.hvm.bios != LIBXL_BIOS_TYPE_ROMBIOS) {
+                LOG(ERROR, "qemu-xen-traditional requires bios=rombios.");
+                return ERROR_INVAL;
+            }
+            break;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            if (b_info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS) {
+                LOG(ERROR, "qemu-xen does not support bios=rombios.");
+                return ERROR_INVAL;
+            }
+            break;
+        default:abort();
+        }
+
+        /* Check HVM direct boot parameters, we should honour ->ramdisk and
+         * ->cmdline iff ->kernel is set.
+         */
+        if (!b_info->kernel && (b_info->ramdisk || b_info->cmdline)) {
+            LOG(ERROR, "direct boot parameters specified but kernel missing");
+            return ERROR_INVAL;
+        }
+    }
+
+    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        libxl_defbool_val(b_info->device_model_stubdomain)) {
+        if (!b_info->stubdomain_kernel) {
+            switch (b_info->device_model_version) {
+                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+                    b_info->stubdomain_kernel =
+                        libxl__abs_path(NOGC, "ioemu-stubdom.gz", libxl__xenfirmwaredir_path());
+                    break;
+                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+                    b_info->stubdomain_kernel =
+                        libxl__abs_path(NOGC,
+                                "qemu-stubdom-linux-kernel",
+                                libxl__xenfirmwaredir_path());
+                    break;
+                default:
+                    abort();
+            }
+        }
+        if (!b_info->stubdomain_ramdisk) {
+            switch (b_info->device_model_version) {
+                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+                    break;
+                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+                    b_info->stubdomain_ramdisk =
+                        libxl__abs_path(NOGC,
+                                "qemu-stubdom-linux-rootfs",
+                                libxl__xenfirmwaredir_path());
+                    break;
+                default:
+                    abort();
+            }
+        }
+    }
+
+    if (!b_info->max_vcpus)
+        b_info->max_vcpus = 1;
+    if (!b_info->avail_vcpus.size) {
+        if (libxl_cpu_bitmap_alloc(CTX, &b_info->avail_vcpus, 1)) {
+            LOG(ERROR, "unable to allocate avail_vcpus bitmap");
+            return ERROR_FAIL;
+        }
+        libxl_bitmap_set(&b_info->avail_vcpus, 0);
+    } else if (b_info->avail_vcpus.size > HVM_MAX_VCPUS) {
+        LOG(ERROR, "avail_vcpus bitmap contains too many VCPUS");
+        return ERROR_FAIL;
+    }
+
+    /* In libxl internals, we want to deal with vcpu_hard_affinity only! */
+    if (b_info->cpumap.size && !b_info->num_vcpu_hard_affinity) {
+        b_info->vcpu_hard_affinity = libxl__calloc(gc, b_info->max_vcpus,
+                                                   sizeof(libxl_bitmap));
+        for (i = 0; i < b_info->max_vcpus; i++) {
+            if (libxl_cpu_bitmap_alloc(CTX, &b_info->vcpu_hard_affinity[i], 0)) {
+                LOG(ERROR, "failed to allocate vcpu hard affinity bitmap");
+                return ERROR_FAIL;
+            }
+            libxl_bitmap_copy(CTX, &b_info->vcpu_hard_affinity[i],
+                              &b_info->cpumap);
+        }
+        b_info->num_vcpu_hard_affinity = b_info->max_vcpus;
+    }
+
+    libxl_defbool_setdefault(&b_info->numa_placement, true);
+
+    if (b_info->max_memkb == LIBXL_MEMKB_DEFAULT)
+        b_info->max_memkb = 32 * 1024;
+    if (b_info->target_memkb == LIBXL_MEMKB_DEFAULT)
+        b_info->target_memkb = b_info->max_memkb;
+
+    if (b_info->stubdomain_memkb == LIBXL_MEMKB_DEFAULT) {
+        if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+            if (libxl__stubdomain_is_linux(b_info))
+                b_info->stubdomain_memkb = LIBXL_LINUX_STUBDOM_MEM * 1024;
+            else
+                b_info->stubdomain_memkb = 28 * 1024; // MiniOS
+        } else {
+            b_info->stubdomain_memkb = 0; // no stubdomain
+        }
+    }
+
+    libxl_defbool_setdefault(&b_info->claim_mode, false);
+
+    libxl_defbool_setdefault(&b_info->localtime, false);
+
+    libxl_defbool_setdefault(&b_info->disable_migrate, false);
+
+    for (i = 0 ; i < b_info->num_iomem; i++)
+        if (b_info->iomem[i].gfn == LIBXL_INVALID_GFN)
+            b_info->iomem[i].gfn = b_info->iomem[i].start;
+
+    if (!b_info->event_channels)
+        b_info->event_channels = 1023;
+
+    libxl__arch_domain_build_info_setdefault(gc, b_info);
+    libxl_defbool_setdefault(&b_info->dm_restrict, false);
+
+    if (b_info->iommu_memkb == LIBXL_MEMKB_DEFAULT)
+        /* Normally defaulted in libxl__domain_create_info_setdefault */
+        b_info->iommu_memkb = 0;
+
+    switch (b_info->type) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
+            /* Normally defaulted in libxl__domain_create_info_setdefault */
+            b_info->shadow_memkb = 0;
+        if (b_info->u.hvm.mmio_hole_memkb == LIBXL_MEMKB_DEFAULT)
+            b_info->u.hvm.mmio_hole_memkb = 0;
+
+        if (b_info->u.hvm.vga.kind == LIBXL_VGA_INTERFACE_TYPE_UNKNOWN) {
+            b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
+        }
+
+        if (!b_info->u.hvm.hdtype)
+            b_info->u.hvm.hdtype = LIBXL_HDTYPE_IDE;
+
+        switch (b_info->device_model_version) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            switch (b_info->u.hvm.vga.kind) {
+            case LIBXL_VGA_INTERFACE_TYPE_NONE:
+                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+                    b_info->video_memkb = 0;
+                break;
+            case LIBXL_VGA_INTERFACE_TYPE_QXL:
+                LOG(ERROR,"qemu upstream required for qxl vga");
+                return ERROR_INVAL;
+                break;
+            case LIBXL_VGA_INTERFACE_TYPE_STD:
+                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+                    b_info->video_memkb = 8 * 1024;
+                if (b_info->video_memkb < 8 * 1024) {
+                    LOG(ERROR, "videoram must be at least 8 MB for STDVGA on QEMU_XEN_TRADITIONAL");
+                    return ERROR_INVAL;
+                }
+                break;
+            case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+            default:
+                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+                    b_info->video_memkb = 4 * 1024;
+                if (b_info->video_memkb != 4 * 1024)
+                    LOG(WARN, "ignoring videoram other than 4 MB for CIRRUS on QEMU_XEN_TRADITIONAL");
+                break;
+            }
+            break;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+        default:
+            switch (b_info->u.hvm.vga.kind) {
+            case LIBXL_VGA_INTERFACE_TYPE_NONE:
+                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+                    b_info->video_memkb = 0;
+                break;
+            case LIBXL_VGA_INTERFACE_TYPE_QXL:
+                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) {
+                    b_info->video_memkb = (128 * 1024);
+                } else if (b_info->video_memkb < (128 * 1024)) {
+                    LOG(ERROR,
+                        "128 Mib videoram is the minimum for qxl default");
+                    return ERROR_INVAL;
+                }
+                break;
+            case LIBXL_VGA_INTERFACE_TYPE_STD:
+                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+                    b_info->video_memkb = 16 * 1024;
+                if (b_info->video_memkb < 16 * 1024) {
+                    LOG(ERROR, "videoram must be at least 16 MB for STDVGA on QEMU_XEN");
+                    return ERROR_INVAL;
+                }
+                break;
+            case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+            default:
+                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+                    b_info->video_memkb = 8 * 1024;
+                if (b_info->video_memkb < 8 * 1024) {
+                    LOG(ERROR, "videoram must be at least 8 MB for CIRRUS on QEMU_XEN");
+                    return ERROR_INVAL;
+                }
+                break;
+            }
+            break;
+        }
+
+        libxl_defbool_setdefault(&b_info->u.hvm.pae,                true);
+        libxl_defbool_setdefault(&b_info->u.hvm.acpi,               true);
+        libxl_defbool_setdefault(&b_info->u.hvm.acpi_s3,            true);
+        libxl_defbool_setdefault(&b_info->u.hvm.acpi_s4,            true);
+        libxl_defbool_setdefault(&b_info->u.hvm.acpi_laptop_slate,  false);
+        libxl_defbool_setdefault(&b_info->u.hvm.nx,                 true);
+        libxl_defbool_setdefault(&b_info->u.hvm.viridian,           false);
+        libxl_defbool_setdefault(&b_info->u.hvm.hpet,               true);
+        libxl_defbool_setdefault(&b_info->u.hvm.vpt_align,          true);
+        libxl_defbool_setdefault(&b_info->u.hvm.altp2m,             false);
+        libxl_defbool_setdefault(&b_info->u.hvm.usb,                false);
+        libxl_defbool_setdefault(&b_info->u.hvm.vkb_device,         true);
+        libxl_defbool_setdefault(&b_info->u.hvm.xen_platform_pci,   true);
+
+        libxl_defbool_setdefault(&b_info->u.hvm.spice.enable, false);
+        if (!libxl_defbool_val(b_info->u.hvm.spice.enable) &&
+            (b_info->u.hvm.spice.usbredirection > 0) ){
+            b_info->u.hvm.spice.usbredirection = 0;
+            LOG(WARN, "spice disabled, disabling usbredirection");
+        }
+
+        if (!b_info->u.hvm.usbversion &&
+            (b_info->u.hvm.spice.usbredirection > 0) )
+            b_info->u.hvm.usbversion = 2;
+
+        if ((b_info->u.hvm.usbversion || b_info->u.hvm.spice.usbredirection) &&
+            ( libxl_defbool_val(b_info->u.hvm.usb)
+            || b_info->u.hvm.usbdevice_list
+            || b_info->u.hvm.usbdevice) ){
+            LOG(ERROR,"usbversion and/or usbredirection cannot be "
+            "enabled with usb and/or usbdevice parameters.");
+            return ERROR_INVAL;
+        }
+
+        if (!b_info->u.hvm.boot)
+            b_info->u.hvm.boot = libxl__strdup(NOGC, "cda");
+
+        libxl_defbool_setdefault(&b_info->u.hvm.vnc.enable, true);
+        if (libxl_defbool_val(b_info->u.hvm.vnc.enable)) {
+            libxl_defbool_setdefault(&b_info->u.hvm.vnc.findunused, true);
+            if (!b_info->u.hvm.vnc.listen)
+                b_info->u.hvm.vnc.listen = libxl__strdup(NOGC, "127.0.0.1");
+        }
+
+        libxl_defbool_setdefault(&b_info->u.hvm.sdl.enable, false);
+        if (libxl_defbool_val(b_info->u.hvm.sdl.enable)) {
+            libxl_defbool_setdefault(&b_info->u.hvm.sdl.opengl, false);
+        }
+
+        if (libxl_defbool_val(b_info->u.hvm.spice.enable)) {
+            libxl_defbool_setdefault(&b_info->u.hvm.spice.disable_ticketing,
+                                     false);
+            libxl_defbool_setdefault(&b_info->u.hvm.spice.agent_mouse, true);
+            libxl_defbool_setdefault(&b_info->u.hvm.spice.vdagent, false);
+            libxl_defbool_setdefault(&b_info->u.hvm.spice.clipboard_sharing,
+                                     false);
+        }
+
+        libxl_defbool_setdefault(&b_info->u.hvm.nographic, false);
+
+        libxl_defbool_setdefault(&b_info->u.hvm.gfx_passthru, false);
+
+        libxl__rdm_setdefault(gc, b_info);
+        break;
+    case LIBXL_DOMAIN_TYPE_PV:
+        libxl_defbool_setdefault(&b_info->u.pv.e820_host, false);
+        if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
+            b_info->video_memkb = 0;
+        if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
+            /* Normally defaulted in libxl__domain_create_info_setdefault */
+            b_info->shadow_memkb = 0;
+        if (b_info->u.pv.slack_memkb == LIBXL_MEMKB_DEFAULT)
+            b_info->u.pv.slack_memkb = 0;
+        break;
+    case LIBXL_DOMAIN_TYPE_PVH:
+        libxl_defbool_setdefault(&b_info->u.pvh.pvshim, false);
+        if (libxl_defbool_val(b_info->u.pvh.pvshim)) {
+            if (!b_info->u.pvh.pvshim_path)
+                b_info->u.pvh.pvshim_path =
+                    libxl__sprintf(NOGC, "%s/%s",
+                                   libxl__xenfirmwaredir_path(),
+                                   PVSHIM_BASENAME);
+            if (!b_info->u.pvh.pvshim_cmdline)
+                b_info->u.pvh.pvshim_cmdline =
+                    libxl__strdup(NOGC, PVSHIM_CMDLINE);
+        }
+
+        break;
+    default:
+        LOG(ERROR, "invalid domain type %s in create info",
+            libxl_domain_type_to_string(b_info->type));
+        return ERROR_INVAL;
+    }
+
+    /* Configuration fields shared between PVH and HVM. */
+    if (b_info->type != LIBXL_DOMAIN_TYPE_PV) {
+        if (libxl__timer_mode_is_default(&b_info->timer_mode))
+            b_info->timer_mode = LIBXL_TIMER_MODE_NO_DELAY_FOR_MISSED_TICKS;
+
+        libxl_defbool_setdefault(&b_info->apic,                     true);
+        libxl_defbool_setdefault(&b_info->nested_hvm,               false);
+    }
+
+    return 0;
+}
+
+static void init_console_info(libxl__gc *gc,
+                             libxl__device_console *console,
+                             int dev_num)
+{
+    libxl__device_console_init(console);
+    console->devid = dev_num;
+    console->consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
+    console->output = libxl__strdup(NOGC, "pty");
+    /* console->{name,connection,path} are NULL on normal consoles.
+       Only 'channels' when mapped to consoles have a string name. */
+}
+
+int libxl__domain_build(libxl__gc *gc,
+                        libxl_domain_config *d_config,
+                        uint32_t domid,
+                        libxl__domain_build_state *state)
+{
+    libxl_domain_build_info *const info = &d_config->b_info;
+    char **vments = NULL, **localents = NULL;
+    struct timeval start_time;
+    int i, ret;
+
+    ret = libxl__build_pre(gc, domid, d_config, state);
+    if (ret)
+        goto out;
+
+    gettimeofday(&start_time, NULL);
+
+    switch (info->type) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        ret = libxl__build_hvm(gc, domid, d_config, state);
+        if (ret)
+            goto out;
+
+        vments = libxl__calloc(gc, 7, sizeof(char *));
+        vments[0] = "rtc/timeoffset";
+        vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
+        vments[2] = "image/ostype";
+        vments[3] = "hvm";
+        vments[4] = "start_time";
+        vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+
+        localents = libxl__calloc(gc, 13, sizeof(char *));
+        i = 0;
+        localents[i++] = "platform/acpi";
+        localents[i++] = libxl__acpi_defbool_val(info) ? "1" : "0";
+        localents[i++] = "platform/acpi_s3";
+        localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s3) ? "1" : "0";
+        localents[i++] = "platform/acpi_s4";
+        localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s4) ? "1" : "0";
+        localents[i++] = "platform/acpi_laptop_slate";
+        localents[i++] = libxl_defbool_val(info->u.hvm.acpi_laptop_slate) ? "1" : "0";
+        if (info->u.hvm.mmio_hole_memkb) {
+            uint64_t max_ram_below_4g =
+                (1ULL << 32) - (info->u.hvm.mmio_hole_memkb << 10);
+
+            if (max_ram_below_4g <= HVM_BELOW_4G_MMIO_START) {
+                localents[i++] = "platform/mmio_hole_size";
+                localents[i++] =
+                    GCSPRINTF("%"PRIu64,
+                                   info->u.hvm.mmio_hole_memkb << 10);
+            }
+        }
+        localents[i++] = "platform/device-model";
+        localents[i++] = (char *)libxl_device_model_version_to_string(info->device_model_version);
+
+        break;
+    case LIBXL_DOMAIN_TYPE_PV:
+        ret = libxl__build_pv(gc, domid, d_config, state);
+        if (ret)
+            goto out;
+
+        vments = libxl__calloc(gc, 11, sizeof(char *));
+        i = 0;
+        vments[i++] = "image/ostype";
+        vments[i++] = "linux";
+        vments[i++] = "image/kernel";
+        vments[i++] = (char *) state->pv_kernel.path;
+        vments[i++] = "start_time";
+        vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+        if (state->pv_ramdisk.path) {
+            vments[i++] = "image/ramdisk";
+            vments[i++] = (char *) state->pv_ramdisk.path;
+        }
+        if (state->pv_cmdline) {
+            vments[i++] = "image/cmdline";
+            vments[i++] = (char *) state->pv_cmdline;
+        }
+
+        break;
+    case LIBXL_DOMAIN_TYPE_PVH:
+        state->shim_path = info->u.pvh.pvshim_path;
+        state->shim_cmdline = GCSPRINTF("%s%s%s",
+                    info->u.pvh.pvshim_cmdline,
+                    info->u.pvh.pvshim_extra ? " " : "",
+                    info->u.pvh.pvshim_extra ? info->u.pvh.pvshim_extra : "");
+
+        ret = libxl__build_hvm(gc, domid, d_config, state);
+        if (ret)
+            goto out;
+
+        vments = libxl__calloc(gc, 3, sizeof(char *));
+        vments[0] = "start_time";
+        vments[1] = GCSPRINTF("%"PRIu64".%02ld",
+                              (uint64_t)start_time.tv_sec,
+                              (long)start_time.tv_usec/10000);
+
+        break;
+    default:
+        ret = ERROR_INVAL;
+        goto out;
+    }
+    ret = libxl__build_post(gc, domid, info, state, vments, localents);
+out:
+    return ret;
+}
+
+int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
+                       libxl__domain_build_state *state,
+                       uint32_t *domid, bool soft_reset)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int ret, rc, nb_vm;
+    const char *dom_type;
+    char *uuid_string;
+    char *dom_path, *vm_path, *libxl_path;
+    struct xs_permissions roperm[2];
+    struct xs_permissions rwperm[1];
+    struct xs_permissions noperm[1];
+    xs_transaction_t t = 0;
+    libxl_vminfo *vm_list;
+
+    /* convenience aliases */
+    libxl_domain_create_info *info = &d_config->c_info;
+    libxl_domain_build_info *b_info = &d_config->b_info;
+
+    assert(soft_reset || *domid == INVALID_DOMID);
+
+    uuid_string = libxl__uuid2string(gc, info->uuid);
+    if (!uuid_string) {
+        rc = ERROR_NOMEM;
+        goto out;
+    }
+
+    if (!soft_reset) {
+        struct xen_domctl_createdomain create = {
+            .ssidref = info->ssidref,
+            .max_vcpus = b_info->max_vcpus,
+            .max_evtchn_port = b_info->event_channels,
+            .max_grant_frames = b_info->max_grant_frames,
+            .max_maptrack_frames = b_info->max_maptrack_frames,
+        };
+
+        if (info->type != LIBXL_DOMAIN_TYPE_PV) {
+            create.flags |= XEN_DOMCTL_CDF_hvm;
+            create.flags |=
+                libxl_defbool_val(info->hap) ? XEN_DOMCTL_CDF_hap : 0;
+            create.flags |=
+                libxl_defbool_val(info->oos) ? 0 : XEN_DOMCTL_CDF_oos_off;
+        }
+
+        assert(info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
+        LOG(DETAIL, "passthrough: %s",
+            libxl_passthrough_to_string(info->passthrough));
+
+        if (info->passthrough != LIBXL_PASSTHROUGH_DISABLED)
+            create.flags |= XEN_DOMCTL_CDF_iommu;
+
+        if (info->passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
+            create.iommu_opts |= XEN_DOMCTL_IOMMU_no_sharept;
+
+        /* Ultimately, handle is an array of 16 uint8_t, same as uuid */
+        libxl_uuid_copy(ctx, (libxl_uuid *)&create.handle, &info->uuid);
+
+        ret = libxl__arch_domain_prepare_config(gc, d_config, &create);
+        if (ret < 0) {
+            LOGED(ERROR, *domid, "fail to get domain config");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        for (;;) {
+            uint32_t local_domid;
+            bool recent;
+
+            if (info->domid == RANDOM_DOMID) {
+                uint16_t v;
+
+                ret = libxl__random_bytes(gc, (void *)&v, sizeof(v));
+                if (ret < 0)
+                    break;
+
+                v &= DOMID_MASK;
+                if (!libxl_domid_valid_guest(v))
+                    continue;
+
+                local_domid = v;
+            } else {
+                local_domid = info->domid; /* May not be valid */
+            }
+
+            ret = xc_domain_create(ctx->xch, &local_domid, &create);
+            if (ret < 0) {
+                /*
+                 * If we generated a random domid and creation failed
+                 * because that domid already exists then simply try
+                 * again.
+                 */
+                if (errno == EEXIST && info->domid == RANDOM_DOMID)
+                    continue;
+
+                LOGED(ERROR, local_domid, "domain creation fail");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+
+            /* A new domain now exists */
+            *domid = local_domid;
+
+            rc = libxl__is_domid_recent(gc, local_domid, &recent);
+            if (rc)
+                goto out;
+
+            /* The domid is not recent, so we're done */
+            if (!recent)
+                break;
+
+            /*
+             * If the domid was specified then there's no point in
+             * trying again.
+             */
+            if (libxl_domid_valid_guest(info->domid)) {
+                LOGED(ERROR, local_domid, "domain id recently used");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+
+            /*
+             * The domain is recent and so cannot be used. Clear domid
+             * here since, if xc_domain_destroy() fails below there is
+             * little point calling it again in the error path.
+             */
+            *domid = INVALID_DOMID;
+
+            ret = xc_domain_destroy(ctx->xch, local_domid);
+            if (ret < 0) {
+                LOGED(ERROR, local_domid, "domain destroy fail");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+
+            /* The domain was successfully destroyed, so we can try again */
+        }
+
+        rc = libxl__arch_domain_save_config(gc, d_config, state, &create);
+        if (rc < 0)
+            goto out;
+    }
+
+    /*
+     * If soft_reset is set the the domid will have been valid on entry.
+     * If it was not set then xc_domain_create() should have assigned a
+     * valid value. Either way, if we reach this point, domid should be
+     * valid.
+     */
+    assert(libxl_domid_valid_guest(*domid));
+
+    ret = xc_cpupool_movedomain(ctx->xch, info->poolid, *domid);
+    if (ret < 0) {
+        LOGED(ERROR, *domid, "domain move fail");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    dom_path = libxl__xs_get_dompath(gc, *domid);
+    if (!dom_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    vm_path = GCSPRINTF("/vm/%s", uuid_string);
+    if (!vm_path) {
+        LOGD(ERROR, *domid, "cannot allocate create paths");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl_path = libxl__xs_libxl_path(gc, *domid);
+    if (!libxl_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    noperm[0].id = 0;
+    noperm[0].perms = XS_PERM_NONE;
+
+    roperm[0].id = 0;
+    roperm[0].perms = XS_PERM_NONE;
+    roperm[1].id = *domid;
+    roperm[1].perms = XS_PERM_READ;
+
+    rwperm[0].id = *domid;
+    rwperm[0].perms = XS_PERM_NONE;
+
+retry_transaction:
+    t = xs_transaction_start(ctx->xsh);
+
+    xs_rm(ctx->xsh, t, dom_path);
+    libxl__xs_mknod(gc, t, dom_path, roperm, ARRAY_SIZE(roperm));
+
+    xs_rm(ctx->xsh, t, vm_path);
+    libxl__xs_mknod(gc, t, vm_path, roperm, ARRAY_SIZE(roperm));
+
+    xs_rm(ctx->xsh, t, libxl_path);
+    libxl__xs_mknod(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm));
+    libxl__xs_mknod(gc, t, GCSPRINTF("%s/device", libxl_path),
+                    noperm, ARRAY_SIZE(noperm));
+
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
+    rc = libxl__domain_rename(gc, *domid, 0, info->name, t);
+    if (rc)
+        goto out;
+
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/cpu", dom_path),
+                    roperm, ARRAY_SIZE(roperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/memory", dom_path),
+                    roperm, ARRAY_SIZE(roperm));
+
+    if (!libxl_defbool_val(info->xend_suspend_evtchn_compat)) {
+        libxl__xs_mknod(gc, t,
+                        GCSPRINTF("%s/device", dom_path),
+                        roperm, ARRAY_SIZE(roperm));
+        libxl__xs_mknod(gc, t,
+                        GCSPRINTF("%s/device/suspend/event-channel",
+                                  dom_path),
+                        rwperm, ARRAY_SIZE(rwperm));
+    } else {
+        libxl__xs_mknod(gc, t,
+                        GCSPRINTF("%s/device", dom_path),
+                        rwperm, ARRAY_SIZE(rwperm));
+    }
+
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/control", dom_path),
+                    roperm, ARRAY_SIZE(roperm));
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM)
+        libxl__xs_mknod(gc, t,
+                        GCSPRINTF("%s/hvmloader", dom_path),
+                        roperm, ARRAY_SIZE(roperm));
+
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/control/shutdown", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/control/feature-poweroff", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/control/feature-reboot", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/control/feature-suspend", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        libxl__xs_mknod(gc, t,
+                        GCSPRINTF("%s/control/feature-s3", dom_path),
+                        rwperm, ARRAY_SIZE(rwperm));
+        libxl__xs_mknod(gc, t,
+                        GCSPRINTF("%s/control/feature-s4", dom_path),
+                        rwperm, ARRAY_SIZE(rwperm));
+    }
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/control/sysrq", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/data", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/drivers", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/feature", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/attr", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+    libxl__xs_mknod(gc, t,
+                    GCSPRINTF("%s/error", dom_path),
+                    rwperm, ARRAY_SIZE(rwperm));
+
+    if (libxl_defbool_val(info->driver_domain)) {
+        /*
+         * Create a local "libxl" directory for each guest, since we might want
+         * to use libxl from inside the guest
+         */
+        libxl__xs_mknod(gc, t, GCSPRINTF("%s/libxl", dom_path), rwperm,
+                        ARRAY_SIZE(rwperm));
+        /*
+         * Create a local "device-model" directory for each guest, since we
+         * might want to use Qemu from inside the guest
+         */
+        libxl__xs_mknod(gc, t, GCSPRINTF("%s/device-model", dom_path), rwperm,
+                        ARRAY_SIZE(rwperm));
+    }
+
+    vm_list = libxl_list_vm(ctx, &nb_vm);
+    if (!vm_list) {
+        LOGD(ERROR, *domid, "cannot get number of running guests");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    libxl_vminfo_list_free(vm_list, nb_vm);
+
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/name", vm_path), info->name, strlen(info->name));
+
+    libxl__xs_writev(gc, t, dom_path, info->xsdata);
+    libxl__xs_writev(gc, t, GCSPRINTF("%s/platform", dom_path), info->platformdata);
+
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1);
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-xs_reset_watches", dom_path), "1", 1);
+
+    dom_type = libxl_domain_type_to_string(info->type);
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/type", libxl_path), dom_type,
+             strlen(dom_type));
+
+    if (!xs_transaction_end(ctx->xsh, t, 0)) {
+        if (errno == EAGAIN) {
+            t = 0;
+            goto retry_transaction;
+        }
+        LOGED(ERROR, *domid, "domain creation ""xenstore transaction commit failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    t = 0;
+
+    rc = 0;
+ out:
+    if (t) xs_transaction_end(ctx->xsh, t, 1);
+    return rc;
+}
+
+static int store_libxl_entry(libxl__gc *gc, uint32_t domid,
+                             libxl_domain_build_info *b_info)
+{
+    char *path = NULL;
+
+    path = libxl__xs_libxl_path(gc, domid);
+    path = GCSPRINTF("%s/dm-version", path);
+    return libxl__xs_printf(gc, XBT_NULL, path, "%s",
+                            libxl_device_model_version_to_string(b_info->device_model_version));
+}
+
+void libxl__domain_build_state_init(libxl__domain_build_state *state)
+{
+    state->dm_monitor_fd = -1;
+}
+
+void libxl__domain_build_state_dispose(libxl__domain_build_state *state)
+{
+    libxl__file_reference_unmap(&state->pv_kernel);
+    libxl__file_reference_unmap(&state->pv_ramdisk);
+    if (state->dm_monitor_fd >= 0) {
+        close(state->dm_monitor_fd);
+        state->dm_monitor_fd = -1;
+    }
+}
+
+/*----- main domain creation -----*/
+
+/* We have a linear control flow; only one event callback is
+ * outstanding at any time.  Each initiation and callback function
+ * arranges for the next to be called, as the very last thing it
+ * does.  (If that particular sub-operation is not needed, a
+ * function will call the next event callback directly.)
+ */
+
+/* Event callbacks, in this order: */
+static void domcreate_bootloader_console_available(libxl__egc *egc,
+                                                   libxl__bootloader_state *bl);
+static void domcreate_console_available(libxl__egc *egc,
+                                        libxl__domain_create_state *dcs);
+
+static void domcreate_bootloader_done(libxl__egc *egc,
+                                      libxl__bootloader_state *bl,
+                                      int rc);
+static void libxl__colo_restore_setup_done(libxl__egc *egc,
+                                           libxl__colo_restore_state *crs,
+                                           int rc);
+static void domcreate_stream_done(libxl__egc *egc,
+                                  libxl__stream_read_state *srs,
+                                  int ret);
+static void domcreate_rebuild_done(libxl__egc *egc,
+                                   libxl__domain_create_state *dcs,
+                                   int ret);
+static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs,
+                                int ret);
+static void domcreate_devmodel_started(libxl__egc *egc,
+                                       libxl__dm_spawn_state *dmss,
+                                       int rc);
+static void domcreate_attach_devices(libxl__egc *egc,
+                                     libxl__multidev *multidev,
+                                     int ret);
+static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
+                                    int rc, const char *p);
+
+/* Our own function to clean up and call the user's callback.
+ * The final call in the sequence. */
+static void domcreate_complete(libxl__egc *egc,
+                               libxl__domain_create_state *dcs,
+                               int rc);
+
+/* If creation is not successful, this callback will be executed
+ * when domain destruction is finished */
+static void domcreate_destruction_cb(libxl__egc *egc,
+                                     libxl__domain_destroy_state *dds,
+                                     int rc);
+
+static bool ok_to_default_memkb_in_create(libxl__gc *gc)
+{
+    /*
+     * This is a fudge.  We are trying to find whether the caller
+     * calls the old version of libxl_domain_need_memory.  If they do
+     * then, because it only gets the b_info, and because it can't
+     * update the b_info (because it's const), it will base its
+     * calculations on defaulting shadow_memkb and iommu_memkb to 0
+     * In that case we probably shouldn't default them differently
+     * during libxl_domain_create.
+     *
+     * The result is that the behaviour with old callers is the same
+     * as in 4.13: no additional memory is allocated for shadow and
+     * iommu (unless the caller set shadow_memkb, eg from a call to
+     * libxl_get_required_shadow_memory).
+     */
+    return !CTX->libxl_domain_need_memory_0x041200_called ||
+            CTX->libxl_domain_need_memory_called;
+    /*
+     * Treat mixed callers as new callers.  Presumably they know what
+     * they are doing.
+     */
+}
+
+static unsigned long libxl__get_required_iommu_memory(unsigned long maxmem_kb)
+{
+    unsigned long iommu_pages = 0, mem_pages = maxmem_kb / 4;
+    unsigned int level;
+
+    /* Assume a 4 level page table with 512 entries per level */
+    for (level = 0; level < 4; level++)
+    {
+        mem_pages = DIV_ROUNDUP(mem_pages, 512);
+        iommu_pages += mem_pages;
+    }
+
+    return iommu_pages * 4;
+}
+
+int libxl__domain_config_setdefault(libxl__gc *gc,
+                                    libxl_domain_config *d_config,
+                                    uint32_t domid /* for logging, only */)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int ret;
+    bool pod_enabled = false;
+    libxl_domain_create_info *c_info = &d_config->c_info;
+
+    libxl_physinfo physinfo;
+    ret = libxl_get_physinfo(CTX, &physinfo);
+    if (ret) goto error_out;
+
+    if (d_config->c_info.ssid_label) {
+        char *s = d_config->c_info.ssid_label;
+        ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
+                                         &d_config->c_info.ssidref);
+        if (ret) {
+            if (errno == ENOSYS) {
+                LOGD(WARN, domid, "XSM Disabled: init_seclabel not supported");
+                ret = 0;
+            } else {
+                LOGD(ERROR, domid, "Invalid init_seclabel: %s", s);
+                goto error_out;
+            }
+        }
+    }
+
+    if (d_config->b_info.exec_ssid_label) {
+        char *s = d_config->b_info.exec_ssid_label;
+        ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
+                                         &d_config->b_info.exec_ssidref);
+        if (ret) {
+            if (errno == ENOSYS) {
+                LOGD(WARN, domid, "XSM Disabled: seclabel not supported");
+                ret = 0;
+            } else {
+                LOGD(ERROR, domid, "Invalid seclabel: %s", s);
+                goto error_out;
+            }
+        }
+    }
+
+    if (d_config->b_info.device_model_ssid_label) {
+        char *s = d_config->b_info.device_model_ssid_label;
+        ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
+                                         &d_config->b_info.device_model_ssidref);
+        if (ret) {
+            if (errno == ENOSYS) {
+                LOGD(WARN, domid,
+                     "XSM Disabled: device_model_stubdomain_seclabel not supported");
+                ret = 0;
+            } else {
+                LOGD(ERROR, domid, "Invalid device_model_stubdomain_seclabel: %s", s);
+                goto error_out;
+            }
+        }
+    }
+
+    if (d_config->c_info.pool_name) {
+        d_config->c_info.poolid = -1;
+        libxl_cpupool_qualifier_to_cpupoolid(ctx, d_config->c_info.pool_name,
+                                             &d_config->c_info.poolid,
+                                             NULL);
+    }
+    if (!libxl_cpupoolid_is_valid(ctx, d_config->c_info.poolid)) {
+        LOGD(ERROR, domid, "Illegal pool specified: %s",
+             d_config->c_info.pool_name);
+        ret = ERROR_INVAL;
+        goto error_out;
+    }
+
+    ret = libxl__domain_create_info_setdefault(gc, &d_config->c_info,
+                                               &physinfo);
+    if (ret) {
+        LOGD(ERROR, domid, "Unable to set domain create info defaults");
+        goto error_out;
+    }
+
+    bool need_pt = d_config->num_pcidevs || d_config->num_dtdevs;
+    if (c_info->passthrough == LIBXL_PASSTHROUGH_DEFAULT) {
+        c_info->passthrough = need_pt
+            ? LIBXL_PASSTHROUGH_ENABLED : LIBXL_PASSTHROUGH_DISABLED;
+    }
+
+    bool iommu_enabled = physinfo.cap_hvm_directio;
+    if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED && !iommu_enabled) {
+        LOGD(ERROR, domid,
+             "passthrough not supported on this platform\n");
+        ret = ERROR_INVAL;
+        goto error_out;
+    }
+
+    if (c_info->passthrough == LIBXL_PASSTHROUGH_DISABLED && need_pt) {
+        LOGD(ERROR, domid,
+             "passthrough disabled but devices are specified");
+        ret = ERROR_INVAL;
+        goto error_out;
+    }
+
+    ret = libxl__arch_passthrough_mode_setdefault(gc,domid,d_config,&physinfo);
+    if (ret) goto error_out;
+
+    /* An explicit setting should now have been chosen */
+    assert(c_info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
+    assert(c_info->passthrough != LIBXL_PASSTHROUGH_ENABLED);
+
+    /* If target_memkb is smaller than max_memkb, the subsequent call
+     * to libxc when building HVM domain will enable PoD mode.
+     */
+    pod_enabled = (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV) &&
+        (d_config->b_info.target_memkb < d_config->b_info.max_memkb);
+
+    /* We cannot have PoD and PCI device assignment at the same time
+     * for HVM guest. It was reported that IOMMU cannot work with PoD
+     * enabled because it needs to populated entire page table for
+     * guest. To stay on the safe side, we disable PCI device
+     * assignment when PoD is enabled.
+     */
+    if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
+        d_config->num_pcidevs && pod_enabled) {
+        ret = ERROR_INVAL;
+        LOGD(ERROR, domid,
+             "PCI device assignment for HVM guest failed due to PoD enabled");
+        goto error_out;
+    }
+
+    /* Disallow PoD and vNUMA to be enabled at the same time because PoD
+     * pool is not vNUMA-aware yet.
+     */
+    if (pod_enabled && d_config->b_info.num_vnuma_nodes) {
+        ret = ERROR_INVAL;
+        LOGD(ERROR, domid, "Cannot enable PoD and vNUMA at the same time");
+        goto error_out;
+    }
+
+    /* PV vNUMA is not yet supported because there is an issue with
+     * cpuid handling.
+     */
+    if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
+        d_config->b_info.num_vnuma_nodes) {
+        ret = ERROR_INVAL;
+        LOGD(ERROR, domid, "PV vNUMA is not yet supported");
+        goto error_out;
+    }
+
+    if (d_config->b_info.shadow_memkb == LIBXL_MEMKB_DEFAULT
+        && ok_to_default_memkb_in_create(gc))
+        d_config->b_info.shadow_memkb =
+            libxl_get_required_shadow_memory(d_config->b_info.max_memkb,
+                                             d_config->b_info.max_vcpus);
+
+    /* No IOMMU reservation is needed if passthrough mode is not 'sync_pt' */
+    if (d_config->b_info.iommu_memkb == LIBXL_MEMKB_DEFAULT
+        && ok_to_default_memkb_in_create(gc))
+        d_config->b_info.iommu_memkb =
+            (d_config->c_info.passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
+            ? libxl__get_required_iommu_memory(d_config->b_info.max_memkb)
+            : 0;
+
+    ret = libxl__domain_build_info_setdefault(gc, &d_config->b_info);
+    if (ret) {
+        LOGD(ERROR, domid, "Unable to set domain build info defaults");
+        goto error_out;
+    }
+
+    if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
+        (libxl_defbool_val(d_config->b_info.nested_hvm) &&
+        ((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
+          libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
+        (d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)))) {
+        ret = ERROR_INVAL;
+        LOGD(ERROR, domid, "nestedhvm and altp2mhvm cannot be used together");
+        goto error_out;
+    }
+
+    if (((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
+         libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
+        (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
+         d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)) &&
+        pod_enabled) {
+        ret = ERROR_INVAL;
+        LOGD(ERROR, domid, "Cannot enable PoD and ALTP2M at the same time");
+        goto error_out;
+    }
+
+    ret = 0;
+ error_out:
+    return ret;
+}
+
+static void initiate_domain_create(libxl__egc *egc,
+                                   libxl__domain_create_state *dcs)
+{
+    STATE_AO_GC(dcs->ao);
+    uint32_t domid;
+    int i, ret;
+
+    /* convenience aliases */
+    libxl_domain_config *const d_config = dcs->guest_config;
+    libxl__domain_build_state *dbs = &dcs->build_state;
+
+    libxl__xswait_init(&dcs->console_xswait);
+
+    domid = dcs->domid;
+    libxl__domain_build_state_init(dbs);
+    dbs->restore = dcs->restore_fd >= 0;
+
+    ret = libxl__domain_config_setdefault(gc,d_config,domid);
+    if (ret) goto error_out;
+
+    ret = libxl__domain_make(gc, d_config, dbs, &domid, dcs->soft_reset);
+    if (ret) {
+        LOGD(ERROR, domid, "cannot make domain: %d", ret);
+        dcs->guest_domid = domid;
+        ret = ERROR_FAIL;
+        goto error_out;
+    }
+
+    dcs->guest_domid = domid;
+    dcs->sdss.dm.guest_domid = 0; /* means we haven't spawned */
+
+    /* post-4.13 todo: move these next bits of defaulting to
+     * libxl__domain_config_setdefault */
+
+    /*
+     * Set the dm version quite early so that libxl doesn't have to pass the
+     * build info around just to know if the domain has a device model or not.
+     */
+    store_libxl_entry(gc, domid, &d_config->b_info);
+
+    for (i = 0; i < d_config->num_disks; i++) {
+        ret = libxl__disk_devtype.set_default(gc, domid, &d_config->disks[i],
+                                              false);
+        if (ret) {
+            LOGD(ERROR, domid, "Unable to set disk defaults for disk %d", i);
+            goto error_out;
+        }
+    }
+
+    dcs->bl.ao = ao;
+    libxl_device_disk *bootdisk =
+        d_config->num_disks > 0 ? &d_config->disks[0] : NULL;
+
+    /*
+     * The devid has to be set before launching the device model. For the
+     * hotplug case this is done in libxl_device_nic_add but on domain
+     * creation this is called too late.
+     * Make two runs over configured NICs in order to avoid duplicate IDs
+     * in case the caller partially assigned IDs.
+     */
+    ret = libxl__device_nic_set_devids(gc, d_config, domid);
+    if (ret)
+        goto error_out;
+
+    if (dbs->restore || dcs->soft_reset) {
+        LOGD(DEBUG, domid, "restoring, not running bootloader");
+        domcreate_bootloader_done(egc, &dcs->bl, 0);
+    } else  {
+        LOGD(DEBUG, domid, "running bootloader");
+        dcs->bl.callback = domcreate_bootloader_done;
+        dcs->bl.console_available = domcreate_bootloader_console_available;
+        dcs->bl.info = &d_config->b_info;
+        dcs->bl.disk = bootdisk;
+        dcs->bl.domid = dcs->guest_domid;
+
+        dcs->bl.kernel = &dbs->pv_kernel;
+        dcs->bl.ramdisk = &dbs->pv_ramdisk;
+
+        libxl__bootloader_run(egc, &dcs->bl);
+    }
+    return;
+
+error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
+static void domcreate_bootloader_console_available(libxl__egc *egc,
+                                                   libxl__bootloader_state *bl)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
+    STATE_AO_GC(bl->ao);
+    domcreate_console_available(egc, dcs);
+}
+
+static void domcreate_console_available(libxl__egc *egc,
+                                        libxl__domain_create_state *dcs) {
+    libxl__ao_progress_report(egc, dcs->ao, &dcs->aop_console_how,
+                              NEW_EVENT(egc, DOMAIN_CREATE_CONSOLE_AVAILABLE,
+                                        dcs->guest_domid,
+                                        dcs->aop_console_how.for_event));
+}
+
+static void domcreate_bootloader_done(libxl__egc *egc,
+                                      libxl__bootloader_state *bl,
+                                      int rc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
+    STATE_AO_GC(bl->ao);
+
+    /* convenience aliases */
+    const uint32_t domid = dcs->guest_domid;
+    libxl_domain_config *const d_config = dcs->guest_config;
+    const int restore_fd = dcs->restore_fd;
+    libxl__domain_build_state *const state = &dcs->build_state;
+    const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
+    libxl__colo_restore_state *const crs = &dcs->crs;
+    libxl_domain_build_info *const info = &d_config->b_info;
+    libxl__srm_restore_autogen_callbacks *const callbacks =
+        &dcs->srs.shs.callbacks.restore.a;
+
+    if (rc) {
+        domcreate_rebuild_done(egc, dcs, rc);
+        return;
+    }
+
+    /* consume bootloader outputs. state->pv_{kernel,ramdisk} have
+     * been initialised by the bootloader already.
+     */
+    state->pv_cmdline = bl->cmdline;
+
+    /* We might be going to call libxl__spawn_local_dm, or _spawn_stub_dm.
+     * Fill in any field required by either, including both relevant
+     * callbacks (_spawn_stub_dm will overwrite our trespass if needed). */
+    dcs->sdss.dm.spawn.ao = ao;
+    dcs->sdss.dm.guest_config = dcs->guest_config;
+    dcs->sdss.dm.build_state = &dcs->build_state;
+    dcs->sdss.dm.callback = domcreate_devmodel_started;
+    dcs->sdss.callback = domcreate_devmodel_started;
+
+    if (restore_fd < 0 && !dcs->soft_reset) {
+        rc = libxl__domain_build(gc, d_config, domid, state);
+        domcreate_rebuild_done(egc, dcs, rc);
+        return;
+    }
+
+    /* Prepare environment for domcreate_stream_done */
+    dcs->srs.dcs = dcs;
+
+    /* Restore */
+    callbacks->static_data_done = libxl__srm_callout_callback_static_data_done;
+    callbacks->restore_results = libxl__srm_callout_callback_restore_results;
+
+    /* COLO only supports HVM now because it does not work very
+     * well with pv drivers:
+     * 1. We need to resume vm in the slow path. In this case we
+     *    need to disconnect/reconnect backend and frontend. It
+     *    will take too much time and the performance is very slow.
+     * 2. PV disk cannot reuse block replication that is implemented
+     *    in QEMU.
+     */
+    if (info->type != LIBXL_DOMAIN_TYPE_HVM &&
+        checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
+        LOGD(ERROR, domid, "COLO only supports HVM, unable to restore domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__build_pre(gc, domid, d_config, state);
+    if (rc)
+        goto out;
+
+    dcs->srs.ao = ao;
+    dcs->srs.fd = restore_fd;
+    dcs->srs.legacy = (dcs->restore_params.stream_version == 1);
+    dcs->srs.back_channel = false;
+    dcs->srs.completion_callback = domcreate_stream_done;
+
+    if (restore_fd >= 0) {
+        switch (checkpointed_stream) {
+        case LIBXL_CHECKPOINTED_STREAM_COLO:
+            /* colo restore setup */
+            crs->ao = ao;
+            crs->domid = domid;
+            crs->send_back_fd = dcs->send_back_fd;
+            crs->recv_fd = restore_fd;
+            crs->hvm = (info->type != LIBXL_DOMAIN_TYPE_PV);
+            crs->callback = libxl__colo_restore_setup_done;
+            libxl__colo_restore_setup(egc, crs);
+            break;
+        case LIBXL_CHECKPOINTED_STREAM_REMUS:
+            libxl__remus_restore_setup(egc, dcs);
+            /* fall through */
+        case LIBXL_CHECKPOINTED_STREAM_NONE:
+            libxl__stream_read_start(egc, &dcs->srs);
+        }
+        return;
+    }
+
+ out:
+    domcreate_stream_done(egc, &dcs->srs, rc);
+}
+
+static void libxl__colo_restore_setup_done(libxl__egc *egc,
+                                           libxl__colo_restore_state *crs,
+                                           int rc)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
+
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, dcs->guest_domid, "colo restore setup fails: %d", rc);
+        domcreate_stream_done(egc, &dcs->srs, rc);
+        return;
+    }
+
+    libxl__stream_read_start(egc, &dcs->srs);
+}
+
+int libxl__srm_callout_callback_static_data_done(unsigned int missing,
+                                                 void *user)
+{
+    libxl__save_helper_state *shs = user;
+    libxl__domain_create_state *dcs = shs->caller_state;
+    STATE_AO_GC(dcs->ao);
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+
+    libxl_domain_config *d_config = dcs->guest_config;
+    libxl_domain_build_info *info = &d_config->b_info;
+
+    /*
+     * CPUID/MSR information is not present in pre Xen-4.14 streams.
+     *
+     * Libxl used to always regenerate the CPUID policy from first principles
+     * on migrate.  Continue to do so for backwards compatibility when the
+     * stream doesn't contain any CPUID data.
+     */
+    if (missing & XGR_SDD_MISSING_CPUID)
+        libxl__cpuid_legacy(ctx, dcs->guest_domid, true, info);
+
+    return 0;
+}
+
+void libxl__srm_callout_callback_restore_results(xen_pfn_t store_mfn,
+          xen_pfn_t console_mfn, void *user)
+{
+    libxl__save_helper_state *shs = user;
+    libxl__domain_create_state *dcs = shs->caller_state;
+    STATE_AO_GC(dcs->ao);
+    libxl__domain_build_state *const state = &dcs->build_state;
+
+    state->store_mfn =            store_mfn;
+    state->console_mfn =          console_mfn;
+    shs->need_results =           0;
+}
+
+static void domcreate_stream_done(libxl__egc *egc,
+                                  libxl__stream_read_state *srs,
+                                  int ret)
+{
+    /* NB perhaps only srs->dcs is valid; eg in the case of an
+     * early branch to domcreate_bootloader_done's `out' block */
+    libxl__domain_create_state *dcs = srs->dcs;
+    STATE_AO_GC(dcs->ao);
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char **vments = NULL, **localents = NULL;
+    struct timeval start_time;
+    int i, esave;
+
+    /* convenience aliases */
+    const uint32_t domid = dcs->guest_domid;
+    libxl_domain_config *const d_config = dcs->guest_config;
+    libxl_domain_build_info *const info = &d_config->b_info;
+    libxl__domain_build_state *const state = &dcs->build_state;
+    const int fd = dcs->restore_fd;
+
+    if (ret)
+        goto out;
+
+    gettimeofday(&start_time, NULL);
+
+    switch (info->type) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        vments = libxl__calloc(gc, 7, sizeof(char *));
+        vments[0] = "rtc/timeoffset";
+        vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
+        vments[2] = "image/ostype";
+        vments[3] = "hvm";
+        vments[4] = "start_time";
+        vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+        break;
+    case LIBXL_DOMAIN_TYPE_PV:
+        vments = libxl__calloc(gc, 11, sizeof(char *));
+        i = 0;
+        vments[i++] = "image/ostype";
+        vments[i++] = "linux";
+        vments[i++] = "image/kernel";
+        vments[i++] = (char *) state->pv_kernel.path;
+        vments[i++] = "start_time";
+        vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
+        if (state->pv_ramdisk.path) {
+            vments[i++] = "image/ramdisk";
+            vments[i++] = (char *) state->pv_ramdisk.path;
+        }
+        if (state->pv_cmdline) {
+            vments[i++] = "image/cmdline";
+            vments[i++] = (char *) state->pv_cmdline;
+        }
+        break;
+    case LIBXL_DOMAIN_TYPE_PVH:
+        vments = libxl__calloc(gc, 3, sizeof(char *));
+        vments[0] = "start_time";
+        vments[1] = GCSPRINTF("%"PRIu64".%02ld",
+                              (uint64_t)start_time.tv_sec,
+                              (long)start_time.tv_usec/10000);
+        break;
+    default:
+        ret = ERROR_INVAL;
+        goto out;
+    }
+
+    /*
+     * The scheduler on the sending domain may be different than the
+     * scheduler running here.  Setting the scheduler to UNKNOWN will
+     * cause the code to take to take whatever parameters are
+     * available in that scheduler, while discarding the rest.
+     */
+    info->sched_params.sched = LIBXL_SCHEDULER_UNKNOWN;
+
+    ret = libxl__build_post(gc, domid, info, state, vments, localents);
+    if (ret)
+        goto out;
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        state->saved_state = GCSPRINTF(
+                       LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
+    }
+
+out:
+    if (info->type == LIBXL_DOMAIN_TYPE_PV) {
+        libxl__file_reference_unmap(&state->pv_kernel);
+        libxl__file_reference_unmap(&state->pv_ramdisk);
+    }
+
+    /* fd == -1 here means we're doing soft reset. */
+    if (fd != -1) {
+        esave = errno;
+        libxl_fd_set_nonblock(ctx, fd, 0);
+        errno = esave;
+    }
+    domcreate_rebuild_done(egc, dcs, ret);
+}
+
+static void domcreate_rebuild_done(libxl__egc *egc,
+                                   libxl__domain_create_state *dcs,
+                                   int ret)
+{
+    STATE_AO_GC(dcs->ao);
+
+    /* convenience aliases */
+    const uint32_t domid = dcs->guest_domid;
+    libxl_domain_config *const d_config = dcs->guest_config;
+
+    if (ret) {
+        LOGD(ERROR, domid, "cannot (re-)build domain: %d", ret);
+        ret = ERROR_FAIL;
+        goto error_out;
+    }
+
+    store_libxl_entry(gc, domid, &d_config->b_info);
+
+    libxl__multidev_begin(ao, &dcs->multidev);
+    dcs->multidev.callback = domcreate_launch_dm;
+    libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev);
+    libxl__multidev_prepared(egc, &dcs->multidev, 0);
+
+    return;
+
+ error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
+static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
+                                int ret)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
+    STATE_AO_GC(dcs->ao);
+    int i;
+
+    /* convenience aliases */
+    const uint32_t domid = dcs->guest_domid;
+    libxl_domain_config *const d_config = dcs->guest_config;
+    libxl__domain_build_state *const state = &dcs->build_state;
+
+    if (ret) {
+        LOGD(ERROR, domid, "unable to add disk devices");
+        goto error_out;
+    }
+
+    for (i = 0; i < d_config->b_info.num_ioports; i++) {
+        libxl_ioport_range *io = &d_config->b_info.ioports[i];
+
+        LOGD(DEBUG, domid, "ioports %"PRIx32"-%"PRIx32,
+             io->first, io->first + io->number - 1);
+
+        ret = xc_domain_ioport_permission(CTX->xch, domid,
+                                          io->first, io->number, 1);
+        if (ret < 0) {
+            LOGED(ERROR, domid,
+                  "failed give domain access to ioports %"PRIx32"-%"PRIx32,
+                  io->first, io->first + io->number - 1);
+            ret = ERROR_FAIL;
+            goto error_out;
+        }
+    }
+
+    for (i = 0; i < d_config->b_info.num_irqs; i++) {
+        int irq = d_config->b_info.irqs[i];
+
+        LOGD(DEBUG, domid, "irq %d", irq);
+
+        ret = irq >= 0 ? libxl__arch_domain_map_irq(gc, domid, irq)
+                       : -EOVERFLOW;
+        if (ret) {
+            LOGED(ERROR, domid, "failed give domain access to irq %d", irq);
+            ret = ERROR_FAIL;
+            goto error_out;
+        }
+    }
+
+    for (i = 0; i < d_config->b_info.num_iomem; i++) {
+        libxl_iomem_range *io = &d_config->b_info.iomem[i];
+
+        LOGD(DEBUG, domid, "iomem %"PRIx64"-%"PRIx64,
+             io->start, io->start + io->number - 1);
+
+        ret = xc_domain_iomem_permission(CTX->xch, domid,
+                                          io->start, io->number, 1);
+        if (ret < 0) {
+            LOGED(ERROR, domid,
+                  "failed give domain access to iomem range %"PRIx64"-%"PRIx64,
+                  io->start, io->start + io->number - 1);
+            ret = ERROR_FAIL;
+            goto error_out;
+        }
+        ret = xc_domain_memory_mapping(CTX->xch, domid,
+                                       io->gfn, io->start,
+                                       io->number, 1);
+        if (ret < 0) {
+            LOGED(ERROR, domid,
+                  "failed to map to domain iomem range %"PRIx64"-%"PRIx64
+                  " to guest address %"PRIx64,
+                  io->start, io->start + io->number - 1, io->gfn);
+            ret = ERROR_FAIL;
+            goto error_out;
+        }
+    }
+
+    /* For both HVM and PV the 0th console is a regular console. We
+       map channels to IOEMU consoles starting at 1 */
+    for (i = 0; i < d_config->num_channels; i++) {
+        libxl__device_console console;
+        libxl__device device;
+        ret = libxl__init_console_from_channel(gc, &console, i + 1,
+                                               &d_config->channels[i]);
+        if ( ret ) {
+            libxl__device_console_dispose(&console);
+            goto error_out;
+        }
+        libxl__device_console_add(gc, domid, &console, NULL, &device);
+        libxl__device_console_dispose(&console);
+    }
+
+    for (i = 0; i < d_config->num_p9s; i++)
+        libxl__device_add(gc, domid, &libxl__p9_devtype, &d_config->p9s[i]);
+
+    for (i = 0; i < d_config->num_pvcallsifs; i++)
+        libxl__device_add(gc, domid, &libxl__pvcallsif_devtype,
+                          &d_config->pvcallsifs[i]);
+
+    switch (d_config->c_info.type) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+    {
+        libxl__device_console console;
+        libxl__device device;
+        libxl_device_vkb vkb;
+
+        init_console_info(gc, &console, 0);
+        console.backend_domid = state->console_domid;
+        libxl__device_console_add(gc, domid, &console, state, &device);
+        libxl__device_console_dispose(&console);
+
+        if (libxl_defbool_val(d_config->b_info.u.hvm.vkb_device)) {
+            libxl_device_vkb_init(&vkb);
+            libxl__device_add(gc, domid, &libxl__vkb_devtype, &vkb);
+            libxl_device_vkb_dispose(&vkb);
+        }
+
+        dcs->sdss.dm.guest_domid = domid;
+        if (libxl_defbool_val(d_config->b_info.device_model_stubdomain))
+            libxl__spawn_stub_dm(egc, &dcs->sdss);
+        else
+            libxl__spawn_local_dm(egc, &dcs->sdss.dm);
+
+        /*
+         * Handle the domain's (and the related stubdomain's) access to
+         * the VGA framebuffer.
+         */
+        ret = libxl__grant_vga_iomem_permission(gc, domid, d_config);
+        if ( ret )
+            goto error_out;
+
+        return;
+    }
+    case LIBXL_DOMAIN_TYPE_PV:
+    case LIBXL_DOMAIN_TYPE_PVH:
+    {
+        libxl__device_console console, vuart;
+        libxl__device device;
+
+        for (i = 0; i < d_config->num_vfbs; i++) {
+            libxl__device_add(gc, domid, &libxl__vfb_devtype,
+                              &d_config->vfbs[i]);
+        }
+
+        for (i = 0; i < d_config->num_vkbs; i++) {
+            libxl__device_add(gc, domid, &libxl__vkb_devtype,
+                              &d_config->vkbs[i]);
+        }
+
+        if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
+            init_console_info(gc, &vuart, 0);
+            vuart.backend_domid = state->console_domid;
+            libxl__device_vuart_add(gc, domid, &vuart, state);
+            libxl__device_console_dispose(&vuart);
+        }
+
+        init_console_info(gc, &console, 0);
+        console.backend_domid = state->console_domid;
+        libxl__device_console_add(gc, domid, &console, state, &device);
+        libxl__device_console_dispose(&console);
+
+        ret = libxl__need_xenpv_qemu(gc, d_config);
+        if (ret < 0)
+            goto error_out;
+        if (ret) {
+            dcs->sdss.dm.guest_domid = domid;
+            libxl__spawn_local_dm(egc, &dcs->sdss.dm);
+            return;
+        } else {
+            assert(!dcs->sdss.dm.guest_domid);
+            domcreate_devmodel_started(egc, &dcs->sdss.dm, 0);
+            return;
+        }
+    }
+    default:
+        ret = ERROR_INVAL;
+        goto error_out;
+    }
+    abort(); /* not reached */
+
+ error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
+static void libxl__add_dtdevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+                              libxl_domain_config *d_config,
+                              libxl__multidev *multidev)
+{
+    AO_GC;
+    libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
+    int i, rc = 0;
+
+    for (i = 0; i < d_config->num_dtdevs; i++) {
+        const libxl_device_dtdev *dtdev = &d_config->dtdevs[i];
+
+        LOGD(DEBUG, domid, "Assign device \"%s\" to domain", dtdev->path);
+        rc = xc_assign_dt_device(CTX->xch, domid, dtdev->path);
+        if (rc < 0) {
+            LOGD(ERROR, domid, "xc_assign_dtdevice failed: %d", rc);
+            goto out;
+        }
+    }
+
+out:
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+#define libxl_device_dtdev_list NULL
+#define libxl_device_dtdev_compare NULL
+#define libxl__device_from_dtdev NULL
+#define libxl__device_dtdev_setdefault NULL
+#define libxl__device_dtdev_update_devid NULL
+static DEFINE_DEVICE_TYPE_STRUCT(dtdev, NONE);
+
+const libxl__device_type *device_type_tbl[] = {
+    &libxl__disk_devtype,
+    &libxl__nic_devtype,
+    &libxl__vtpm_devtype,
+    &libxl__usbctrl_devtype,
+    &libxl__usbdev_devtype,
+    &libxl__pcidev_devtype,
+    &libxl__dtdev_devtype,
+    &libxl__vdispl_devtype,
+    &libxl__vsnd_devtype,
+    NULL
+};
+
+static void domcreate_devmodel_started(libxl__egc *egc,
+                                       libxl__dm_spawn_state *dmss,
+                                       int ret)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(dmss, *dcs, sdss.dm);
+    STATE_AO_GC(dmss->spawn.ao);
+    int domid = dcs->guest_domid;
+
+    if (ret) {
+        LOGD(ERROR, domid, "device model did not start: %d", ret);
+        goto error_out;
+    }
+
+    dcs->device_type_idx = -1;
+    domcreate_attach_devices(egc, &dcs->multidev, 0);
+    return;
+
+error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
+static void domcreate_attach_devices(libxl__egc *egc,
+                                     libxl__multidev *multidev,
+                                     int ret)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
+    STATE_AO_GC(dcs->ao);
+    int domid = dcs->guest_domid;
+    libxl_domain_config *const d_config = dcs->guest_config;
+    const libxl__device_type *dt;
+    char *tty_path;
+
+    if (ret) {
+        LOGD(ERROR, domid, "unable to add %s devices",
+             libxl__device_kind_to_string(device_type_tbl[dcs->device_type_idx]->type));
+        goto error_out;
+    }
+
+    dcs->device_type_idx++;
+    dt = device_type_tbl[dcs->device_type_idx];
+    if (dt) {
+        if (*libxl__device_type_get_num(dt, d_config) > 0 && !dt->skip_attach) {
+            /* Attach devices */
+            libxl__multidev_begin(ao, &dcs->multidev);
+            dcs->multidev.callback = domcreate_attach_devices;
+            dt->add(egc, ao, domid, d_config, &dcs->multidev);
+            libxl__multidev_prepared(egc, &dcs->multidev, 0);
+            return;
+        }
+
+        domcreate_attach_devices(egc, &dcs->multidev, 0);
+        return;
+    }
+
+    ret = libxl__console_tty_path(gc, domid, 0, LIBXL_CONSOLE_TYPE_PV, &tty_path);
+    if (ret) {
+        LOG(ERROR, "failed to get domain %d console tty path",
+            domid);
+        goto error_out;
+    }
+
+    dcs->console_xswait.ao = ao;
+    dcs->console_xswait.what = GCSPRINTF("domain %d console tty", domid);
+    dcs->console_xswait.path = tty_path;
+    dcs->console_xswait.timeout_ms = LIBXL_INIT_TIMEOUT * 1000;
+    dcs->console_xswait.callback = console_xswait_callback;
+    ret = libxl__xswait_start(gc, &dcs->console_xswait);
+    if (ret) {
+        LOG(ERROR, "unable to set up watch for domain %d console tty path",
+            domid);
+        goto error_out;
+    }
+
+    return;
+
+error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
+static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
+                                    int rc, const char *p)
+{
+    EGC_GC;
+    libxl__domain_create_state *dcs = CONTAINER_OF(xswa, *dcs, console_xswait);
+
+    if (rc) {
+        if (rc == ERROR_TIMEDOUT)
+            LOG(ERROR, "%s: timed out", xswa->what);
+        goto out;
+    }
+
+    if (p && p[0] != '\0') {
+        domcreate_console_available(egc, dcs);
+        goto out;
+    }
+
+    return;
+
+out:
+    libxl__xswait_stop(gc, xswa);
+    domcreate_complete(egc, dcs, rc);
+}
+
+static void domcreate_complete(libxl__egc *egc,
+                               libxl__domain_create_state *dcs,
+                               int rc)
+{
+    STATE_AO_GC(dcs->ao);
+    libxl_domain_config *const d_config = dcs->guest_config;
+    libxl_domain_config *d_config_saved = &dcs->guest_config_saved;
+
+    libxl__xswait_stop(gc, &dcs->console_xswait);
+
+    libxl__domain_build_state_dispose(&dcs->build_state);
+
+    if (!rc && d_config->b_info.exec_ssidref)
+        rc = xc_flask_relabel_domain(CTX->xch, dcs->guest_domid, d_config->b_info.exec_ssidref);
+
+    bool retain_domain = !rc || rc == ERROR_ABORTED;
+
+    if (retain_domain) {
+        libxl__flock *lock;
+
+        /* Note that we hold CTX lock at this point so only need to
+         * take data store lock
+         */
+        lock = libxl__lock_domain_userdata(gc, dcs->guest_domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+        } else {
+            libxl__update_domain_configuration(gc, d_config_saved, d_config);
+            int cfg_rc = libxl__set_domain_configuration
+                (gc, dcs->guest_domid, d_config_saved);
+            if (!rc)
+                rc = cfg_rc;
+            libxl__unlock_file(lock);
+        }
+    }
+
+    libxl_domain_config_dispose(d_config_saved);
+
+    if (!retain_domain) {
+        if (dcs->guest_domid > 0) {
+            dcs->dds.ao = ao;
+            dcs->dds.domid = dcs->guest_domid;
+            dcs->dds.callback = domcreate_destruction_cb;
+            libxl__domain_destroy(egc, &dcs->dds);
+            return;
+        }
+        dcs->guest_domid = INVALID_DOMID;
+    }
+    dcs->callback(egc, dcs, rc, dcs->guest_domid);
+}
+
+static void domcreate_destruction_cb(libxl__egc *egc,
+                                     libxl__domain_destroy_state *dds,
+                                     int rc)
+{
+    STATE_AO_GC(dds->ao);
+    libxl__domain_create_state *dcs = CONTAINER_OF(dds, *dcs, dds);
+
+    if (rc)
+        LOGD(ERROR, dds->domid, "unable to destroy domain following failed creation");
+
+    dcs->callback(egc, dcs, ERROR_FAIL, dcs->guest_domid);
+}
+
+/*----- application-facing domain creation interface -----*/
+
+typedef struct {
+    libxl__domain_create_state dcs;
+    uint32_t *domid_out;
+} libxl__app_domain_create_state;
+
+typedef struct {
+    libxl__app_domain_create_state cdcs;
+    libxl__domain_destroy_state dds;
+    libxl__domain_save_state dss;
+    char *toolstack_buf;
+    uint32_t toolstack_len;
+} libxl__domain_soft_reset_state;
+
+static void domain_create_cb(libxl__egc *egc,
+                             libxl__domain_create_state *dcs,
+                             int rc, uint32_t domid);
+
+static int do_domain_create(libxl_ctx *ctx, libxl_domain_config *d_config,
+                            uint32_t *domid, int restore_fd, int send_back_fd,
+                            const libxl_domain_restore_params *params,
+                            const libxl_asyncop_how *ao_how,
+                            const libxl_asyncprogress_how *aop_console_how)
+{
+    AO_CREATE(ctx, 0, ao_how);
+    libxl__app_domain_create_state *cdcs;
+    int rc;
+
+    GCNEW(cdcs);
+    cdcs->dcs.ao = ao;
+    cdcs->dcs.guest_config = d_config;
+    libxl_domain_config_init(&cdcs->dcs.guest_config_saved);
+    libxl_domain_config_copy(ctx, &cdcs->dcs.guest_config_saved, d_config);
+    cdcs->dcs.restore_fd = cdcs->dcs.libxc_fd = restore_fd;
+    cdcs->dcs.send_back_fd = send_back_fd;
+    if (restore_fd >= 0) {
+        cdcs->dcs.restore_params = *params;
+        rc = libxl__fd_flags_modify_save(gc, cdcs->dcs.restore_fd,
+                                         ~(O_NONBLOCK|O_NDELAY), 0,
+                                         &cdcs->dcs.restore_fdfl);
+        if (rc < 0) goto out_err;
+    }
+    cdcs->dcs.callback = domain_create_cb;
+    cdcs->dcs.domid = INVALID_DOMID;
+    cdcs->dcs.soft_reset = false;
+
+    if (cdcs->dcs.restore_params.checkpointed_stream ==
+        LIBXL_CHECKPOINTED_STREAM_COLO) {
+        cdcs->dcs.colo_proxy_script =
+            cdcs->dcs.restore_params.colo_proxy_script;
+        cdcs->dcs.crs.cps.is_userspace_proxy =
+            libxl_defbool_val(cdcs->dcs.restore_params.userspace_colo_proxy);
+    } else {
+        cdcs->dcs.colo_proxy_script = NULL;
+        cdcs->dcs.crs.cps.is_userspace_proxy = false;
+    }
+
+    libxl__ao_progress_gethow(&cdcs->dcs.aop_console_how, aop_console_how);
+    cdcs->domid_out = domid;
+
+    initiate_domain_create(egc, &cdcs->dcs);
+
+    return AO_INPROGRESS;
+
+ out_err:
+    return AO_CREATE_FAIL(rc);
+
+}
+
+static void domain_soft_reset_cb(libxl__egc *egc,
+                                 libxl__domain_destroy_state *dds,
+                                 int rc)
+{
+    STATE_AO_GC(dds->ao);
+    libxl__domain_soft_reset_state *srs = CONTAINER_OF(dds, *srs, dds);
+    libxl__app_domain_create_state *cdcs = &srs->cdcs;
+    char *savefile, *restorefile;
+
+    if (rc) {
+        LOGD(ERROR, dds->domid, "destruction of domain failed.");
+        goto error;
+    }
+
+    cdcs->dcs.guest_domid = dds->domid;
+    rc = libxl__restore_emulator_xenstore_data(&cdcs->dcs, srs->toolstack_buf,
+                                               srs->toolstack_len);
+    if (rc) {
+        LOGD(ERROR, dds->domid, "failed to restore toolstack record.");
+        goto error;
+    }
+
+    if (cdcs->dcs.guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+        savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", dds->domid);
+        restorefile = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d",
+                                dds->domid);
+        rc = rename(savefile, restorefile);
+        if (rc) {
+            LOGD(ERROR, dds->domid, "failed to rename dm save file.");
+            goto error;
+        }
+    }
+
+    initiate_domain_create(egc, &cdcs->dcs);
+    return;
+
+error:
+    domcreate_complete(egc, &cdcs->dcs, rc);
+}
+
+static void soft_reset_dm_suspended(libxl__egc *egc,
+                                    libxl__domain_suspend_state *dsps,
+                                    int rc);
+static int do_domain_soft_reset(libxl_ctx *ctx,
+                                libxl_domain_config *d_config,
+                                uint32_t domid,
+                                const libxl_asyncop_how *ao_how,
+                                const libxl_asyncprogress_how
+                                *aop_console_how)
+{
+    AO_CREATE(ctx, 0, ao_how);
+    libxl__domain_soft_reset_state *srs;
+    libxl__app_domain_create_state *cdcs;
+    libxl__domain_create_state *dcs;
+    libxl__domain_build_state *state;
+    libxl__domain_save_state *dss;
+    const char *console_tty, *xs_store_mfn, *xs_console_mfn;
+    char *dom_path;
+    uint32_t domid_out;
+    int rc;
+
+    GCNEW(srs);
+    cdcs = &srs->cdcs;
+    dcs = &cdcs->dcs;
+    state = &dcs->build_state;
+    dss = &srs->dss;
+
+    srs->cdcs.dcs.ao = ao;
+    srs->cdcs.dcs.guest_config = d_config;
+    libxl_domain_config_init(&srs->cdcs.dcs.guest_config_saved);
+    libxl_domain_config_copy(ctx, &srs->cdcs.dcs.guest_config_saved,
+                             d_config);
+    cdcs->dcs.restore_fd = -1;
+    cdcs->dcs.domid = domid;
+    cdcs->dcs.soft_reset = true;
+    cdcs->dcs.callback = domain_create_cb;
+    libxl__ao_progress_gethow(&srs->cdcs.dcs.aop_console_how,
+                              aop_console_how);
+    cdcs->domid_out = &domid_out;
+
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path) {
+        LOGD(ERROR, domid, "failed to read domain path");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/store/ring-ref", dom_path),
+                                &xs_store_mfn);
+    if (rc) {
+        LOGD(ERROR, domid, "failed to read store/ring-ref.");
+        goto out;
+    }
+    state->store_mfn = xs_store_mfn ? atol(xs_store_mfn): 0;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/console/ring-ref", dom_path),
+                                &xs_console_mfn);
+    if (rc) {
+        LOGD(ERROR, domid, "failed to read console/ring-ref.");
+        goto out;
+    }
+    state->console_mfn = xs_console_mfn ? atol(xs_console_mfn): 0;
+
+    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+                                  GCSPRINTF("%s/console/tty", dom_path),
+                                  &console_tty);
+    if (rc) {
+        LOGD(ERROR, domid, "failed to read console/tty.");
+        goto out;
+    }
+    state->console_tty = libxl__strdup(gc, console_tty);
+
+    dss->ao = ao;
+    dss->domid = dss->dsps.domid = domid;
+    dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d",
+                                      domid);
+
+    rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf,
+                                            &srs->toolstack_len);
+    if (rc) {
+        LOGD(ERROR, domid, "failed to save toolstack record.");
+        goto out;
+    }
+
+    dss->dsps.ao = ao;
+    dss->dsps.callback_device_model_done = soft_reset_dm_suspended;
+    libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */
+
+    return AO_INPROGRESS;
+
+ out:
+    return AO_CREATE_FAIL(rc);
+}
+
+static void soft_reset_dm_suspended(libxl__egc *egc,
+                                    libxl__domain_suspend_state *dsps,
+                                    int rc)
+{
+    STATE_AO_GC(dsps->ao);
+    libxl__domain_soft_reset_state *srs =
+        CONTAINER_OF(dsps, *srs, dss.dsps);
+    libxl__app_domain_create_state *cdcs = &srs->cdcs;
+
+    /*
+     * Ask all backends to disconnect by removing the domain from
+     * xenstore. On the creation path the domain will be introduced to
+     * xenstore again with probably different store/console/...
+     * channels.
+     */
+    xs_release_domain(CTX->xsh, cdcs->dcs.domid);
+
+    srs->dds.ao = ao;
+    srs->dds.domid = cdcs->dcs.domid;
+    srs->dds.callback = domain_soft_reset_cb;
+    srs->dds.soft_reset = true;
+    libxl__domain_destroy(egc, &srs->dds);
+}
+
+static void domain_create_cb(libxl__egc *egc,
+                             libxl__domain_create_state *dcs,
+                             int rc, uint32_t domid)
+{
+    libxl__app_domain_create_state *cdcs = CONTAINER_OF(dcs, *cdcs, dcs);
+    int flrc;
+    STATE_AO_GC(cdcs->dcs.ao);
+
+    *cdcs->domid_out = domid;
+
+    if (dcs->restore_fd >= 0) {
+        flrc = libxl__fd_flags_restore(gc,
+                dcs->restore_fd, dcs->restore_fdfl);
+        /*
+         * If restore has failed already then report that error not
+         * this one.
+         */
+        if (flrc && !rc) rc = flrc;
+    }
+
+    libxl__ao_complete(egc, ao, rc);
+}
+
+
+static void set_disk_colo_restore(libxl_domain_config *d_config)
+{
+    int i;
+
+    for (i = 0; i < d_config->num_disks; i++)
+        libxl_defbool_set(&d_config->disks[i].colo_restore_enable, true);
+}
+
+static void unset_disk_colo_restore(libxl_domain_config *d_config)
+{
+    int i;
+
+    for (i = 0; i < d_config->num_disks; i++)
+        libxl_defbool_set(&d_config->disks[i].colo_restore_enable, false);
+}
+
+int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
+                            uint32_t *domid,
+                            const libxl_asyncop_how *ao_how,
+                            const libxl_asyncprogress_how *aop_console_how)
+{
+    unset_disk_colo_restore(d_config);
+    return do_domain_create(ctx, d_config, domid, -1, -1, NULL,
+                            ao_how, aop_console_how);
+}
+
+int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
+                                uint32_t *domid, int restore_fd,
+                                int send_back_fd,
+                                const libxl_domain_restore_params *params,
+                                const libxl_asyncop_how *ao_how,
+                                const libxl_asyncprogress_how *aop_console_how)
+{
+    if (params->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
+        set_disk_colo_restore(d_config);
+    } else {
+        unset_disk_colo_restore(d_config);
+    }
+
+    return do_domain_create(ctx, d_config, domid, restore_fd, send_back_fd,
+                            params, ao_how, aop_console_how);
+}
+
+int libxl_domain_soft_reset(libxl_ctx *ctx,
+                            libxl_domain_config *d_config,
+                            uint32_t domid,
+                            const libxl_asyncop_how *ao_how,
+                            const libxl_asyncprogress_how
+                            *aop_console_how)
+{
+    libxl_domain_build_info *const info = &d_config->b_info;
+
+    if (info->type != LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL;
+
+    return do_domain_soft_reset(ctx, d_config, domid, ao_how,
+                                aop_console_how);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_device.c b/tools/libs/light/libxl_device.c
new file mode 100644 (file)
index 0000000..e081faf
--- /dev/null
@@ -0,0 +1,2090 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+static char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device)
+{
+    char *dom_path = libxl__xs_get_dompath(gc, device->domid);
+
+    /* Console 0 is a special case */
+    if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0)
+        return GCSPRINTF("%s/%s", dom_path,
+                         libxl__device_kind_to_string(device->kind));
+
+    if (device->kind == LIBXL__DEVICE_KIND_VUART)
+        return GCSPRINTF("%s/%s/%d", dom_path,
+                         libxl__device_kind_to_string(device->kind),
+                         device->devid);
+
+    return GCSPRINTF("%s/device/%s/%d", dom_path,
+                     libxl__device_kind_to_string(device->kind),
+                     device->devid);
+}
+
+char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
+                                         libxl__device_kind device_kind)
+{
+    char *dom_path = libxl__xs_get_dompath(gc, domid);
+
+    return GCSPRINTF("%s/device/%s/%d", dom_path,
+                     libxl__device_kind_to_string(device_kind), devid);
+}
+
+char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device)
+{
+    char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid);
+
+    return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
+                     libxl__device_kind_to_string(device->backend_kind),
+                     device->domid, device->devid);
+}
+
+char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
+                                        uint32_t domid, uint32_t devid,
+                                        libxl__device_kind backend_kind)
+{
+    char *dom_path = libxl__xs_get_dompath(gc, backend_domid);
+
+    return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
+                     libxl__device_kind_to_string(backend_kind),
+                     domid, devid);
+}
+
+char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device)
+{
+    char *libxl_dom_path = libxl__xs_libxl_path(gc, device->domid);
+
+    return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
+                     libxl__device_kind_to_string(device->kind),
+                     device->devid);
+}
+
+char *libxl__domain_device_libxl_path(libxl__gc *gc,  uint32_t domid, uint32_t devid,
+                                      libxl__device_kind device_kind)
+{
+    char *libxl_dom_path = libxl__xs_libxl_path(gc, domid);
+
+    return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
+                     libxl__device_kind_to_string(device_kind), devid);
+}
+
+/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
+int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
+                         libxl__device *device)
+{
+    int rc;
+    char *be_path = libxl__device_libxl_path(gc, device);
+    const char *dir;
+
+    rc = libxl__xs_read_checked(gc, t, be_path, &dir);
+
+    if (rc)
+        return rc;
+
+    if (dir)
+        return 1;
+    return 0;
+}
+
+int libxl__parse_backend_path(libxl__gc *gc,
+                              const char *path,
+                              libxl__device *dev)
+{
+    /* /local/domain/<domid>/backend/<kind>/<domid>/<devid> */
+    char strkind[16]; /* Longest is actually "console" */
+    int rc = sscanf(path, "/local/domain/%d/backend/%15[^/]/%u/%d",
+                    &dev->backend_domid,
+                    strkind,
+                    &dev->domid,
+                    &dev->devid);
+
+    if (rc != 4)
+        return ERROR_FAIL;
+
+    return libxl__device_kind_from_string(strkind, &dev->backend_kind);
+}
+
+int libxl__nic_type(libxl__gc *gc, libxl__device *dev, libxl_nic_type *nictype)
+{
+    char *snictype, *be_path;
+    int rc = 0;
+
+    be_path = libxl__device_backend_path(gc, dev);
+    snictype = libxl__xs_read(gc, XBT_NULL,
+                              GCSPRINTF("%s/%s", be_path, "type"));
+    if (!snictype) {
+        LOGED(ERROR, dev->domid, "unable to read nictype from %s", be_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = libxl_nic_type_from_string(snictype, nictype);
+    if (rc) {
+        LOGED(ERROR, dev->domid, "unable to parse nictype from %s", be_path);
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
+        libxl__device *device, char **bents, char **fents, char **ro_fents)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *frontend_path = NULL, *backend_path = NULL, *libxl_path;
+    struct xs_permissions frontend_perms[2];
+    struct xs_permissions ro_frontend_perms[2];
+    struct xs_permissions backend_perms[2];
+    int create_transaction = t == XBT_NULL;
+    int libxl_only = device->backend_kind == LIBXL__DEVICE_KIND_NONE;
+    int rc;
+
+    if (libxl_only) {
+        /* bents should be set as this is used to setup libxl_path content. */
+        assert(!fents && !ro_fents);
+    } else {
+        frontend_path = libxl__device_frontend_path(gc, device);
+        backend_path = libxl__device_backend_path(gc, device);
+    }
+    libxl_path = libxl__device_libxl_path(gc, device);
+
+    frontend_perms[0].id = device->domid;
+    frontend_perms[0].perms = XS_PERM_NONE;
+    frontend_perms[1].id = device->backend_domid;
+    frontend_perms[1].perms = XS_PERM_READ;
+
+    ro_frontend_perms[0].id = backend_perms[0].id = device->backend_domid;
+    ro_frontend_perms[0].perms = backend_perms[0].perms = XS_PERM_NONE;
+    ro_frontend_perms[1].id = backend_perms[1].id = device->domid;
+    ro_frontend_perms[1].perms = backend_perms[1].perms = XS_PERM_READ;
+
+retry_transaction:
+    if (create_transaction)
+        t = xs_transaction_start(ctx->xsh);
+
+    /* FIXME: read frontend_path and check state before removing stuff */
+
+    rc = libxl__xs_rm_checked(gc, t, libxl_path);
+    if (rc) goto out;
+
+    if (!libxl_only) {
+        rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/frontend",libxl_path),
+                                     frontend_path);
+        if (rc) goto out;
+
+        rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/backend",libxl_path),
+                                     backend_path);
+        if (rc) goto out;
+    }
+
+    /* xxx much of this function lacks error checks! */
+
+    if (fents || ro_fents) {
+        xs_rm(ctx->xsh, t, frontend_path);
+        xs_mkdir(ctx->xsh, t, frontend_path);
+        /* Console 0 is a special case. It doesn't use the regular PV
+         * state machine but also the frontend directory has
+         * historically contained other information, such as the
+         * vnc-port, which we don't want the guest fiddling with.
+         */
+        if ((device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0) ||
+            (device->kind == LIBXL__DEVICE_KIND_VUART))
+            xs_set_permissions(ctx->xsh, t, frontend_path,
+                               ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
+        else
+            xs_set_permissions(ctx->xsh, t, frontend_path,
+                               frontend_perms, ARRAY_SIZE(frontend_perms));
+        xs_write(ctx->xsh, t, GCSPRINTF("%s/backend", frontend_path),
+                 backend_path, strlen(backend_path));
+        if (fents)
+            libxl__xs_writev_perms(gc, t, frontend_path, fents,
+                                   frontend_perms, ARRAY_SIZE(frontend_perms));
+        if (ro_fents)
+            libxl__xs_writev_perms(gc, t, frontend_path, ro_fents,
+                                   ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
+    }
+
+    if (bents) {
+        if (!libxl_only) {
+            xs_rm(ctx->xsh, t, backend_path);
+            xs_mkdir(ctx->xsh, t, backend_path);
+            xs_set_permissions(ctx->xsh, t, backend_path, backend_perms,
+                               ARRAY_SIZE(backend_perms));
+            xs_write(ctx->xsh, t, GCSPRINTF("%s/frontend", backend_path),
+                     frontend_path, strlen(frontend_path));
+            libxl__xs_writev(gc, t, backend_path, bents);
+        }
+
+        /*
+         * We make a copy of everything for the backend in the libxl
+         * path as well.  This means we don't need to trust the
+         * backend.  Ideally this information would not be used and we
+         * would use the information from the json configuration
+         * instead.  But there are still places in libxl that try to
+         * reconstruct a config from xenstore.
+         *
+         * For devices without PV backend (e.g. USB devices emulated via qemu)
+         * only the libxl path is written.
+         *
+         * This duplication will typically produces duplicate keys
+         * which will go out of date, but that's OK because nothing
+         * reads those.  For example, there is usually
+         *   /libxl/$guest/device/$kind/$devid/state
+         * which starts out containing XenbusStateInitialising ("1")
+         * just like the copy in
+         *  /local/domain/$driverdom/backend/$guest/$kind/$devid/state
+         * but which won't ever be updated.
+         *
+         * This duplication is superfluous and messy but as discussed
+         * the proper fix is more intrusive than we want to do now.
+         */
+        rc = libxl__xs_writev(gc, t, libxl_path, bents);
+        if (rc) goto out;
+    }
+
+    if (!create_transaction)
+        return 0;
+
+    if (!xs_transaction_end(ctx->xsh, t, 0)) {
+        if (errno == EAGAIN)
+            goto retry_transaction;
+        else {
+            LOGED(ERROR, device->domid, "xs transaction failed");
+            return ERROR_FAIL;
+        }
+    }
+    return 0;
+
+ out:
+    if (create_transaction && t)
+        libxl__xs_transaction_abort(gc, &t);
+    return rc;
+}
+
+typedef struct {
+    libxl__gc *gc;
+    libxl_device_disk *disk;
+    struct stat stab;
+} disk_try_backend_args;
+
+static int disk_try_backend(disk_try_backend_args *a,
+                            libxl_disk_backend backend)
+ {
+    libxl__gc *gc = a->gc;
+    /* returns 0 (ie, DISK_BACKEND_UNKNOWN) on failure, or
+     * backend on success */
+
+    switch (backend) {
+    case LIBXL_DISK_BACKEND_PHY:
+        if (a->disk->format != LIBXL_DISK_FORMAT_RAW) {
+            goto bad_format;
+        }
+
+        if (libxl_defbool_val(a->disk->colo_enable))
+            goto bad_colo;
+
+        if (a->disk->backend_domid != LIBXL_TOOLSTACK_DOMID) {
+            LOG(DEBUG, "Disk vdev=%s, is using a storage driver domain, "
+                       "skipping physical device check", a->disk->vdev);
+            return backend;
+        }
+
+        if (a->disk->script) {
+            LOG(DEBUG, "Disk vdev=%s, uses script=... assuming phy backend",
+                a->disk->vdev);
+            return backend;
+        }
+
+        if (libxl__try_phy_backend(a->stab.st_mode))
+            return backend;
+
+        LOG(DEBUG, "Disk vdev=%s, backend phy unsuitable as phys path not a "
+                   "block device", a->disk->vdev);
+        return 0;
+
+    case LIBXL_DISK_BACKEND_TAP:
+        LOG(DEBUG, "Disk vdev=%s, backend tap unsuitable because blktap "
+                   "not available", a->disk->vdev);
+        return 0;
+
+    case LIBXL_DISK_BACKEND_QDISK:
+        if (a->disk->script) goto bad_script;
+        return backend;
+
+    default:
+        LOG(DEBUG, "Disk vdev=%s, backend %d unknown", a->disk->vdev, backend);
+        return 0;
+
+    }
+    abort(); /* notreached */
+
+ bad_format:
+    LOG(DEBUG, "Disk vdev=%s, backend %s unsuitable due to format %s",
+               a->disk->vdev,
+               libxl_disk_backend_to_string(backend),
+               libxl_disk_format_to_string(a->disk->format));
+    return 0;
+
+ bad_script:
+    LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with script=...",
+        a->disk->vdev, libxl_disk_backend_to_string(backend));
+    return 0;
+
+ bad_colo:
+    LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with colo",
+        a->disk->vdev, libxl_disk_backend_to_string(backend));
+    return 0;
+}
+
+int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
+                                   libxl_domid *domid_out) {
+    int r;
+    unsigned int domid_sc;
+    char delim_sc;
+
+    r = sscanf(be_path, "/local/domain/%u%c", &domid_sc, &delim_sc);
+    if (!(r==2 && delim_sc=='/')) {
+        LOG(ERROR, "internal error: backend path %s unparseable!", be_path);
+        return ERROR_FAIL;
+    }
+    *domid_out = domid_sc;
+    return 0;
+}
+
+int libxl__device_disk_set_backend(libxl__gc *gc, libxl_device_disk *disk) {
+    libxl_disk_backend ok;
+    disk_try_backend_args a;
+
+    a.gc = gc;
+    a.disk = disk;
+
+    LOG(DEBUG, "Disk vdev=%s spec.backend=%s", disk->vdev,
+               libxl_disk_backend_to_string(disk->backend));
+
+    if (disk->format == LIBXL_DISK_FORMAT_EMPTY) {
+        if (!disk->is_cdrom) {
+            LOG(ERROR, "Disk vdev=%s is empty but not cdrom", disk->vdev);
+            return ERROR_INVAL;
+        }
+        if (disk->pdev_path != NULL && strcmp(disk->pdev_path, "")) {
+            LOG(ERROR,
+                "Disk vdev=%s is empty but an image has been provided: %s",
+                disk->vdev, disk->pdev_path);
+            return ERROR_INVAL;
+        }
+        memset(&a.stab, 0, sizeof(a.stab));
+    } else if ((disk->backend == LIBXL_DISK_BACKEND_UNKNOWN ||
+                disk->backend == LIBXL_DISK_BACKEND_PHY) &&
+               disk->backend_domid == LIBXL_TOOLSTACK_DOMID &&
+               !disk->script) {
+        if (stat(disk->pdev_path, &a.stab)) {
+            LOGE(ERROR, "Disk vdev=%s failed to stat: %s",
+                        disk->vdev, disk->pdev_path);
+            return ERROR_INVAL;
+        }
+    }
+
+    if (disk->backend != LIBXL_DISK_BACKEND_UNKNOWN) {
+        ok= disk_try_backend(&a, disk->backend);
+    } else {
+        ok=
+            disk_try_backend(&a, LIBXL_DISK_BACKEND_PHY) ?:
+            disk_try_backend(&a, LIBXL_DISK_BACKEND_QDISK) ?:
+            disk_try_backend(&a, LIBXL_DISK_BACKEND_TAP);
+        if (ok)
+            LOG(DEBUG, "Disk vdev=%s, using backend %s",
+                       disk->vdev,
+                       libxl_disk_backend_to_string(ok));
+    }
+    if (!ok) {
+        LOG(ERROR, "no suitable backend for disk %s", disk->vdev);
+        return ERROR_INVAL;
+    }
+    disk->backend = ok;
+    return 0;
+}
+
+char *libxl__device_disk_string_of_format(libxl_disk_format format)
+{
+    switch (format) {
+        case LIBXL_DISK_FORMAT_QCOW: return "qcow";
+        case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
+        case LIBXL_DISK_FORMAT_VHD: return "vhd";
+        case LIBXL_DISK_FORMAT_RAW:
+        case LIBXL_DISK_FORMAT_EMPTY: return "aio";
+        case LIBXL_DISK_FORMAT_QED: return "qed";
+        default: return NULL;
+    }
+}
+
+char *libxl__device_disk_string_of_backend(libxl_disk_backend backend)
+{
+    switch (backend) {
+        case LIBXL_DISK_BACKEND_QDISK: return "qdisk";
+        case LIBXL_DISK_BACKEND_TAP: return "phy";
+        case LIBXL_DISK_BACKEND_PHY: return "phy";
+        default: return NULL;
+    }
+}
+
+const char *libxl__qemu_disk_format_string(libxl_disk_format format)
+{
+    switch (format) {
+    case LIBXL_DISK_FORMAT_QCOW: return "qcow";
+    case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
+    case LIBXL_DISK_FORMAT_VHD: return "vpc";
+    case LIBXL_DISK_FORMAT_RAW: return "raw";
+    case LIBXL_DISK_FORMAT_EMPTY: return NULL;
+    case LIBXL_DISK_FORMAT_QED: return "qed";
+    default: return NULL;
+    }
+}
+
+int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor)
+{
+    struct stat buf;
+    if (stat(physpath, &buf) < 0)
+        return -1;
+    if (!S_ISBLK(buf.st_mode))
+        return -1;
+    *major = major(buf.st_rdev);
+    *minor = minor(buf.st_rdev);
+    return 0;
+}
+
+static int device_virtdisk_matches(const char *virtpath, const char *devtype,
+                                   int *index_r, int max_index,
+                                   int *partition_r, int max_partition) {
+    const char *p;
+    char *ep;
+    int tl, c;
+    long pl;
+
+    tl = strlen(devtype);
+    if (memcmp(virtpath, devtype, tl))
+        return 0;
+
+    /* We decode the drive letter as if it were in base 52
+     * with digits a-zA-Z, more or less */
+    *index_r = -1;
+    p = virtpath + tl;
+    for (;;) {
+        c = *p++;
+        if (c >= 'a' && c <= 'z') {
+            c -= 'a';
+        } else {
+            --p;
+            break;
+        }
+        (*index_r)++;
+        (*index_r) *= 26;
+        (*index_r) += c;
+
+        if (*index_r > max_index)
+            return 0;
+    }
+
+    if (!*p) {
+        *partition_r = 0;
+        return 1;
+    }
+
+    if (*p=='0')
+        return 0; /* leading zeroes not permitted in partition number */
+
+    pl = strtoul(p, &ep, 10);
+    if (pl > max_partition || *ep)
+        return 0;
+
+    *partition_r = pl;
+    return 1;
+}
+
+int libxl__device_disk_dev_number(const char *virtpath, int *pdisk,
+                                  int *ppartition)
+{
+    int disk, partition;
+    char *ep;
+    unsigned long ul;
+    int chrused;
+
+    chrused = -1;
+    if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused)  >= 2
+         && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
+        ||
+        device_virtdisk_matches(virtpath, "xvd",
+                                &disk, (1<<20)-1,
+                                &partition, 255)) {
+        if (pdisk) *pdisk = disk;
+        if (ppartition) *ppartition = partition;
+        if (disk <= 15 && partition <= 15)
+            return (202 << 8) | (disk << 4) | partition;
+        else
+            return (1 << 28) | (disk << 8) | partition;
+    }
+
+    errno = 0;
+    ul = strtoul(virtpath, &ep, 0);
+    if (!errno && !*ep && ul <= INT_MAX) {
+        /* FIXME: should parse ul to determine these. */
+        if (pdisk || ppartition)
+            return -1;
+        return ul;
+    }
+
+    if (device_virtdisk_matches(virtpath, "hd",
+                                &disk, 3,
+                                &partition, 63)) {
+        if (pdisk) *pdisk = disk;
+        if (ppartition) *ppartition = partition;
+        return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
+    }
+    if (device_virtdisk_matches(virtpath, "sd",
+                                &disk, 15,
+                                &partition, 15)) {
+        if (pdisk) *pdisk = disk;
+        if (ppartition) *ppartition = partition;
+        return (8 << 8) | (disk << 4) | partition;
+    }
+    return -1;
+}
+
+static char *encode_disk_name(char *ptr, unsigned int n)
+{
+    if (n >= 26)
+        ptr = encode_disk_name(ptr, n / 26 - 1);
+    *ptr = 'a' + n % 26;
+    return ptr + 1;
+}
+
+char *libxl__devid_to_vdev(libxl__gc *gc, int devid)
+{
+    unsigned int minor;
+    int offset;
+    int nr_parts;
+    char *ptr = NULL;
+/* Same as in Linux.
+ * encode_disk_name might end up using up to 29 bytes (BUFFER_SIZE - 3)
+ * including the trailing \0.
+ *
+ * The code is safe because 26 raised to the power of 28 (that is the
+ * maximum offset that can be stored in the allocated buffer as a
+ * string) is far greater than UINT_MAX on 64 bits so offset cannot be
+ * big enough to exhaust the available bytes in ret. */
+#define BUFFER_SIZE 32
+    char *ret = libxl__zalloc(gc, BUFFER_SIZE);
+
+#define EXT_SHIFT 28
+#define EXTENDED (1<<EXT_SHIFT)
+#define VDEV_IS_EXTENDED(dev) ((dev)&(EXTENDED))
+#define BLKIF_MINOR_EXT(dev) ((dev)&(~EXTENDED))
+/* the size of the buffer to store the device name is 32 bytes to match the
+ * equivalent buffer in the Linux kernel code */
+
+    if (!VDEV_IS_EXTENDED(devid)) {
+        minor = devid & 0xff;
+        nr_parts = 16;
+    } else {
+        minor = BLKIF_MINOR_EXT(devid);
+        nr_parts = 256;
+    }
+    offset = minor / nr_parts;
+
+    strcpy(ret, "xvd");
+    ptr = encode_disk_name(ret + 3, offset);
+    if (minor % nr_parts == 0)
+        *ptr = 0;
+    else
+        /* overflow cannot happen, thanks to the upper bound */
+        snprintf(ptr, ret + 32 - ptr,
+                "%d", minor & (nr_parts - 1));
+    return ret;
+#undef BUFFER_SIZE
+#undef EXT_SHIFT
+#undef EXTENDED
+#undef VDEV_IS_EXTENDED
+#undef BLKIF_MINOR_EXT
+}
+
+/* Device AO operations */
+
+void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev)
+{
+    aodev->ao = ao;
+    aodev->rc = 0;
+    aodev->dev = NULL;
+    aodev->num_exec = 0;
+    /* Initialize timer for QEMU Bodge */
+    libxl__ev_time_init(&aodev->timeout);
+    /*
+     * Initialize xs_watch, because it's not used on all possible
+     * execution paths, but it's unconditionally destroyed when finished.
+     */
+    libxl__xswait_init(&aodev->xswait);
+    aodev->active = 1;
+    /* We init this here because we might call device_hotplug_done
+     * without actually calling any hotplug script */
+    libxl__async_exec_init(&aodev->aes);
+    libxl__ev_child_init(&aodev->child);
+
+    libxl__ev_qmp_init(&aodev->qmp);
+}
+
+/* multidev */
+
+void libxl__multidev_begin(libxl__ao *ao, libxl__multidev *multidev)
+{
+    AO_GC;
+
+    multidev->ao = ao;
+    multidev->array = 0;
+    multidev->used = multidev->allocd = 0;
+
+    /* We allocate an aodev to represent the operation of preparing
+     * all of the other operations.  This operation is completed when
+     * we have started all the others (ie, when the user calls
+     * _prepared).  That arranges automatically that
+     *  (i) we do not think we have finished even if one of the
+     *      operations completes while we are still preparing
+     *  (ii) if we are starting zero operations, we do still
+     *      make the callback as soon as we know this fact
+     *  (iii) we have a nice consistent way to deal with any
+     *      error that might occur while deciding what to initiate
+     */
+    multidev->preparation = libxl__multidev_prepare(multidev);
+}
+
+void libxl__multidev_prepare_with_aodev(libxl__multidev *multidev,
+                                        libxl__ao_device *aodev) {
+    STATE_AO_GC(multidev->ao);
+
+    aodev->multidev = multidev;
+    aodev->callback = libxl__multidev_one_callback;
+    libxl__prepare_ao_device(ao, aodev);
+
+    if (multidev->used >= multidev->allocd) {
+        multidev->allocd = multidev->used * 2 + 5;
+        GCREALLOC_ARRAY(multidev->array, multidev->allocd);
+    }
+    multidev->array[multidev->used++] = aodev;
+}
+
+libxl__ao_device *libxl__multidev_prepare(libxl__multidev *multidev) {
+    STATE_AO_GC(multidev->ao);
+    libxl__ao_device *aodev;
+
+    GCNEW(aodev);
+    libxl__multidev_prepare_with_aodev(multidev, aodev);
+
+    return aodev;
+}
+
+void libxl__multidev_one_callback(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl__multidev *multidev = aodev->multidev;
+    int i, error = 0;
+
+    aodev->active = 0;
+
+    for (i = 0; i < multidev->used; i++) {
+        if (multidev->array[i]->active)
+            return;
+
+        if (multidev->array[i]->rc)
+            error = multidev->array[i]->rc;
+    }
+
+    multidev->callback(egc, multidev, error);
+    return;
+}
+
+void libxl__multidev_prepared(libxl__egc *egc,
+                              libxl__multidev *multidev, int rc)
+{
+    multidev->preparation->rc = rc;
+    libxl__multidev_one_callback(egc, multidev->preparation);
+}
+
+/******************************************************************************/
+
+int libxl__device_destroy(libxl__gc *gc, libxl__device *dev)
+{
+    const char *be_path = NULL;
+    const char *fe_path = NULL;
+    const char *libxl_path = libxl__device_libxl_path(gc, dev);
+    xs_transaction_t t = 0;
+    int rc;
+    uint32_t domid;
+    int libxl_only = dev->backend_kind == LIBXL__DEVICE_KIND_NONE;
+
+    if (!libxl_only) {
+        be_path = libxl__device_backend_path(gc, dev);
+        fe_path = libxl__device_frontend_path(gc, dev);
+    }
+
+    rc = libxl__get_domid(gc, &domid);
+    if (rc) goto out;
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        if (domid == LIBXL_TOOLSTACK_DOMID) {
+            /*
+             * The toolstack domain is in charge of removing the
+             * frontend and libxl paths.
+             */
+            if (!libxl_only)
+                libxl__xs_path_cleanup(gc, t, fe_path);
+            libxl__xs_path_cleanup(gc, t, libxl_path);
+        }
+        if (dev->backend_domid == domid && !libxl_only) {
+            /*
+             * The driver domain is in charge of removing what it can
+             * from the backend path.
+             */
+            libxl__xs_path_cleanup(gc, t, be_path);
+        }
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    return rc;
+}
+
+/* Callback for device destruction */
+
+static void devices_remove_callback(libxl__egc *egc,
+                                    libxl__multidev *multidev, int rc);
+
+void libxl__devices_destroy(libxl__egc *egc, libxl__devices_remove_state *drs)
+{
+    STATE_AO_GC(drs->ao);
+    uint32_t domid = drs->domid;
+    char *path;
+    unsigned int num_kinds, num_dev_xsentries;
+    char **kinds = NULL, **devs = NULL;
+    int i, j, rc = 0;
+    libxl__device *dev;
+    libxl__multidev *multidev = &drs->multidev;
+    libxl__ao_device *aodev;
+    libxl__device_kind kind;
+
+    libxl__multidev_begin(ao, multidev);
+    multidev->callback = devices_remove_callback;
+
+    path = GCSPRINTF("/libxl/%d/device", domid);
+    kinds = libxl__xs_directory(gc, XBT_NULL, path, &num_kinds);
+    if (!kinds) {
+        if (errno != ENOENT) {
+            LOGE(ERROR, "unable to get xenstore device listing %s", path);
+            goto out;
+        }
+        num_kinds = 0;
+    }
+    for (i = 0; i < num_kinds; i++) {
+        if (libxl__device_kind_from_string(kinds[i], &kind))
+            continue;
+
+        path = GCSPRINTF("/libxl/%d/device/%s", domid, kinds[i]);
+        devs = libxl__xs_directory(gc, XBT_NULL, path, &num_dev_xsentries);
+        if (!devs)
+            continue;
+        for (j = 0; j < num_dev_xsentries; j++) {
+            path = GCSPRINTF("/libxl/%d/device/%s/%s/backend",
+                             domid, kinds[i], devs[j]);
+            path = libxl__xs_read(gc, XBT_NULL, path);
+            GCNEW(dev);
+            if (path && libxl__parse_backend_path(gc, path, dev) == 0) {
+                dev->domid = domid;
+                dev->kind = kind;
+                dev->devid = atoi(devs[j]);
+                if (dev->backend_kind == LIBXL__DEVICE_KIND_CONSOLE ||
+                    dev->backend_kind == LIBXL__DEVICE_KIND_VUART) {
+                    /* Currently console devices can be destroyed
+                     * synchronously by just removing xenstore entries,
+                     * this is what libxl__device_destroy does.
+                     */
+                    libxl__device_destroy(gc, dev);
+                    continue;
+                }
+                aodev = libxl__multidev_prepare(multidev);
+                aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+                aodev->dev = dev;
+                aodev->force = drs->force;
+                if (dev->kind == LIBXL__DEVICE_KIND_VUSB)
+                    libxl__initiate_device_usbctrl_remove(egc, aodev);
+                else
+                    libxl__initiate_device_generic_remove(egc, aodev);
+            }
+        }
+    }
+
+out:
+    libxl__multidev_prepared(egc, multidev, rc);
+}
+
+/* Callbacks for device related operations */
+
+/*
+ * device_backend_callback is the main callback entry point, for both device
+ * addition and removal. It gets called if we reach the desired state
+ * (XenbusStateClosed or XenbusStateInitWait). After that, all this
+ * functions get called in the order displayed below.
+ *
+ * If new device types are added, they should only need to modify the
+ * specific hotplug scripts call, which can be found in each OS specific
+ * file. If this new devices don't need a hotplug script, no modification
+ * should be needed.
+ */
+
+/* This callback is part of the Qemu devices Badge */
+static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                                const struct timeval *requested_abs, int rc);
+
+static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
+                                   int rc);
+
+static void device_backend_cleanup(libxl__gc *gc,
+                                   libxl__ao_device *aodev);
+
+static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev);
+
+static void device_hotplug_child_death_cb(libxl__egc *egc,
+                                          libxl__async_exec_state *aes,
+                                          int rc, int status);
+
+static void device_destroy_be_watch_cb(libxl__egc *egc,
+                                       libxl__xswait_state *xswait,
+                                       int rc, const char *data);
+
+static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev);
+
+static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev);
+
+void libxl__wait_device_connection(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    char *be_path = libxl__device_backend_path(gc, aodev->dev);
+    char *state_path = GCSPRINTF("%s/state", be_path);
+    int rc = 0;
+
+    if (QEMU_BACKEND(aodev->dev)) {
+        /*
+         * If Qemu is not running, there's no point in waiting for
+         * it to change the state of the device.
+         *
+         * If Qemu is running, it will set the state of the device to
+         * 4 directly, without waiting in state 2 for any hotplug execution.
+         */
+        device_hotplug(egc, aodev);
+        return;
+    }
+
+    rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
+                                 device_backend_callback,
+                                 state_path, XenbusStateInitWait,
+                                 LIBXL_INIT_TIMEOUT * 1000);
+    if (rc) {
+        LOGD(ERROR, aodev->dev->domid, "unable to initialize device %s", be_path);
+        goto out;
+    }
+
+    return;
+
+out:
+    aodev->rc = rc;
+    device_hotplug_done(egc, aodev);
+    return;
+}
+
+void libxl__initiate_device_generic_remove(libxl__egc *egc,
+                                           libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    xs_transaction_t t = 0;
+    char *be_path = libxl__device_backend_path(gc, aodev->dev);
+    char *state_path = GCSPRINTF("%s/state", be_path);
+    char *online_path = GCSPRINTF("%s/online", be_path);
+    const char *state;
+    libxl_dominfo info;
+    uint32_t my_domid, domid = aodev->dev->domid;
+    int rc = 0;
+
+    libxl_dominfo_init(&info);
+
+    rc = libxl__get_domid(gc, &my_domid);
+    if (rc) {
+        LOGD(ERROR, domid, "unable to get my domid");
+        goto out;
+    }
+
+    if (my_domid == LIBXL_TOOLSTACK_DOMID) {
+        rc = libxl_domain_info(CTX, &info, domid);
+        if (rc) {
+            LOGD(ERROR, domid, "unable to get info for domain %d", domid);
+            goto out;
+        }
+        if (QEMU_BACKEND(aodev->dev) &&
+            (info.paused || info.dying || info.shutdown)) {
+            /*
+             * TODO: 4.2 Bodge due to QEMU, see comment on top of
+             * libxl__initiate_device_generic_remove in libxl_internal.h
+             */
+            rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+                                             device_qemu_timeout,
+                                             LIBXL_QEMU_BODGE_TIMEOUT * 1000);
+            if (rc) {
+                LOGD(ERROR, domid, "unable to register timeout for Qemu device %s",
+                            be_path);
+                goto out;
+            }
+            goto out_success;
+        }
+    }
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) {
+            LOGD(ERROR, domid, "unable to start transaction");
+            goto out;
+        }
+
+        if (aodev->force.flag == LIBXL__FORCE_ON)
+            libxl__xs_path_cleanup(gc, t,
+                                   libxl__device_frontend_path(gc, aodev->dev));
+
+        rc = libxl__xs_read_checked(gc, t, state_path, &state);
+        if (rc) {
+            LOGD(ERROR, domid, "unable to read device state from path %s", state_path);
+            goto out;
+        }
+
+        /* if state_path is empty, assume backend is gone (backend domain
+         * shutdown?), cleanup frontend only; rc=0 */
+        if (!state) {
+            LOG(INFO, "backend %s already removed, cleanup frontend only", be_path);
+            goto out;
+        }
+
+        rc = libxl__xs_write_checked(gc, t, online_path, "0");
+        if (rc)
+            goto out;
+
+        /*
+         * Check if device is already in "closed" state, in which case
+         * it should not be changed.
+         */
+         if (state && atoi(state) != XenbusStateClosed) {
+            rc = libxl__xs_write_checked(gc, t, state_path, GCSPRINTF("%d", XenbusStateClosing));
+            if (rc) {
+                LOGD(ERROR, domid, "unable to write to xenstore path %s", state_path);
+                goto out;
+            }
+        }
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
+                                 device_backend_callback,
+                                 state_path, XenbusStateClosed,
+                                 LIBXL_DESTROY_TIMEOUT * 1000);
+    if (rc) {
+        LOGD(ERROR, domid, "unable to remove device %s", be_path);
+        goto out;
+    }
+
+out_success:
+    libxl_dominfo_dispose(&info);
+    return;
+
+out:
+    aodev->rc = rc;
+    libxl_dominfo_dispose(&info);
+    libxl__xs_transaction_abort(gc, &t);
+    device_hotplug_done(egc, aodev);
+    return;
+}
+
+static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                                const struct timeval *requested_abs, int rc)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+    STATE_AO_GC(aodev->ao);
+    char *be_path = libxl__device_backend_path(gc, aodev->dev);
+    char *state_path = GCSPRINTF("%s/state", be_path);
+    const char *xs_state;
+    xs_transaction_t t = 0;
+
+    if (rc != ERROR_TIMEDOUT)
+        goto out;
+
+    libxl__ev_time_deregister(gc, &aodev->timeout);
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) {
+            LOGD(ERROR, aodev->dev->domid, "unable to start transaction");
+            goto out;
+        }
+
+        /*
+         * Check that the state path exists and is actually different than
+         * 6 before unconditionally setting it. If Qemu runs on a driver
+         * domain it is possible that the driver domain has already cleaned
+         * the backend path if the device has reached state 6.
+         */
+        rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &xs_state);
+        if (rc) goto out;
+
+        if (xs_state && atoi(xs_state) != XenbusStateClosed) {
+            rc = libxl__xs_write_checked(gc, XBT_NULL, state_path, GCSPRINTF("%d", XenbusStateClosed));
+            if (rc) goto out;
+        }
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    device_hotplug(egc, aodev);
+    return;
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    aodev->rc = rc;
+    device_hotplug_done(egc, aodev);
+}
+
+static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
+                                   int rc) {
+    libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds);
+    STATE_AO_GC(aodev->ao);
+
+    LOGD(DEBUG, aodev->dev->domid, "calling device_backend_cleanup");
+    device_backend_cleanup(gc, aodev);
+
+    if (rc == ERROR_TIMEDOUT &&
+        aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
+        aodev->force.flag == LIBXL__FORCE_AUTO) {
+        LOGD(DEBUG, aodev->dev->domid, "Timeout reached, initiating forced remove");
+        aodev->force.flag = LIBXL__FORCE_ON;
+        libxl__initiate_device_generic_remove(egc, aodev);
+        return;
+    }
+
+    if (rc) {
+        LOGD(ERROR, aodev->dev->domid, "unable to %s device with path %s",
+                    libxl__device_action_to_string(aodev->action),
+                    libxl__device_backend_path(gc, aodev->dev));
+        goto out;
+    }
+
+    device_hotplug(egc, aodev);
+    return;
+
+out:
+    aodev->rc = rc;
+    device_hotplug_done(egc, aodev);
+    return;
+}
+
+static void device_backend_cleanup(libxl__gc *gc, libxl__ao_device *aodev)
+{
+    if (!aodev) return;
+    libxl__ev_devstate_cancel(gc, &aodev->backend_ds);
+}
+
+static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl__async_exec_state *aes = &aodev->aes;
+    char *be_path = libxl__device_backend_path(gc, aodev->dev);
+    char **args = NULL, **env = NULL;
+    int rc = 0;
+    int hotplug, nullfd = -1;
+    uint32_t domid;
+
+    /*
+     * If device is attached from a driver domain don't try to execute
+     * hotplug scripts
+     */
+    rc = libxl__get_domid(gc, &domid);
+    if (rc) {
+        LOGD(ERROR, aodev->dev->domid, "Failed to get domid");
+        goto out;
+    }
+    if (aodev->dev->backend_domid != domid) {
+        LOGD(DEBUG, aodev->dev->domid,
+             "Backend domid %d, domid %d, assuming driver domains",
+             aodev->dev->backend_domid, domid);
+
+        if (aodev->action != LIBXL__DEVICE_ACTION_REMOVE) {
+            LOG(DEBUG, "Not a remove, not executing hotplug scripts");
+            goto out;
+        }
+
+        aodev->xswait.ao = ao;
+        aodev->xswait.what = "removal of backend path";
+        aodev->xswait.path = be_path;
+        aodev->xswait.timeout_ms = LIBXL_DESTROY_TIMEOUT * 1000;
+        aodev->xswait.callback = device_destroy_be_watch_cb;
+        rc = libxl__xswait_start(gc, &aodev->xswait);
+        if (rc) {
+            LOGD(ERROR, aodev->dev->domid,
+                 "Setup of backend removal watch failed (path %s)", be_path);
+            goto out;
+        }
+
+        return;
+    }
+
+    /* Check if we have to execute hotplug scripts for this device
+     * and return the necessary args/env vars for execution */
+    hotplug = libxl__get_hotplug_script_info(gc, aodev->dev, &args, &env,
+                                             aodev->action,
+                                             aodev->num_exec);
+    switch (hotplug) {
+    case 0:
+        /* no hotplug script to execute */
+        LOGD(DEBUG, aodev->dev->domid, "No hotplug script to execute");
+        goto out;
+    case 1:
+        /* execute hotplug script */
+        break;
+    default:
+        /* everything else is an error */
+        LOGD(ERROR, aodev->dev->domid,
+                    "unable to get args/env to execute hotplug script for "
+                    "device %s", libxl__device_backend_path(gc, aodev->dev));
+        rc = hotplug;
+        goto out;
+    }
+
+    assert(args != NULL);
+    LOGD(DEBUG, aodev->dev->domid, "calling hotplug script: %s %s", args[0], args[1]);
+    LOGD(DEBUG, aodev->dev->domid, "extra args:");
+    {
+        const char *arg;
+        unsigned int x;
+
+        for (x = 2; (arg = args[x]); x++)
+            LOGD(DEBUG, aodev->dev->domid, "\t%s", arg);
+    }
+    LOGD(DEBUG, aodev->dev->domid, "env:");
+    if (env != NULL) {
+        const char *k, *v;
+        unsigned int x;
+
+        for (x = 0; (k = env[x]); x += 2) {
+            v = env[x+1];
+            LOGD(DEBUG, aodev->dev->domid, "\t%s: %s", k, v);
+        }
+    }
+
+    nullfd = open("/dev/null", O_RDONLY);
+    if (nullfd < 0) {
+        LOGD(ERROR, aodev->dev->domid, "unable to open /dev/null for hotplug script");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    aes->ao = ao;
+    aes->what = GCSPRINTF("%s %s", args[0], args[1]);
+    aes->env = env;
+    aes->args = args;
+    aes->callback = device_hotplug_child_death_cb;
+    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+    aes->stdfds[0] = nullfd;
+    aes->stdfds[1] = 2;
+    aes->stdfds[2] = -1;
+
+    rc = libxl__async_exec_start(aes);
+    if (rc)
+        goto out;
+
+    close(nullfd);
+    assert(libxl__async_exec_inuse(&aodev->aes));
+
+    return;
+
+out:
+    if (nullfd >= 0) close(nullfd);
+    aodev->rc = rc;
+    device_hotplug_done(egc, aodev);
+    return;
+}
+
+static void device_hotplug_child_death_cb(libxl__egc *egc,
+                                          libxl__async_exec_state *aes,
+                                          int rc, int status)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+    STATE_AO_GC(aodev->ao);
+    char *be_path = libxl__device_backend_path(gc, aodev->dev);
+    char *hotplug_error;
+
+    device_hotplug_clean(gc, aodev);
+
+    if (status && !rc) {
+        hotplug_error = libxl__xs_read(gc, XBT_NULL,
+                                       GCSPRINTF("%s/hotplug-error", be_path));
+        if (hotplug_error)
+            LOG(ERROR, "script: %s", hotplug_error);
+        rc = ERROR_FAIL;
+    }
+
+    if (rc) {
+        if (!aodev->rc)
+            aodev->rc = rc;
+        if (aodev->action == LIBXL__DEVICE_ACTION_ADD)
+            /*
+             * Only fail on device connection, on disconnection
+             * ignore error, and continue with the remove process
+             */
+             goto error;
+    }
+
+    /* Increase num_exec and call hotplug scripts again if necessary
+     * If no more executions are needed, device_hotplug will call
+     * device_hotplug_done breaking the loop.
+     */
+    aodev->num_exec++;
+    device_hotplug(egc, aodev);
+
+    return;
+
+error:
+    assert(aodev->rc);
+    device_hotplug_done(egc, aodev);
+}
+
+static void device_destroy_be_watch_cb(libxl__egc *egc,
+                                       libxl__xswait_state *xswait,
+                                       int rc, const char *dir)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(xswait, *aodev, xswait);
+    STATE_AO_GC(aodev->ao);
+
+    if (rc) {
+        if (rc == ERROR_TIMEDOUT)
+            LOGD(ERROR, aodev->dev->domid,
+                 "timed out while waiting for %s to be removed",
+                 xswait->path);
+        aodev->rc = rc;
+        goto out;
+    }
+
+    if (dir) {
+        /* backend path still exists, wait a little longer... */
+        return;
+    }
+
+out:
+    /* We are done, backend path no longer exists */
+    device_hotplug_done(egc, aodev);
+}
+
+static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    int rc;
+
+    device_hotplug_clean(gc, aodev);
+
+    /* Clean xenstore if it's a disconnection */
+    if (aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
+        (aodev->force.flag == LIBXL__FORCE_ON || !aodev->rc)) {
+        rc = libxl__device_destroy(gc, aodev->dev);
+        if (!aodev->rc)
+            aodev->rc = rc;
+    }
+
+    aodev->callback(egc, aodev);
+    return;
+}
+
+static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev)
+{
+    /* Clean events and check reentrancy */
+    libxl__ev_time_deregister(gc, &aodev->timeout);
+    libxl__xswait_stop(gc, &aodev->xswait);
+    assert(!libxl__async_exec_inuse(&aodev->aes));
+}
+
+static void devices_remove_callback(libxl__egc *egc,
+                                    libxl__multidev *multidev, int rc)
+{
+    libxl__devices_remove_state *drs = CONTAINER_OF(multidev, *drs, multidev);
+    STATE_AO_GC(drs->ao);
+
+    drs->callback(egc, drs, rc);
+    return;
+}
+
+int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
+                                 uint32_t domid, char *state,
+                                 libxl__spawn_starting *spawning,
+                                 int (*check_callback)(libxl__gc *gc,
+                                                       uint32_t domid,
+                                                       const char *state,
+                                                       void *userdata),
+                                 void *check_callback_userdata)
+{
+    char *path;
+    uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+    return libxl__xenstore_child_wait_deprecated(gc, domid,
+                                     LIBXL_DEVICE_MODEL_START_TIMEOUT,
+                                     "Device Model", path, state, spawning,
+                                     check_callback, check_callback_userdata);
+}
+
+int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
+                            const char *state)
+{
+    int watchdog = 100;
+    const char *p, *path = GCSPRINTF("%s/state", be_path);
+    int rc;
+
+    while (watchdog-- > 0) {
+        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &p);
+        if (rc) return rc;
+
+        if (p == NULL) {
+            LOG(ERROR, "Backend %s does not exist", be_path);
+            return ERROR_FAIL;
+        }
+
+        if (!strcmp(p, state))
+            return 0;
+
+        usleep(100000);
+    }
+
+    LOG(ERROR, "Backend %s not ready", be_path);
+    return ERROR_FAIL;
+}
+
+/* generic callback for devices that only need to set ao_complete */
+void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+
+    if (aodev->rc) {
+        if (aodev->dev) {
+            LOGD(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
+                        libxl__device_action_to_string(aodev->action),
+                        libxl__device_kind_to_string(aodev->dev->kind),
+                        aodev->dev->devid);
+        } else {
+            LOG(ERROR, "unable to %s device",
+                       libxl__device_action_to_string(aodev->action));
+        }
+        goto out;
+    }
+
+out:
+    libxl__ao_complete(egc, ao, aodev->rc);
+    return;
+}
+
+/* common function to get next device id */
+int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
+                         libxl__device_kind device)
+{
+    char *libxl_dom_path, **l;
+    unsigned int nb;
+    int nextid = -1;
+
+    if (!(libxl_dom_path = libxl__xs_libxl_path(gc, domid)))
+        return nextid;
+
+    l = libxl__xs_directory(gc, XBT_NULL,
+        GCSPRINTF("%s/device/%s", libxl_dom_path,
+                  libxl__device_kind_to_string(device)), &nb);
+    if (l == NULL || nb == 0)
+        nextid = 0;
+    else
+        nextid = strtoul(l[nb - 1], NULL, 10) + 1;
+
+    return nextid;
+}
+
+static void device_complete(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+
+    LOG(DEBUG, "device %s %s %s",
+               libxl__device_backend_path(gc, aodev->dev),
+               libxl__device_action_to_string(aodev->action),
+               aodev->rc ? "failed" : "succeed");
+
+    libxl__nested_ao_free(aodev->ao);
+}
+
+static void qdisk_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss,
+                                int rc)
+{
+    STATE_AO_GC(dmss->spawn.ao);
+
+    LOGD(DEBUG, dmss->guest_domid, "qdisk backend spawn %s",
+                rc ? "failed" : "succeed");
+
+    libxl__nested_ao_free(dmss->spawn.ao);
+}
+
+/*
+ * Data structures used to track devices handled by driver domains
+ */
+
+/*
+ * Structure that describes a device handled by a driver domain
+ */
+typedef struct libxl__ddomain_device {
+    libxl__device *dev;
+    LIBXL_SLIST_ENTRY(struct libxl__ddomain_device) next;
+} libxl__ddomain_device;
+
+/*
+ * Structure that describes a domain and it's associated devices
+ */
+typedef struct libxl__ddomain_guest {
+    uint32_t domid;
+    int num_qdisks;
+    LIBXL_SLIST_HEAD(, struct libxl__ddomain_device) devices;
+    LIBXL_SLIST_ENTRY(struct libxl__ddomain_guest) next;
+} libxl__ddomain_guest;
+
+/*
+ * Main structure used by a driver domain to keep track of devices
+ * currently in use
+ */
+typedef struct {
+    libxl__ao *ao;
+    libxl__ev_xswatch watch;
+    LIBXL_SLIST_HEAD(, struct libxl__ddomain_guest) guests;
+} libxl__ddomain;
+
+static libxl__ddomain_guest *search_for_guest(libxl__ddomain *ddomain,
+                                               uint32_t domid)
+{
+    libxl__ddomain_guest *dguest;
+
+    LIBXL_SLIST_FOREACH(dguest, &ddomain->guests, next) {
+        if (dguest->domid == domid)
+            return dguest;
+    }
+    return NULL;
+}
+
+static libxl__ddomain_device *search_for_device(libxl__ddomain_guest *dguest,
+                                                libxl__device *dev)
+{
+    libxl__ddomain_device *ddev;
+
+    LIBXL_SLIST_FOREACH(ddev, &dguest->devices, next) {
+#define LIBXL_DEVICE_CMP(dev1, dev2, entry) (dev1->entry == dev2->entry)
+        if (LIBXL_DEVICE_CMP(ddev->dev, dev, backend_devid) &&
+            LIBXL_DEVICE_CMP(ddev->dev, dev, backend_domid) &&
+            LIBXL_DEVICE_CMP(ddev->dev, dev, devid) &&
+            LIBXL_DEVICE_CMP(ddev->dev, dev, domid) &&
+            LIBXL_DEVICE_CMP(ddev->dev, dev, backend_kind) &&
+            LIBXL_DEVICE_CMP(ddev->dev, dev, kind))
+            return ddev;
+#undef LIBXL_DEVICE_CMP
+    }
+
+    return NULL;
+}
+
+static void check_and_maybe_remove_guest(libxl__gc *gc,
+                                         libxl__ddomain *ddomain,
+                                         libxl__ddomain_guest *dguest)
+{
+    assert(ddomain);
+
+    if (dguest != NULL && LIBXL_SLIST_FIRST(&dguest->devices) == NULL) {
+        LIBXL_SLIST_REMOVE(&ddomain->guests, dguest, libxl__ddomain_guest,
+                           next);
+        LOGD(DEBUG, dguest->domid, "Removed domain from the list of active guests");
+        /* Clear any leftovers in libxl/<domid> */
+        libxl__xs_rm_checked(gc, XBT_NULL,
+                             GCSPRINTF("libxl/%u", dguest->domid));
+        free(dguest);
+    }
+}
+
+/*
+ * The following comment applies to both add_device and remove_device.
+ *
+ * If the return value is greater than 0, it means there's no ao dispatched,
+ * so the free of the nested ao should be done by the parent when it has
+ * finished.
+ */
+static int add_device(libxl__egc *egc, libxl__ao *ao,
+                      libxl__ddomain_guest *dguest,
+                      libxl__device *dev)
+{
+    AO_GC;
+    libxl__ao_device *aodev;
+    libxl__ddomain_device *ddev;
+    libxl__dm_spawn_state *dmss;
+    int rc = 0;
+
+    /*
+     * New device addition, allocate a struct to hold it and add it
+     * to the list of active devices for a given guest.
+     */
+    ddev = libxl__zalloc(NOGC, sizeof(*ddev));
+    ddev->dev = libxl__zalloc(NOGC, sizeof(*ddev->dev));
+    *ddev->dev = *dev;
+    LIBXL_SLIST_INSERT_HEAD(&dguest->devices, ddev, next);
+    LOGD(DEBUG, dev->domid, "Added device %s to the list of active devices",
+         libxl__device_backend_path(gc, dev));
+
+    switch(dev->backend_kind) {
+    case LIBXL__DEVICE_KIND_QDISK:
+        if (dguest->num_qdisks == 0) {
+            GCNEW(dmss);
+            dmss->guest_domid = dev->domid;
+            dmss->spawn.ao = ao;
+            dmss->callback = qdisk_spawn_outcome;
+
+            libxl__spawn_qdisk_backend(egc, dmss);
+        }
+        dguest->num_qdisks++;
+        break;
+    default:
+        GCNEW(aodev);
+        libxl__prepare_ao_device(ao, aodev);
+        /*
+         * Clone the libxl__device to avoid races if remove_device is called
+         * before the device addition has finished.
+         */
+        GCNEW(aodev->dev);
+        *aodev->dev = *dev;
+        aodev->action = LIBXL__DEVICE_ACTION_ADD;
+        aodev->callback = device_complete;
+        libxl__wait_device_connection(egc, aodev);
+        break;
+    }
+
+    return rc;
+}
+
+static int remove_device(libxl__egc *egc, libxl__ao *ao,
+                         libxl__ddomain_guest *dguest,
+                         libxl__ddomain_device *ddev)
+{
+    AO_GC;
+    libxl__device *dev = ddev->dev;
+    libxl__ao_device *aodev;
+    int rc = 0;
+
+    switch(ddev->dev->backend_kind) {
+    case LIBXL__DEVICE_KIND_QDISK:
+        if (--dguest->num_qdisks == 0) {
+            rc = libxl__destroy_qdisk_backend(gc, dev->domid);
+            if (rc)
+                goto out;
+        }
+        libxl__device_destroy(gc, dev);
+        /* Return > 0, no ao has been dispatched */
+        rc = 1;
+        break;
+    default:
+        GCNEW(aodev);
+        libxl__prepare_ao_device(ao, aodev);
+        /*
+         * Clone the libxl__device to avoid races if there's a add_device
+         * running in parallel.
+         */
+        GCNEW(aodev->dev);
+        *aodev->dev = *dev;
+        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+        aodev->callback = device_complete;
+        libxl__initiate_device_generic_remove(egc, aodev);
+        break;
+    }
+
+    /*
+     * Removal of an active device, remove it from the list and
+     * free it's data structures if they are no longer needed.
+     *
+     * NB: the freeing is safe because all the async ops launched
+     * above or from add_device make a copy of the data they use, so
+     * there's no risk of dereferencing.
+     */
+    LIBXL_SLIST_REMOVE(&dguest->devices, ddev, libxl__ddomain_device,
+                       next);
+    LOGD(DEBUG, dev->domid, "Removed device %s from the list of active devices",
+         libxl__device_backend_path(gc, dev));
+
+    free(ddev->dev);
+    free(ddev);
+
+out:
+    return rc;
+}
+
+static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
+                                   const char *watch_path,
+                                   const char *event_path)
+{
+    libxl__ddomain *ddomain = CONTAINER_OF(watch, *ddomain, watch);
+    libxl__ao *nested_ao = libxl__nested_ao_create(ddomain->ao);
+    STATE_AO_GC(nested_ao);
+    char *p, *path;
+    const char *sstate, *sonline;
+    int state, online, rc;
+    libxl__device *dev;
+    libxl__ddomain_device *ddev = NULL;
+    libxl__ddomain_guest *dguest = NULL;
+    bool free_ao = false;
+
+    /* Check if event_path ends with "state" or "online" and truncate it. */
+    path = libxl__strdup(gc, event_path);
+    p = strrchr(path, '/');
+    if (p == NULL)
+        goto skip;
+    if (strcmp(p, "/state") != 0 && strcmp(p, "/online") != 0)
+        goto skip;
+    /* Truncate the string so it points to the backend directory. */
+    *p = '\0';
+
+    /* Fetch the value of the state and online nodes. */
+    rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/state", path),
+                                &sstate);
+    if (rc || !sstate)
+        goto skip;
+    state = atoi(sstate);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/online", path),
+                                &sonline);
+    if (rc || !sonline)
+        goto skip;
+    online = atoi(sonline);
+
+    GCNEW(dev);
+    rc = libxl__parse_backend_path(gc, path, dev);
+    if (rc)
+        goto skip;
+
+    dguest = search_for_guest(ddomain, dev->domid);
+    if (dguest == NULL && state == XenbusStateClosed) {
+        /*
+         * Spurious state change, device has already been disconnected
+         * or never attached.
+         */
+        goto skip;
+    }
+    if (dguest == NULL) {
+        /* Create a new guest struct and initialize it */
+        dguest = libxl__zalloc(NOGC, sizeof(*dguest));
+        dguest->domid = dev->domid;
+        LIBXL_SLIST_INIT(&dguest->devices);
+        LIBXL_SLIST_INSERT_HEAD(&ddomain->guests, dguest, next);
+        LOGD(DEBUG, dguest->domid, "Added domain to the list of active guests");
+    }
+    ddev = search_for_device(dguest, dev);
+    if (ddev == NULL && state == XenbusStateClosed) {
+        /*
+         * Spurious state change, device has already been disconnected
+         * or never attached.
+         */
+        goto skip;
+    } else if (ddev == NULL) {
+        rc = add_device(egc, nested_ao, dguest, dev);
+        if (rc > 0)
+            free_ao = true;
+    } else if (state == XenbusStateClosed && online == 0) {
+        rc = remove_device(egc, nested_ao, dguest, ddev);
+        if (rc > 0)
+            free_ao = true;
+        check_and_maybe_remove_guest(gc, ddomain, dguest);
+    }
+
+    if (free_ao)
+        libxl__nested_ao_free(nested_ao);
+
+    return;
+
+skip:
+    libxl__nested_ao_free(nested_ao);
+    check_and_maybe_remove_guest(gc, ddomain, dguest);
+    return;
+}
+
+/* Handler of events for device driver domains */
+int libxl_device_events_handler(libxl_ctx *ctx,
+                                const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, 0, ao_how);
+    int rc;
+    uint32_t domid;
+    libxl__ddomain ddomain;
+    char *be_path;
+    char **kinds = NULL, **domains = NULL, **devs = NULL;
+    const char *sstate;
+    char *state_path;
+    int state;
+    unsigned int nkinds, ndomains, ndevs;
+    int i, j, k;
+
+    ddomain.ao = ao;
+    LIBXL_SLIST_INIT(&ddomain.guests);
+
+    rc = libxl__get_domid(gc, &domid);
+    if (rc) {
+        LOG(ERROR, "unable to get domain id");
+        goto out;
+    }
+
+    /*
+     * We use absolute paths because we want xswatch to also return
+     * absolute paths that can be parsed by libxl__parse_backend_path.
+     */
+    be_path = GCSPRINTF("/local/domain/%u/backend", domid);
+    rc = libxl__ev_xswatch_register(gc, &ddomain.watch, backend_watch_callback,
+                                    be_path);
+    if (rc) goto out;
+
+    kinds = libxl__xs_directory(gc, XBT_NULL, be_path, &nkinds);
+    if (kinds) {
+        for (i = 0; i < nkinds; i++) {
+            domains = libxl__xs_directory(gc, XBT_NULL,
+                    GCSPRINTF("%s/%s", be_path, kinds[i]), &ndomains);
+            if (!domains)
+                continue;
+            for (j = 0; j < ndomains; j++) {
+                devs = libxl__xs_directory(gc, XBT_NULL,
+                        GCSPRINTF("%s/%s/%s", be_path, kinds[i], domains[j]), &ndevs);
+                if (!devs)
+                    continue;
+                for (k = 0; k < ndevs; k++) {
+                    state_path = GCSPRINTF("%s/%s/%s/%s/state",
+                            be_path, kinds[i], domains[j], devs[k]);
+                    rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &sstate);
+                    if (rc || !sstate)
+                        continue;
+                    state = atoi(sstate);
+                    if (state == XenbusStateInitWait)
+                        backend_watch_callback(egc, &ddomain.watch,
+                                               be_path, state_path);
+                }
+            }
+        }
+    }
+
+    return AO_INPROGRESS;
+
+out:
+    return AO_CREATE_FAIL(rc);
+}
+
+void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
+                              const libxl__device_type *dt, const void *dev)
+{
+    int *num_dev;
+    unsigned int i;
+    void *item = NULL;
+
+    num_dev = libxl__device_type_get_num(dt, d_config);
+
+    /* Check for existing device */
+    for (i = 0; i < *num_dev; i++) {
+        if (dt->compare(libxl__device_type_get_elem(dt, d_config, i), dev)) {
+            item = libxl__device_type_get_elem(dt, d_config, i);
+        }
+    }
+
+    if (!item) {
+        void **devs = libxl__device_type_get_ptr(dt, d_config);
+        *devs = libxl__realloc(NOGC, *devs,
+                               dt->dev_elem_size * (*num_dev + 1));
+        item = libxl__device_type_get_elem(dt, d_config, *num_dev);
+        (*num_dev)++;
+    } else {
+        dt->dispose(item);
+    }
+
+    dt->init(item);
+    dt->copy(CTX, item, dev);
+}
+
+void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
+                             const libxl__device_type *dt, void *type,
+                             libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    flexarray_t *back;
+    flexarray_t *front, *ro_front;
+    libxl__device *device;
+    xs_transaction_t t = XBT_NULL;
+    libxl_domain_config d_config;
+    void *type_saved;
+    libxl__flock *lock = NULL;
+    int rc;
+
+    libxl_domain_config_init(&d_config);
+
+    type_saved = libxl__malloc(gc, dt->dev_elem_size);
+
+    dt->init(type_saved);
+    dt->copy(CTX, type_saved, type);
+
+    if (dt->set_default) {
+        rc = dt->set_default(gc, domid, type, aodev->update_json);
+        if (rc) goto out;
+    }
+
+    if (dt->update_devid) {
+        rc = dt->update_devid(gc, domid, type);
+        if (rc) goto out;
+    }
+
+    if (dt->update_config)
+        dt->update_config(gc, type_saved, type);
+
+    GCNEW(device);
+    rc = dt->to_device(gc, domid, type, device);
+    if (rc) goto out;
+
+    if (aodev->update_json) {
+        lock = libxl__lock_domain_userdata(gc, domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+            goto out;
+        }
+
+        rc = libxl__get_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        device_add_domain_config(gc, &d_config, dt, type_saved);
+
+        rc = libxl__dm_check_start(gc, &d_config, domid);
+        if (rc) goto out;
+    }
+
+    back = flexarray_make(gc, 16, 1);
+    front = flexarray_make(gc, 16, 1);
+    ro_front = flexarray_make(gc, 16, 1);
+
+    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+    flexarray_append_pair(back, "online", "1");
+    flexarray_append_pair(back, "state",
+                          GCSPRINTF("%d", XenbusStateInitialising));
+
+    flexarray_append_pair(front, "backend-id",
+                          GCSPRINTF("%d", device->backend_domid));
+    flexarray_append_pair(front, "state",
+                          GCSPRINTF("%d", XenbusStateInitialising));
+
+    if (dt->set_xenstore_config)
+        dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        rc = libxl__device_exists(gc, t, device);
+        if (rc < 0) goto out;
+        if (rc == 1) {              /* already exists in xenstore */
+            LOGD(ERROR, domid, "device already exists in xenstore");
+            aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
+            rc = ERROR_DEVICE_EXISTS;
+            goto out;
+        }
+
+        if (aodev->update_json) {
+            rc = libxl__set_domain_configuration(gc, domid, &d_config);
+            if (rc) goto out;
+        }
+
+        libxl__device_generic_add(gc, t, device,
+                                  libxl__xs_kvs_of_flexarray(gc, back),
+                                  libxl__xs_kvs_of_flexarray(gc, front),
+                                  libxl__xs_kvs_of_flexarray(gc, ro_front));
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    aodev->dev = device;
+    aodev->action = LIBXL__DEVICE_ACTION_ADD;
+    libxl__wait_device_connection(egc, aodev);
+
+    rc = 0;
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    if (lock) libxl__unlock_file(lock);
+    dt->dispose(type_saved);
+    libxl_domain_config_dispose(&d_config);
+    aodev->rc = rc;
+    if (rc) aodev->callback(egc, aodev);
+    return;
+}
+
+int libxl__device_add(libxl__gc *gc, uint32_t domid,
+                      const libxl__device_type *dt, void *type)
+{
+    flexarray_t *back;
+    flexarray_t *front, *ro_front;
+    libxl__device *device;
+    int rc;
+
+    if (dt->set_default) {
+        rc = dt->set_default(gc, domid, type, false);
+        if (rc) goto out;
+    }
+
+    if (dt->update_devid) {
+        rc = dt->update_devid(gc, domid, type);
+        if (rc) goto out;
+    }
+
+    GCNEW(device);
+    rc = dt->to_device(gc, domid, type, device);
+    if (rc) goto out;
+
+    back = flexarray_make(gc, 16, 1);
+    front = flexarray_make(gc, 16, 1);
+    ro_front = flexarray_make(gc, 16, 1);
+
+    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+    flexarray_append_pair(back, "online", "1");
+    flexarray_append_pair(back, "state",
+                          GCSPRINTF("%d", XenbusStateInitialising));
+    flexarray_append_pair(front, "backend-id",
+                          libxl__sprintf(gc, "%d", device->backend_domid));
+    flexarray_append_pair(front, "state",
+                          GCSPRINTF("%d", XenbusStateInitialising));
+
+    if (dt->set_xenstore_config)
+        dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
+
+    rc = libxl__device_generic_add(gc, XBT_NULL, device,
+                                   libxl__xs_kvs_of_flexarray(gc, back),
+                                   libxl__xs_kvs_of_flexarray(gc, front),
+                                   libxl__xs_kvs_of_flexarray(gc, ro_front));
+    if (rc) goto out;
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+void *libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
+                         uint32_t domid, int *num)
+{
+    void *r = NULL;
+    void *list = NULL;
+    void *item = NULL;
+    char *libxl_path;
+    char **dir = NULL;
+    unsigned int ndirs = 0;
+    unsigned int ndevs = 0;
+    int rc;
+
+    *num = 0;
+
+    libxl_path = GCSPRINTF("%s/device/%s",
+                           libxl__xs_libxl_path(gc, domid),
+                           libxl__device_kind_to_string(dt->type));
+
+    dir = libxl__xs_directory(gc, XBT_NULL, libxl_path, &ndirs);
+
+    if (dir && ndirs) {
+        if (dt->get_num) {
+            if (ndirs != 1) {
+                LOGD(ERROR, domid, "multiple entries in %s\n", libxl_path);
+                rc = ERROR_FAIL;
+                goto out;
+            }
+            rc = dt->get_num(gc, GCSPRINTF("%s/%s", libxl_path, *dir), &ndevs);
+            if (rc) goto out;
+        } else {
+            ndevs = ndirs;
+        }
+        list = libxl__malloc(NOGC, dt->dev_elem_size * ndevs);
+        item = list;
+
+        while (*num < ndevs) {
+            dt->init(item);
+
+            if (dt->from_xenstore) {
+                int nr = dt->get_num ? *num : atoi(*dir);
+                char *device_libxl_path = GCSPRINTF("%s/%s", libxl_path, *dir);
+                rc = dt->from_xenstore(gc, device_libxl_path, nr, item);
+                if (rc) goto out;
+            }
+
+            item = (uint8_t *)item + dt->dev_elem_size;
+            ++(*num);
+            if (!dt->get_num)
+                ++dir;
+        }
+    }
+
+    r = list;
+    list = NULL;
+
+out:
+
+    if (list) {
+        libxl__device_list_free(dt, list, *num);
+        *num = 0;
+    }
+
+    return r;
+}
+
+void libxl__device_list_free(const libxl__device_type *dt,
+                             void *list, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++)
+        dt->dispose((uint8_t*)list + i * dt->dev_elem_size);
+
+    free(list);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_disk.c b/tools/libs/light/libxl_disk.c
new file mode 100644 (file)
index 0000000..de183e0
--- /dev/null
@@ -0,0 +1,1390 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+#define BACKEND_STRING_SIZE 5
+
+static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
+                                        const char *wpath, const char *epath) {
+    EGC_GC;
+    libxl_evgen_disk_eject *evg = (void*)w;
+    const char *backend;
+    char *value;
+    char backend_type[BACKEND_STRING_SIZE+1];
+    int rc;
+
+    value = libxl__xs_read(gc, XBT_NULL, wpath);
+
+    if (!value || strcmp(value,  "eject"))
+        return;
+
+    if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) {
+        LIBXL__EVENT_DISASTER(gc, "xs_write failed acknowledging eject",
+                              errno, LIBXL_EVENT_TYPE_DISK_EJECT);
+        return;
+    }
+
+    libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user);
+    libxl_device_disk *disk = &ev->u.disk_eject.disk;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend);
+    if (rc) {
+        LIBXL__EVENT_DISASTER(gc, "xs_read failed reading be_ptr_path",
+                              errno, LIBXL_EVENT_TYPE_DISK_EJECT);
+        return;
+    }
+    if (!backend) {
+        /* device has been removed, not simply ejected */
+        return;
+    }
+
+    sscanf(backend,
+            "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
+           "[a-z]/%*d/%*d",
+           &disk->backend_domid, backend_type);
+    if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
+        disk->backend = LIBXL_DISK_BACKEND_TAP;
+    } else if (!strcmp(backend_type, "qdisk")) {
+        disk->backend = LIBXL_DISK_BACKEND_QDISK;
+    } else {
+        disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
+    }
+
+    disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
+    disk->format = LIBXL_DISK_FORMAT_EMPTY;
+    /* this value is returned to the user: do not free right away */
+    disk->vdev = libxl__strdup(NOGC, evg->vdev);
+    disk->removable = 1;
+    disk->readwrite = 0;
+    disk->is_cdrom = 1;
+
+    libxl__event_occurred(egc, ev);
+}
+
+int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
+                              const char *vdev, libxl_ev_user user,
+                              libxl_evgen_disk_eject **evgen_out) {
+    GC_INIT(ctx);
+    CTX_LOCK;
+    int rc;
+    char *path;
+    libxl_evgen_disk_eject *evg = NULL;
+
+    evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
+    memset(evg, 0, sizeof(*evg));
+    evg->user = user;
+    evg->domid = guest_domid;
+    LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
+
+    uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
+
+    if (!domid)
+        domid = guest_domid;
+
+    int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
+
+    path = GCSPRINTF("%s/eject",
+                    libxl__domain_device_frontend_path(gc, domid, devid,
+                    LIBXL__DEVICE_KIND_VBD));
+    if (!path) { rc = ERROR_NOMEM; goto out; }
+
+    const char *libxl_path = libxl__domain_device_frontend_path(gc, domid, devid,
+                                                                LIBXL__DEVICE_KIND_VBD);
+    evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
+
+    const char *configured_vdev;
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+            GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
+    if (rc) goto out;
+
+    evg->vdev = libxl__strdup(NOGC, configured_vdev);
+
+    rc = libxl__ev_xswatch_register(gc, &evg->watch,
+                                    disk_eject_xswatch_callback, path);
+    if (rc) goto out;
+
+    *evgen_out = evg;
+    CTX_UNLOCK;
+    GC_FREE;
+    return 0;
+
+ out:
+    if (evg)
+        libxl__evdisable_disk_eject(gc, evg);
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
+    CTX_LOCK;
+
+    LIBXL_LIST_REMOVE(evg, entry);
+
+    if (libxl__ev_xswatch_isregistered(&evg->watch))
+        libxl__ev_xswatch_deregister(gc, &evg->watch);
+
+    free(evg->vdev);
+    free(evg->be_ptr_path);
+    free(evg);
+
+    CTX_UNLOCK;
+}
+
+void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
+    GC_INIT(ctx);
+    libxl__evdisable_disk_eject(gc, evg);
+    GC_FREE;
+}
+
+static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
+                                         libxl_device_disk *disk, bool hotplug)
+{
+    int rc;
+
+    libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
+    libxl_defbool_setdefault(&disk->colo_enable, false);
+    libxl_defbool_setdefault(&disk->colo_restore_enable, false);
+
+    rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
+    if (rc < 0) return rc;
+
+    /* Force Qdisk backend for CDROM devices of guests with a device model. */
+    if (disk->is_cdrom != 0 &&
+        libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
+        if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
+              disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
+            LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
+            return ERROR_FAIL;
+        }
+        disk->backend = LIBXL_DISK_BACKEND_QDISK;
+    }
+
+    rc = libxl__device_disk_set_backend(gc, disk);
+    return rc;
+}
+
+static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
+                                   const libxl_device_disk *disk,
+                                   libxl__device *device)
+{
+    int devid;
+
+    devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+    if (devid==-1) {
+        LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
+             disk->vdev);
+        return ERROR_INVAL;
+    }
+
+    device->backend_domid = disk->backend_domid;
+    device->backend_devid = devid;
+
+    switch (disk->backend) {
+        case LIBXL_DISK_BACKEND_PHY:
+            device->backend_kind = LIBXL__DEVICE_KIND_VBD;
+            break;
+        case LIBXL_DISK_BACKEND_TAP:
+            device->backend_kind = LIBXL__DEVICE_KIND_VBD;
+            break;
+        case LIBXL_DISK_BACKEND_QDISK:
+            device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
+            break;
+        default:
+            LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
+                 disk->backend);
+            return ERROR_INVAL;
+    }
+
+    device->domid = domid;
+    device->devid = devid;
+    device->kind  = LIBXL__DEVICE_KIND_VBD;
+
+    return 0;
+}
+
+/* Specific function called directly only by local disk attach,
+ * all other users should instead use the regular
+ * libxl__device_disk_add wrapper
+ *
+ * The (optionally) passed function get_vdev will be used to
+ * set the vdev the disk should be attached to. When it is set the caller
+ * must also pass get_vdev_user, which will be passed to get_vdev.
+ *
+ * The passed get_vdev function is also in charge of printing
+ * the corresponding error message when appropiate.
+ */
+static void device_disk_add(libxl__egc *egc, uint32_t domid,
+                           libxl_device_disk *disk,
+                           libxl__ao_device *aodev,
+                           char *get_vdev(libxl__gc *, void *,
+                                          xs_transaction_t),
+                           void *get_vdev_user)
+{
+    STATE_AO_GC(aodev->ao);
+    flexarray_t *front = NULL;
+    flexarray_t *back = NULL;
+    char *dev = NULL, *script;
+    libxl__device *device;
+    int rc;
+    libxl_ctx *ctx = gc->owner;
+    xs_transaction_t t = XBT_NULL;
+    libxl_domain_config d_config;
+    libxl_device_disk disk_saved;
+    libxl__flock *lock = NULL;
+
+    libxl_domain_config_init(&d_config);
+    libxl_device_disk_init(&disk_saved);
+    libxl_device_disk_copy(ctx, &disk_saved, disk);
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /*
+     * get_vdev != NULL -> local attach
+     * get_vdev == NULL -> block attach
+     *
+     * We don't care about local attach state because it's only
+     * intermediate state.
+     */
+    if (!get_vdev && aodev->update_json) {
+        lock = libxl__lock_domain_userdata(gc, domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+            goto out;
+        }
+
+        rc = libxl__get_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
+                                 &disk_saved);
+
+        rc = libxl__dm_check_start(gc, &d_config, domid);
+        if (rc) goto out;
+    }
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        if (get_vdev) {
+            assert(get_vdev_user);
+            disk->vdev = get_vdev(gc, get_vdev_user, t);
+            if (disk->vdev == NULL) {
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+
+        rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
+        if (rc) goto out;
+
+        front = flexarray_make(gc, 16, 1);
+        back = flexarray_make(gc, 16, 1);
+
+        GCNEW(device);
+        rc = libxl__device_from_disk(gc, domid, disk, device);
+        if (rc != 0) {
+            LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
+                 disk->vdev);
+            goto out;
+        }
+
+        rc = libxl__device_exists(gc, t, device);
+        if (rc < 0) goto out;
+        if (rc == 1) {              /* already exists in xenstore */
+            LOGD(ERROR, domid, "device already exists in xenstore");
+            aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
+            rc = ERROR_DEVICE_EXISTS;
+            goto out;
+        }
+
+        switch (disk->backend) {
+            case LIBXL_DISK_BACKEND_PHY:
+                dev = disk->pdev_path;
+
+                flexarray_append(back, "params");
+                flexarray_append(back, dev);
+
+                script = libxl__abs_path(gc, disk->script?: "block",
+                                         libxl__xen_script_dir_path());
+                flexarray_append_pair(back, "script", script);
+
+                assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
+                break;
+
+            case LIBXL_DISK_BACKEND_TAP:
+                LOG(ERROR, "blktap is not supported");
+                rc = ERROR_FAIL;
+                goto out;
+            case LIBXL_DISK_BACKEND_QDISK:
+                flexarray_append(back, "params");
+                flexarray_append(back, GCSPRINTF("%s:%s",
+                              libxl__device_disk_string_of_format(disk->format),
+                              disk->pdev_path ? : ""));
+                if (libxl_defbool_val(disk->colo_enable)) {
+                    flexarray_append(back, "colo-host");
+                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
+                    flexarray_append(back, "colo-port");
+                    flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
+                    flexarray_append(back, "colo-export");
+                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
+                    flexarray_append(back, "active-disk");
+                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
+                    flexarray_append(back, "hidden-disk");
+                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
+                }
+                assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
+                break;
+            default:
+                LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
+                     disk->backend);
+                rc = ERROR_INVAL;
+                goto out;
+        }
+
+        flexarray_append(back, "frontend-id");
+        flexarray_append(back, GCSPRINTF("%d", domid));
+        flexarray_append(back, "online");
+        flexarray_append(back, "1");
+        flexarray_append(back, "removable");
+        flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
+        flexarray_append(back, "bootable");
+        flexarray_append(back, GCSPRINTF("%d", 1));
+        flexarray_append(back, "state");
+        flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
+        flexarray_append(back, "dev");
+        flexarray_append(back, disk->vdev);
+        flexarray_append(back, "type");
+        flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
+        flexarray_append(back, "mode");
+        flexarray_append(back, disk->readwrite ? "w" : "r");
+        flexarray_append(back, "device-type");
+        flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
+        if (disk->direct_io_safe) {
+            flexarray_append(back, "direct-io-safe");
+            flexarray_append(back, "1");
+        }
+        flexarray_append_pair(back, "discard-enable",
+                              libxl_defbool_val(disk->discard_enable) ?
+                              "1" : "0");
+
+        flexarray_append(front, "backend-id");
+        flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
+        flexarray_append(front, "state");
+        flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
+        flexarray_append(front, "virtual-device");
+        flexarray_append(front, GCSPRINTF("%d", device->devid));
+        flexarray_append(front, "device-type");
+        flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
+
+        /*
+         * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
+         * write disk native protocol to frontend node. Xend does this, port
+         * this behaviour to xl.
+         *
+         * New kernels write this node themselves. In that case it just
+         * overwrites an existing node which is OK.
+         */
+        if (type == LIBXL_DOMAIN_TYPE_PV) {
+            const char *protocol =
+                xc_domain_get_native_protocol(ctx->xch, domid);
+            if (protocol) {
+                flexarray_append(front, "protocol");
+                flexarray_append(front, libxl__strdup(gc, protocol));
+            }
+        }
+
+        if (!get_vdev && aodev->update_json) {
+            rc = libxl__set_domain_configuration(gc, domid, &d_config);
+            if (rc) goto out;
+        }
+
+        libxl__device_generic_add(gc, t, device,
+                                  libxl__xs_kvs_of_flexarray(gc, back),
+                                  libxl__xs_kvs_of_flexarray(gc, front),
+                                  NULL);
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    aodev->dev = device;
+    aodev->action = LIBXL__DEVICE_ACTION_ADD;
+    libxl__wait_device_connection(egc, aodev);
+
+    rc = 0;
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    if (lock) libxl__unlock_file(lock);
+    libxl_device_disk_dispose(&disk_saved);
+    libxl_domain_config_dispose(&d_config);
+    aodev->rc = rc;
+    if (rc) aodev->callback(egc, aodev);
+    return;
+}
+
+static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
+                                   libxl_device_disk *disk,
+                                   libxl__ao_device *aodev)
+{
+    device_disk_add(egc, domid, disk, aodev, NULL, NULL);
+}
+
+static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
+                                     libxl_devid devid,
+                                     libxl_device_disk *disk)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    unsigned int len;
+    char *tmp;
+    int rc;
+
+    const char *backend_path;
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/backend", libxl_path),
+                                &backend_path);
+    if (rc) goto out;
+
+    if (!backend_path) {
+        LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
+    if (rc) {
+        LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
+        goto out;
+    }
+
+    /*
+     * "params" may not be present; but everything else must be.
+     * colo releated entries(colo-host, colo-port, colo-export,
+     * active-disk and hidden-disk) are present only if colo is
+     * enabled.
+     */
+    tmp = xs_read(ctx->xsh, XBT_NULL,
+                  GCSPRINTF("%s/params", libxl_path), &len);
+    if (tmp && strchr(tmp, ':')) {
+        disk->pdev_path = strdup(strchr(tmp, ':') + 1);
+        free(tmp);
+    } else {
+        disk->pdev_path = tmp;
+    }
+
+    tmp = xs_read(ctx->xsh, XBT_NULL,
+                  GCSPRINTF("%s/colo-host", libxl_path), &len);
+    if (tmp) {
+        libxl_defbool_set(&disk->colo_enable, true);
+        disk->colo_host = tmp;
+
+        tmp = xs_read(ctx->xsh, XBT_NULL,
+                      GCSPRINTF("%s/colo-port", libxl_path), &len);
+        if (!tmp) {
+            LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
+            goto cleanup;
+        }
+        disk->colo_port = atoi(tmp);
+
+#define XS_READ_COLO(param, item) do {                                  \
+        tmp = xs_read(ctx->xsh, XBT_NULL,                               \
+                      GCSPRINTF("%s/"#param"", libxl_path), &len);         \
+        if (!tmp) {                                                     \
+            LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path);   \
+            goto cleanup;                                               \
+        }                                                               \
+        disk->item = tmp;                                               \
+} while (0)
+        XS_READ_COLO(colo-export, colo_export);
+        XS_READ_COLO(active-disk, active_disk);
+        XS_READ_COLO(hidden-disk, hidden_disk);
+#undef XS_READ_COLO
+    } else {
+        libxl_defbool_set(&disk->colo_enable, false);
+    }
+
+    tmp = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/type", libxl_path));
+    if (!tmp) {
+        LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
+        goto cleanup;
+    }
+    libxl_string_to_backend(ctx, tmp, &(disk->backend));
+
+    disk->vdev = xs_read(ctx->xsh, XBT_NULL,
+                         GCSPRINTF("%s/dev", libxl_path), &len);
+    if (!disk->vdev) {
+        LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
+        goto cleanup;
+    }
+
+    tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
+                         (gc, "%s/removable", libxl_path));
+    if (!tmp) {
+        LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
+        goto cleanup;
+    }
+    disk->removable = atoi(tmp);
+
+    tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
+    if (!tmp) {
+        LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
+        goto cleanup;
+    }
+    if (!strcmp(tmp, "w"))
+        disk->readwrite = 1;
+    else
+        disk->readwrite = 0;
+
+    tmp = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/device-type", libxl_path));
+    if (!tmp) {
+        LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
+        goto cleanup;
+    }
+    disk->is_cdrom = !strcmp(tmp, "cdrom");
+
+    disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
+
+    return 0;
+cleanup:
+    rc = ERROR_FAIL;
+ out:
+    libxl_device_disk_dispose(disk);
+    return rc;
+}
+
+int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
+                              const char *vdev, libxl_device_disk *disk)
+{
+    GC_INIT(ctx);
+    char *libxl_path;
+    int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
+    int rc = ERROR_FAIL;
+
+    if (devid < 0)
+        return ERROR_INVAL;
+
+    libxl_device_disk_init(disk);
+
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, devid,
+                                                 LIBXL__DEVICE_KIND_VBD);
+
+    rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
+                              const libxl_device_disk *disk,
+                              libxl_diskinfo *diskinfo)
+{
+    GC_INIT(ctx);
+    char *fe_path, *libxl_path;
+    char *val;
+    int rc;
+
+    diskinfo->backend = NULL;
+
+    diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+
+    /* tap devices entries in xenstore are written as vbd devices. */
+    fe_path = libxl__domain_device_frontend_path(gc, domid, diskinfo->devid,
+                                                 LIBXL__DEVICE_KIND_VBD);
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, diskinfo->devid,
+                                                 LIBXL__DEVICE_KIND_VBD);
+    diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
+                                GCSPRINTF("%s/backend", libxl_path), NULL);
+    if (!diskinfo->backend) {
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
+                                        &diskinfo->backend_id);
+    if (rc) goto out;
+
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
+    diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
+    diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
+    diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
+    diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
+                                 GCSPRINTF("%s/frontend", libxl_path), NULL);
+    diskinfo->frontend_id = domid;
+
+    GC_FREE;
+    return 0;
+
+ out:
+    free(diskinfo->backend);
+    return rc;
+}
+
+typedef struct {
+    libxl__ao *ao;
+    libxl_domid domid;
+    libxl_device_disk *disk;
+    libxl_device_disk disk_saved;
+    libxl__ev_slowlock qmp_lock;
+    int dm_ver;
+    libxl__ev_time time;
+    libxl__ev_qmp qmp;
+} libxl__cdrom_insert_state;
+
+static void cdrom_insert_lock_acquired(libxl__egc *, libxl__ev_slowlock *,
+                                       int rc);
+static void cdrom_insert_ejected(libxl__egc *egc, libxl__ev_qmp *,
+                                 const libxl__json_object *, int rc);
+static void cdrom_insert_addfd_cb(libxl__egc *egc, libxl__ev_qmp *,
+                                  const libxl__json_object *, int rc);
+static void cdrom_insert_inserted(libxl__egc *egc, libxl__ev_qmp *,
+                                  const libxl__json_object *, int rc);
+static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
+                                const struct timeval *requested_abs,
+                                int rc);
+static void cdrom_insert_done(libxl__egc *egc,
+                              libxl__cdrom_insert_state *cis,
+                              int rc);
+
+int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
+                       const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int num = 0, i;
+    libxl_device_disk *disks = NULL;
+    int rc;
+    libxl__cdrom_insert_state *cis;
+
+    GCNEW(cis);
+    cis->ao = ao;
+    cis->domid = domid;
+    cis->disk = disk;
+    libxl_device_disk_init(&cis->disk_saved);
+    libxl_device_disk_copy(ctx, &cis->disk_saved, disk);
+    libxl__ev_devlock_init(&cis->qmp_lock);
+    cis->qmp_lock.ao = ao;
+    cis->qmp_lock.domid = domid;
+    libxl__ev_time_init(&cis->time);
+    libxl__ev_qmp_init(&cis->qmp);
+    cis->qmp.ao = ao;
+    cis->qmp.domid = domid;
+    cis->qmp.payload_fd = -1;
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (type != LIBXL_DOMAIN_TYPE_HVM) {
+        LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    if (libxl_get_stubdom_id(ctx, domid) != 0) {
+        LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    cis->dm_ver = libxl__device_model_version_running(gc, domid);
+    if (cis->dm_ver == -1) {
+        LOGD(ERROR, domid, "Cannot determine device model version");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
+    for (i = 0; i < num; i++) {
+        if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
+        {
+            /* Found.  Set backend type appropriately. */
+            disk->backend=disks[i].backend;
+            break;
+        }
+    }
+    if (i == num) {
+        LOGD(ERROR, domid, "Virtual device not found");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__device_disk_setdefault(gc, domid, disk, false);
+    if (rc) goto out;
+
+    if (!disk->pdev_path) {
+        disk->pdev_path = libxl__strdup(NOGC, "");
+        disk->format = LIBXL_DISK_FORMAT_EMPTY;
+    }
+
+out:
+    libxl__device_list_free(&libxl__disk_devtype, disks, num);
+    if (rc) {
+        cdrom_insert_done(egc, cis, rc); /* must be last */
+    } else {
+        cis->qmp_lock.callback = cdrom_insert_lock_acquired;
+        libxl__ev_slowlock_lock(egc, &cis->qmp_lock); /* must be last */
+    }
+    return AO_INPROGRESS;
+}
+
+static void cdrom_insert_lock_acquired(libxl__egc *egc,
+                                       libxl__ev_slowlock *lock,
+                                       int rc)
+{
+    libxl__cdrom_insert_state *cis = CONTAINER_OF(lock, *cis, qmp_lock);
+    STATE_AO_GC(cis->ao);
+
+    if (rc) goto out;
+
+    rc = libxl__ev_time_register_rel(ao, &cis->time,
+                                     cdrom_insert_timout,
+                                     LIBXL_HOTPLUG_TIMEOUT * 1000);
+    if (rc) goto out;
+
+    /* We need to eject the original image first.
+     * JSON is not updated.
+     */
+
+    if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+        libxl__json_object *args = NULL;
+        int devid = libxl__device_disk_dev_number(cis->disk->vdev,
+                                                  NULL, NULL);
+
+        QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
+        cis->qmp.callback = cdrom_insert_ejected;
+        rc = libxl__ev_qmp_send(egc, &cis->qmp, "eject", args);
+        if (rc) goto out;
+    } else {
+        cdrom_insert_ejected(egc, &cis->qmp, NULL, 0); /* must be last */
+    }
+    return;
+
+out:
+    cdrom_insert_done(egc, cis, rc); /* must be last */
+}
+
+static void cdrom_insert_ejected(libxl__egc *egc,
+                                 libxl__ev_qmp *qmp,
+                                 const libxl__json_object *response,
+                                 int rc)
+{
+    EGC_GC;
+    libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
+    libxl__flock *data_lock = NULL;
+    libxl__device device;
+    const char *be_path, *libxl_path;
+    flexarray_t *empty = NULL;
+    xs_transaction_t t = XBT_NULL;
+    char *tmp;
+    libxl_domain_config d_config;
+    bool has_callback = false;
+
+    /* convenience aliases */
+    libxl_domid domid = cis->domid;
+    libxl_device_disk *disk = cis->disk;
+
+    libxl_domain_config_init(&d_config);
+
+    if (rc) goto out;
+
+    rc = libxl__device_from_disk(gc, domid, disk, &device);
+    if (rc) goto out;
+    be_path = libxl__device_backend_path(gc, &device);
+    libxl_path = libxl__device_libxl_path(gc, &device);
+
+    data_lock = libxl__lock_domain_userdata(gc, domid);
+    if (!data_lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    empty = flexarray_make(gc, 4, 1);
+    flexarray_append_pair(empty, "type",
+                          libxl__device_disk_string_of_backend(disk->backend));
+    flexarray_append_pair(empty, "params", "");
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+        /* Sanity check: make sure the device exists before writing here */
+        tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
+        if (!tmp)
+        {
+            LOGD(ERROR, domid, "Internal error: %s does not exist",
+                 GCSPRINTF("%s/frontend", libxl_path));
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
+
+        rc = libxl__xs_writev(gc, t, be_path, kvs);
+        if (rc) goto out;
+
+        rc = libxl__xs_writev(gc, t, libxl_path, kvs);
+        if (rc) goto out;
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    /*
+     * Now that the drive is empty, we can insert the new media.
+     */
+
+    rc = libxl__get_domain_configuration(gc, domid, &d_config);
+    if (rc) goto out;
+
+    device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
+                             &cis->disk_saved);
+
+    rc = libxl__dm_check_start(gc, &d_config, domid);
+    if (rc) goto out;
+
+    if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
+        disk->format != LIBXL_DISK_FORMAT_EMPTY) {
+        libxl__json_object *args = NULL;
+
+        assert(qmp->payload_fd == -1);
+        qmp->payload_fd = open(disk->pdev_path, O_RDONLY);
+        if (qmp->payload_fd < 0) {
+            LOGED(ERROR, domid, "Failed to open cdrom file %s",
+                  disk->pdev_path);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        /* This free form parameter is not use by QEMU or libxl. */
+        QMP_PARAMETERS_SPRINTF(&args, "opaque", "%s:%s",
+                               libxl_disk_format_to_string(disk->format),
+                               disk->pdev_path);
+        qmp->callback = cdrom_insert_addfd_cb;
+        rc = libxl__ev_qmp_send(egc, qmp, "add-fd", args);
+        if (rc) goto out;
+        has_callback = true;
+    } else {
+        has_callback = false;
+    }
+
+    rc = 0;
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    libxl_domain_config_dispose(&d_config);
+    if (data_lock) libxl__unlock_file(data_lock);
+    if (rc) {
+        cdrom_insert_done(egc, cis, rc); /* must be last */
+    } else if (!has_callback) {
+        /* Only called if no asynchronous callback are set. */
+        cdrom_insert_inserted(egc, qmp, NULL, 0); /* must be last */
+    }
+}
+
+static void cdrom_insert_addfd_cb(libxl__egc *egc,
+                                  libxl__ev_qmp *qmp,
+                                  const libxl__json_object *response,
+                                  int rc)
+{
+    EGC_GC;
+    libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
+    libxl__json_object *args = NULL;
+    const libxl__json_object *o;
+    int devid;
+    int fdset;
+
+    /* convenience aliases */
+    libxl_device_disk *disk = cis->disk;
+
+    close(qmp->payload_fd);
+    qmp->payload_fd = -1;
+
+    if (rc) goto out;
+
+    o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
+    if (!o) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    fdset = libxl__json_object_get_integer(o);
+
+    devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
+    QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
+    QMP_PARAMETERS_SPRINTF(&args, "target", "/dev/fdset/%d", fdset);
+    libxl__qmp_param_add_string(gc, &args, "arg",
+        libxl__qemu_disk_format_string(disk->format));
+    qmp->callback = cdrom_insert_inserted;
+    rc = libxl__ev_qmp_send(egc, qmp, "change", args);
+out:
+    if (rc)
+        cdrom_insert_done(egc, cis, rc); /* must be last */
+}
+
+static void cdrom_insert_inserted(libxl__egc *egc,
+                                  libxl__ev_qmp *qmp,
+                                  const libxl__json_object *response,
+                                  int rc)
+{
+    EGC_GC;
+    libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
+    libxl__flock *data_lock = NULL;
+    libxl_domain_config d_config;
+    flexarray_t *insert = NULL;
+    xs_transaction_t t = XBT_NULL;
+    libxl__device device;
+    const char *be_path, *libxl_path;
+    char *tmp;
+
+    /* convenience aliases */
+    libxl_domid domid = cis->domid;
+    libxl_device_disk *disk = cis->disk;
+
+    libxl_domain_config_init(&d_config);
+
+    if (rc) goto out;
+
+    rc = libxl__device_from_disk(gc, domid, disk, &device);
+    if (rc) goto out;
+    be_path = libxl__device_backend_path(gc, &device);
+    libxl_path = libxl__device_libxl_path(gc, &device);
+
+    data_lock = libxl__lock_domain_userdata(gc, domid);
+    if (!data_lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    rc = libxl__get_domain_configuration(gc, domid, &d_config);
+    if (rc) goto out;
+
+    device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
+                             &cis->disk_saved);
+
+    insert = flexarray_make(gc, 4, 1);
+    flexarray_append_pair(insert, "type",
+                      libxl__device_disk_string_of_backend(disk->backend));
+    if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
+        flexarray_append_pair(insert, "params",
+                    GCSPRINTF("%s:%s",
+                        libxl__device_disk_string_of_format(disk->format),
+                        disk->pdev_path));
+    else
+        flexarray_append_pair(insert, "params", "");
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+        /* Sanity check: make sure the device exists before writing here */
+        tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
+        if (!tmp)
+        {
+            LOGD(ERROR, domid, "Internal error: %s does not exist",
+                 GCSPRINTF("%s/frontend", libxl_path));
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__set_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
+
+        rc = libxl__xs_writev(gc, t, be_path, kvs);
+        if (rc) goto out;
+
+        rc = libxl__xs_writev(gc, t, libxl_path, kvs);
+        if (rc) goto out;
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    rc = 0;
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    libxl_domain_config_dispose(&d_config);
+    if (data_lock) libxl__unlock_file(data_lock);
+    cdrom_insert_done(egc, cis, rc); /* must be last */
+}
+
+static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
+                                const struct timeval *requested_abs,
+                                int rc)
+{
+    EGC_GC;
+    libxl__cdrom_insert_state *cis = CONTAINER_OF(ev, *cis, time);
+    LOGD(ERROR, cis->domid, "cdrom insertion timed out");
+    cdrom_insert_done(egc, cis, rc);
+}
+
+static void cdrom_insert_done(libxl__egc *egc,
+                              libxl__cdrom_insert_state *cis,
+                              int rc)
+{
+    EGC_GC;
+
+    libxl__ev_time_deregister(gc, &cis->time);
+    libxl__ev_qmp_dispose(gc, &cis->qmp);
+    if (cis->qmp.payload_fd >= 0) close(cis->qmp.payload_fd);
+    libxl__ev_slowlock_unlock(gc, &cis->qmp_lock);
+    libxl_device_disk_dispose(&cis->disk_saved);
+    libxl__ao_complete(egc, cis->ao, rc);
+}
+
+/* libxl__alloc_vdev only works on the local domain, that is the domain
+ * where the toolstack is running */
+static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
+        xs_transaction_t t)
+{
+    const char *blkdev_start = (const char *) get_vdev_user;
+    int devid = 0, disk = 0, part = 0;
+
+    libxl__device_disk_dev_number(blkdev_start, &disk, &part);
+    if (part != 0) {
+        LOG(ERROR, "blkdev_start is invalid");
+        return NULL;
+    }
+
+    do {
+        devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
+                NULL, NULL);
+        if (devid < 0)
+            return NULL;
+        if (libxl__xs_read(gc, t, GCSPRINTF("%s/backend",
+                           libxl__domain_device_libxl_path(gc,
+                           LIBXL_TOOLSTACK_DOMID, devid,
+                           LIBXL__DEVICE_KIND_VBD))) == NULL) {
+            if (errno == ENOENT)
+                return libxl__devid_to_vdev(gc, devid);
+            else
+                return NULL;
+        }
+        disk++;
+    } while (1);
+    return NULL;
+}
+
+/* Callbacks */
+
+char *libxl__device_disk_find_local_path(libxl__gc *gc,
+                                          libxl_domid guest_domid,
+                                          const libxl_device_disk *disk,
+                                          bool qdisk_direct)
+{
+    char *path = NULL;
+
+    /* No local paths for driver domains */
+    if (disk->backend_domname != NULL) {
+        LOG(DEBUG, "Non-local backend, can't access locally.\n");
+        goto out;
+    }
+
+    /*
+     * If this is in raw format, and we're not using a script or a
+     * driver domain, we can access the target path directly.
+     */
+    if (disk->format == LIBXL_DISK_FORMAT_RAW
+        && disk->script == NULL) {
+        path = libxl__strdup(gc, disk->pdev_path);
+        LOG(DEBUG, "Directly accessing local RAW disk %s", path);
+        goto out;
+    }
+
+    /*
+     * If we're being called for a qemu path, we can pass the target
+     * string directly as well
+     */
+    if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
+        path = libxl__strdup(gc, disk->pdev_path);
+        LOG(DEBUG, "Directly accessing local QDISK target %s", path);
+        goto out;
+    }
+
+    /*
+     * If the format isn't raw and / or we're using a script, then see
+     * if the script has written a path to the "cooked" node
+     */
+    if (disk->script && guest_domid != INVALID_DOMID) {
+        libxl__device device;
+        char *be_path, *pdpath;
+        int rc;
+
+        LOGD(DEBUG, guest_domid,
+             "Run from a script; checking for physical-device-path (vdev %s)",
+             disk->vdev);
+
+        rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
+        if (rc < 0)
+            goto out;
+
+        be_path = libxl__device_backend_path(gc, &device);
+
+        pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
+
+        LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
+        path = libxl__xs_read(gc, XBT_NULL, pdpath);
+
+        if (path)
+            LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
+        else
+            LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
+
+        goto out;
+    }
+
+ out:
+    return path;
+}
+
+static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
+
+void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
+                                     libxl__disk_local_state *dls)
+{
+    STATE_AO_GC(dls->ao);
+    int rc;
+    const libxl_device_disk *in_disk = dls->in_disk;
+    libxl_device_disk *disk = &dls->disk;
+    const char *blkdev_start = dls->blkdev_start;
+
+    assert(in_disk->pdev_path);
+
+    disk->vdev = NULL;
+
+    if (dls->diskpath)
+        LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
+
+    LOG(DEBUG, "Trying to find local path");
+
+    dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
+                                                       in_disk, false);
+    if (dls->diskpath) {
+        LOG(DEBUG, "Local path found, executing callback.");
+        dls->callback(egc, dls, 0);
+    } else {
+        LOG(DEBUG, "Local path not found, initiating attach.");
+
+        memcpy(disk, in_disk, sizeof(libxl_device_disk));
+        disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
+        if (in_disk->script != NULL)
+            disk->script = libxl__strdup(gc, in_disk->script);
+        disk->vdev = NULL;
+
+        rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
+                                           false);
+        if (rc) goto out;
+
+        libxl__prepare_ao_device(ao, &dls->aodev);
+        dls->aodev.callback = local_device_attach_cb;
+        device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
+                        libxl__alloc_vdev, (void *) blkdev_start);
+    }
+
+    return;
+
+ out:
+    assert(rc);
+    dls->rc = rc;
+    libxl__device_disk_local_initiate_detach(egc, dls);
+    dls->callback(egc, dls, rc);
+}
+
+static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
+    char *be_path = NULL;
+    int rc;
+    libxl__device device;
+    libxl_device_disk *disk = &dls->disk;
+
+    rc = aodev->rc;
+    if (rc) {
+        LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
+        goto out;
+    }
+
+    rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
+    if (rc < 0)
+        goto out;
+    be_path = libxl__device_backend_path(gc, &device);
+    rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
+    if (rc < 0)
+        goto out;
+
+    dls->diskpath = GCSPRINTF("/dev/%s",
+                              libxl__devid_to_localdev(gc, device.devid));
+    LOG(DEBUG, "locally attached disk %s", dls->diskpath);
+
+    dls->callback(egc, dls, 0);
+    return;
+
+ out:
+    assert(rc);
+    dls->rc = rc;
+    libxl__device_disk_local_initiate_detach(egc, dls);
+    return;
+}
+
+/* Callbacks for local detach */
+
+static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
+
+void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
+                                     libxl__disk_local_state *dls)
+{
+    STATE_AO_GC(dls->ao);
+    int rc = 0;
+    libxl_device_disk *disk = &dls->disk;
+    libxl__device *device;
+    libxl__ao_device *aodev = &dls->aodev;
+    libxl__prepare_ao_device(ao, aodev);
+
+    if (!dls->diskpath) goto out;
+
+    if (disk->vdev != NULL) {
+        GCNEW(device);
+        rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
+                                     disk, device);
+        if (rc != 0) goto out;
+
+        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+        aodev->dev = device;
+        aodev->callback = local_device_detach_cb;
+        aodev->force.flag = LIBXL__FORCE_AUTO;
+        libxl__initiate_device_generic_remove(egc, aodev);
+        return;
+    }
+
+out:
+    aodev->rc = rc;
+    local_device_detach_cb(egc, aodev);
+    return;
+}
+
+static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
+    int rc;
+
+    if (aodev->rc) {
+        LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
+                     libxl__device_action_to_string(aodev->action),
+                     libxl__device_kind_to_string(aodev->dev->kind),
+                     aodev->dev->devid);
+        goto out;
+    }
+
+out:
+    /*
+     * If there was an error in dls->rc, it means we have been called from
+     * a failed execution of libxl__device_disk_local_initiate_attach,
+     * so return the original error.
+     */
+    rc = dls->rc ? dls->rc : aodev->rc;
+    dls->callback(egc, dls, rc);
+    return;
+}
+
+/* The following functions are defined:
+ * libxl_device_disk_add
+ * libxl__add_disks
+ * libxl_device_disk_remove
+ * libxl_device_disk_destroy
+ * libxl_device_disk_safe_remove
+ */
+LIBXL_DEFINE_DEVICE_ADD(disk)
+LIBXL_DEFINE_DEVICES_ADD(disk)
+LIBXL_DEFINE_DEVICE_REMOVE(disk)
+LIBXL_DEFINE_DEVICE_SAFE_REMOVE(disk)
+
+static int libxl_device_disk_compare(const libxl_device_disk *d1,
+                                     const libxl_device_disk *d2)
+{
+    return COMPARE_DISK(d1, d2);
+}
+
+/* Take care of removable device. We maintain invariant in the
+ * insert / remove operation so that:
+ * 1. if xenstore is "empty" while JSON is not, the result
+ *    is "empty"
+ * 2. if xenstore has a different media than JSON, use the
+ *    one in JSON
+ * 3. if xenstore and JSON have the same media, well, you
+ *    know the answer :-)
+ *
+ * Currently there is only one removable device -- CDROM.
+ * Look for libxl_cdrom_insert for reference.
+ */
+static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
+{
+    GC_INIT(ctx);
+    libxl_device_disk *src = d1;
+    libxl_device_disk *dst = d2;
+
+    if (src->removable) {
+        if (!src->pdev_path || *src->pdev_path == '\0') {
+            /* 1, no media in drive */
+            free(dst->pdev_path);
+            dst->pdev_path = libxl__strdup(NOGC, "");
+            dst->format = LIBXL_DISK_FORMAT_EMPTY;
+        } else {
+            /* 2 and 3, use JSON, no need to touch anything */
+            ;
+        }
+    }
+}
+
+static int libxl_device_disk_dm_needed(void *e, unsigned domid)
+{
+    libxl_device_disk *elem = e;
+
+    return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
+           elem->backend_domid == domid;
+}
+
+LIBXL_DEFINE_DEVICE_LIST(disk)
+
+#define libxl__device_disk_update_devid NULL
+
+DEFINE_DEVICE_TYPE_STRUCT(disk, VBD,
+    .merge       = libxl_device_disk_merge,
+    .dm_needed   = libxl_device_disk_dm_needed,
+    .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
+    .skip_attach = 1,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c
new file mode 100644 (file)
index 0000000..fec4e0f
--- /dev/null
@@ -0,0 +1,3795 @@
+/*
+ * Copyright (C) 2010      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ * Author Gianni Tedesco <gianni.tedesco@citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <xenctrl_dom.h>
+#include <xen/hvm/e820.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+static const char *libxl_tapif_script(libxl__gc *gc)
+{
+#if defined(__linux__) || defined(__FreeBSD__)
+    return libxl__strdup(gc, "no");
+#else
+    return GCSPRINTF("%s/qemu-ifup", libxl__xen_script_dir_path());
+#endif
+}
+
+const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid)
+{
+    return GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", domid);
+}
+
+static const char *qemu_xen_path(libxl__gc *gc)
+{
+    return QEMU_XEN_PATH;
+}
+
+static int libxl__create_qemu_logfile(libxl__gc *gc, char *name)
+{
+    char *logfile;
+    int rc, logfile_w;
+
+    rc = libxl_create_logfile(CTX, name, &logfile);
+    if (rc) return rc;
+
+    logfile_w = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
+
+    if (logfile_w < 0) {
+        LOGE(ERROR, "unable to open Qemu logfile: %s", logfile);
+        free(logfile);
+        return ERROR_FAIL;
+    }
+
+    free(logfile);
+
+    return logfile_w;
+}
+
+/*
+ *  userlookup_helper_getpwnam(libxl__gc*, const char *user,
+ *                             struct passwd **pwd_r);
+ *
+ *  userlookup_helper_getpwuid(libxl__gc*, uid_t uid,
+ *                             struct passwd **pwd_r);
+ *
+ *  If the user is found, return 0 and set *pwd_r to the appropriat
+ *  value.
+ *
+ *  If the user is not found but there are no errors, return 0
+ *  and set *pwd_r to NULL.
+ *
+ *  On error, return a libxl-style error code.
+ */
+#define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF)     \
+    static int userlookup_helper_##NAME(libxl__gc *gc,                  \
+                                        SPEC_TYPE spec,                 \
+                                        struct STRUCTNAME *resultbuf,   \
+                                        struct STRUCTNAME **out)        \
+    {                                                                   \
+        struct STRUCTNAME *resultp = NULL;                              \
+        char *buf = NULL;                                               \
+        long buf_size;                                                  \
+        int r;                                                          \
+                                                                        \
+        buf_size = sysconf(SYSCONF);                                    \
+        if (buf_size < 0) {                                             \
+            buf_size = 2048;                                            \
+            LOG(DEBUG,                                                  \
+    "sysconf failed, setting the initial buffer size to %ld",           \
+                buf_size);                                              \
+        }                                                               \
+                                                                        \
+        while (1) {                                                     \
+            buf = libxl__realloc(gc, buf, buf_size);                    \
+            r = NAME##_r(spec, resultbuf, buf, buf_size, &resultp);     \
+            if (r == ERANGE) {                                          \
+                buf_size += 128;                                        \
+                continue;                                               \
+            }                                                           \
+            if (r != 0) {                                               \
+                LOGEV(ERROR, r, "Looking up username/uid with " #NAME); \
+                return ERROR_FAIL;                                      \
+            }                                                           \
+            *out = resultp;                                             \
+            return 0;                                                   \
+        }                                                               \
+    }
+
+DEFINE_USERLOOKUP_HELPER(getpwnam, const char*, passwd, _SC_GETPW_R_SIZE_MAX);
+DEFINE_USERLOOKUP_HELPER(getpwuid, uid_t,       passwd, _SC_GETPW_R_SIZE_MAX);
+
+static int libxl__domain_get_device_model_uid(libxl__gc *gc,
+                                              libxl__dm_spawn_state *dmss)
+{
+    int guest_domid = dmss->guest_domid;
+    libxl__domain_build_state *const state = dmss->build_state;
+    const libxl_domain_build_info *b_info = &dmss->guest_config->b_info;
+
+    struct passwd *user_base, user_pwbuf;
+    int rc;
+    char *user;
+    uid_t intended_uid = -1;
+    bool kill_by_uid;
+
+    /* Only qemu-upstream can run as a different uid */
+    if (b_info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN)
+        return 0;
+
+    /*
+     * From this point onward, all paths should go through the `out`
+     * label.  The invariants should be:
+     * - rc may be 0, or an error code.
+     * - if rc is an error code, user and intended_uid are ignored.
+     * - if rc is 0, user may be set or not set.
+     * - if user is set, then intended_uid must be set to a UID matching
+     *   the username `user`, and kill_by_uid must be set to the appropriate
+     *   value.  intended_uid will be checked for root (0).
+     */
+    
+    /*
+     * If device_model_user is present, set `-runas` even if
+     * dm_restrict isn't in use
+     */
+    user = b_info->device_model_user;
+    if (user) {
+        rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
+        if (rc)
+            goto out;
+
+        if (!user_base) {
+            LOGD(ERROR, guest_domid, "Couldn't find device_model_user %s",
+                 user);
+            rc = ERROR_INVAL;
+            goto out;
+        }
+
+        intended_uid = user_base->pw_uid;
+        kill_by_uid = true;
+        rc = 0;
+        goto out;
+    }
+
+    /*
+     * If dm_restrict isn't set, and we don't have a specified user, don't
+     * bother setting a `-runas` parameter.
+     */
+    if (!libxl_defbool_val(b_info->dm_restrict)) {
+        LOGD(DEBUG, guest_domid,
+             "dm_restrict disabled, starting QEMU as root");
+        user = NULL; /* Should already be null, but just in case */
+        kill_by_uid = false; /* Keep older versions of gcc happy */
+        rc = 0;
+        goto out;
+    }
+
+    /*
+     * dm_restrict is set, but device_model_user isn't set; look for
+     * QEMU_USER_BASE_RANGE
+     */
+    rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
+                                         &user_pwbuf, &user_base);
+    if (rc)
+        goto out;
+    if (user_base) {
+        struct passwd *user_clash, user_clash_pwbuf;
+
+        intended_uid = user_base->pw_uid + guest_domid;
+        rc = userlookup_helper_getpwuid(gc, intended_uid,
+                                         &user_clash_pwbuf, &user_clash);
+        if (rc)
+            goto out;
+        if (user_clash) {
+            LOGD(ERROR, guest_domid,
+                 "wanted to use uid %ld (%s + %d) but that is user %s !",
+                 (long)intended_uid, LIBXL_QEMU_USER_RANGE_BASE,
+                 guest_domid, user_clash->pw_name);
+            rc = ERROR_INVAL;
+            goto out;
+        }
+
+        LOGD(DEBUG, guest_domid, "using uid %ld", (long)intended_uid);
+        user = GCSPRINTF("%ld:%ld", (long)intended_uid,
+                         (long)user_base->pw_gid);
+        kill_by_uid = true;
+        rc = 0;
+        goto out;
+    }
+
+    /*
+     * We couldn't find QEMU_USER_BASE_RANGE; look for
+     * QEMU_USER_SHARED.  NB for QEMU_USER_SHARED, all QEMU will run
+     * as the same UID, we can't kill by uid; therefore don't set uid.
+     */
+    user = LIBXL_QEMU_USER_SHARED;
+    rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
+    if (rc)
+        goto out;
+    if (user_base) {
+        LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s",
+             LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED);
+        intended_uid = user_base->pw_uid;
+        kill_by_uid = false;
+        rc = 0;
+        goto out;
+    }
+
+    /*
+     * dm_depriv is set, but we can't find a non-root uid to run as;
+     * fail domain creation
+     */
+    LOGD(ERROR, guest_domid,
+         "Could not find user %s or range base pseudo-user %s, cannot restrict",
+         LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE);
+    rc = ERROR_INVAL;
+
+out:
+    /* First, do a root check if appropriate */
+    if (!rc) {
+        if (user && intended_uid == 0) {
+            LOGD(ERROR, guest_domid, "intended_uid is 0 (root)!");
+            rc = ERROR_INVAL;
+        }
+    }
+
+    /* Then do the final set, if still appropriate */
+    if (!rc && user) {
+        state->dm_runas = user;
+        if (kill_by_uid)
+            state->dm_kill_uid = GCSPRINTF("%ld", (long)intended_uid);
+    }
+
+    return rc;
+}
+
+/*
+ * Look up "reaper UID".  If present and non-root, returns 0 and sets
+ * reaper_uid.  Otherwise returns libxl-style error.
+ */
+static int libxl__get_reaper_uid(libxl__gc *gc, uid_t *reaper_uid)
+{
+    struct passwd *user_base, user_pwbuf;
+    int rc;
+
+    rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_REAPER,
+                                         &user_pwbuf, &user_base);
+    /* 
+     * Either there was an error, or we found a suitable user; stop
+     * looking
+     */
+    if (rc || user_base)
+        goto out;
+
+    rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
+                                         &user_pwbuf, &user_base);
+    if (rc || user_base)
+        goto out;
+
+    LOG(WARN, "Couldn't find uid for reaper process");
+    rc = ERROR_INVAL;
+
+ out:
+    /* First check to see if the discovered user maps to root */
+    if (!rc) {
+        if (user_base->pw_uid == 0) {
+            LOG(ERROR, "UID for reaper process maps to root!");
+            rc = ERROR_INVAL;
+        }
+    }
+
+    /* If everything is OK, set reaper_uid as appropriate */
+    if (!rc)
+        *reaper_uid = user_base->pw_uid;
+
+    return rc;
+}
+
+const char *libxl__domain_device_model(libxl__gc *gc,
+                                       const libxl_domain_build_info *info)
+{
+    const char *dm;
+
+    if (libxl_defbool_val(info->device_model_stubdomain))
+        return NULL;
+
+    if (info->device_model) {
+        dm = libxl__strdup(gc, info->device_model);
+    } else {
+        switch (info->device_model_version) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            dm = libxl__abs_path(gc, "qemu-dm", libxl__private_bindir_path());
+            break;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            dm = qemu_xen_path(gc);
+            break;
+        default:
+            LOG(ERROR, "invalid device model version %d",
+                info->device_model_version);
+            dm = NULL;
+            break;
+        }
+    }
+    return dm;
+}
+
+static int
+libxl__xc_device_get_rdm(libxl__gc *gc,
+                         uint32_t flags,
+                         uint16_t seg,
+                         uint8_t bus,
+                         uint8_t devfn,
+                         unsigned int *nr_entries,
+                         struct xen_reserved_device_memory **xrdm)
+{
+    int rc = 0, r;
+
+    /*
+     * We really can't presume how many entries we can get in advance.
+     */
+    *nr_entries = 0;
+    r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
+                                      NULL, nr_entries);
+    assert(r <= 0);
+    /* "0" means we have no any rdm entry. */
+    if (!r) goto out;
+
+    if (errno != ENOBUFS) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    GCNEW_ARRAY(*xrdm, *nr_entries);
+    r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
+                                      *xrdm, nr_entries);
+    if (r)
+        rc = ERROR_FAIL;
+
+ out:
+    if (rc) {
+        *nr_entries = 0;
+        *xrdm = NULL;
+        LOG(ERROR, "Could not get reserved device memory maps.");
+    }
+    return rc;
+}
+
+/*
+ * Check whether there exists rdm hole in the specified memory range.
+ * Returns true if exists, else returns false.
+ */
+static bool overlaps_rdm(uint64_t start, uint64_t memsize,
+                         uint64_t rdm_start, uint64_t rdm_size)
+{
+    return (start + memsize > rdm_start) && (start < rdm_start + rdm_size);
+}
+
+static void
+add_rdm_entry(libxl__gc *gc, libxl_domain_config *d_config,
+              uint64_t rdm_start, uint64_t rdm_size, int rdm_policy)
+{
+    d_config->rdms = libxl__realloc(NOGC, d_config->rdms,
+                    (d_config->num_rdms+1) * sizeof(libxl_device_rdm));
+
+    d_config->rdms[d_config->num_rdms].start = rdm_start;
+    d_config->rdms[d_config->num_rdms].size = rdm_size;
+    d_config->rdms[d_config->num_rdms].policy = rdm_policy;
+    d_config->num_rdms++;
+}
+
+/*
+ * Check reported RDM regions and handle potential gfn conflicts according
+ * to user preferred policy.
+ *
+ * RDM can reside in address space beyond 4G theoretically, but we never
+ * see this in real world. So in order to avoid breaking highmem layout
+ * we don't solve highmem conflict. Note this means highmem rmrr could
+ * still be supported if no conflict.
+ *
+ * But in the case of lowmem, RDM probably scatter the whole RAM space.
+ * Especially multiple RDM entries would worsen this to lead a complicated
+ * memory layout. And then its hard to extend hvm_info_table{} to work
+ * hvmloader out. So here we're trying to figure out a simple solution to
+ * avoid breaking existing layout. So when a conflict occurs,
+ *
+ * #1. Above a predefined boundary (default 2G)
+ * - Move lowmem_end below reserved region to solve conflict;
+ *
+ * #2. Below a predefined boundary (default 2G)
+ * - Check strict/relaxed policy.
+ * "strict" policy leads to fail libxl.
+ * "relaxed" policy issue a warning message and also mask this entry
+ * INVALID to indicate we shouldn't expose this entry to hvmloader.
+ * Note when both policies are specified on a given region, the per-device
+ * policy should override the global policy.
+ */
+int libxl__domain_device_construct_rdm(libxl__gc *gc,
+                                       libxl_domain_config *d_config,
+                                       uint64_t rdm_mem_boundary,
+                                       struct xc_dom_image *dom)
+{
+    int i, j, conflict, rc;
+    struct xen_reserved_device_memory *xrdm = NULL;
+    uint32_t strategy = d_config->b_info.u.hvm.rdm.strategy;
+    uint16_t seg;
+    uint8_t bus, devfn;
+    uint64_t rdm_start, rdm_size;
+    uint64_t highmem_end = dom->highmem_end;
+
+    /*
+     * We just want to construct RDM once since RDM is specific to the
+     * given platform, so this shouldn't change again.
+     */
+    if (d_config->num_rdms)
+        return 0;
+
+    /* Might not expose rdm. */
+    if (strategy == LIBXL_RDM_RESERVE_STRATEGY_IGNORE &&
+        !d_config->num_pcidevs)
+        return 0;
+
+    /* Query all RDM entries in this platform */
+    if (strategy == LIBXL_RDM_RESERVE_STRATEGY_HOST) {
+        unsigned int nr_entries;
+
+        /* Collect all rdm info if exist. */
+        rc = libxl__xc_device_get_rdm(gc, XENMEM_RDM_ALL,
+                                      0, 0, 0, &nr_entries, &xrdm);
+        if (rc)
+            goto out;
+        if (!nr_entries)
+            return 0;
+
+        assert(xrdm);
+
+        for (i = 0; i < nr_entries; i++)
+        {
+            add_rdm_entry(gc, d_config,
+                          pfn_to_paddr(xrdm[i].start_pfn),
+                          pfn_to_paddr(xrdm[i].nr_pages),
+                          d_config->b_info.u.hvm.rdm.policy);
+        }
+    }
+
+    /* Query RDM entries per-device */
+    for (i = 0; i < d_config->num_pcidevs; i++) {
+        unsigned int n, nr_entries;
+
+        seg = d_config->pcidevs[i].domain;
+        bus = d_config->pcidevs[i].bus;
+        devfn = PCI_DEVFN(d_config->pcidevs[i].dev,
+                          d_config->pcidevs[i].func);
+        nr_entries = 0;
+        rc = libxl__xc_device_get_rdm(gc, 0,
+                                      seg, bus, devfn, &nr_entries, &xrdm);
+        if (rc)
+            goto out;
+        /* No RDM to associated with this device. */
+        if (!nr_entries)
+            continue;
+
+        assert(xrdm);
+
+        rc = libxl__device_pci_setdefault(gc, DOMID_INVALID,
+                                          &d_config->pcidevs[i], false);
+        if (rc)
+            goto out;
+
+        for (n = 0; n < nr_entries; ++n) {
+            bool new = true;
+
+            /*
+             * Need to check whether this entry is already saved in the
+             * array. This could come from two cases:
+             *
+             *   - user may configure to get all RDMs in this platform,
+             *   which is already queried before this point
+             *   - or two assigned devices may share one RDM entry
+             *
+             * Different policies may be configured on the same RDM due to
+             * above two cases. But we don't allow to assign such a group
+             * of devices right now so it doesn't come true in our case.
+             */
+            for (j = 0; j < d_config->num_rdms; j++) {
+                if (d_config->rdms[j].start
+                    == pfn_to_paddr(xrdm[n].start_pfn))
+                {
+                    /*
+                     * So the per-device policy always override the
+                     * global policy in this case.
+                     */
+                    d_config->rdms[j].policy
+                        = d_config->pcidevs[i].rdm_policy;
+                    new = false;
+                    break;
+                }
+            }
+
+            if (new)
+                add_rdm_entry(gc, d_config,
+                              pfn_to_paddr(xrdm[n].start_pfn),
+                              pfn_to_paddr(xrdm[n].nr_pages),
+                              d_config->pcidevs[i].rdm_policy);
+        }
+    }
+
+    /*
+     * Next step is to check and avoid potential conflict between RDM
+     * entries and guest RAM. To avoid intrusive impact to existing
+     * memory layout {lowmem, mmio, highmem} which is passed around
+     * various function blocks, below conflicts are not handled which
+     * are rare and handling them would lead to a more scattered
+     * layout:
+     *  - RDM  in highmem area (>4G)
+     *  - RDM lower than a defined memory boundary (e.g. 2G)
+     * Otherwise for conflicts between boundary and 4G, we'll simply
+     * move lowmem end below reserved region to solve conflict.
+     *
+     * If a conflict is detected on a given RDM entry, an error will
+     * be returned if 'strict' policy is specified. Instead, if
+     * 'relaxed' policy specified, this conflict is treated just as a
+     * warning, but we mark this RDM entry as INVALID to indicate that
+     * this entry shouldn't be exposed to hvmloader.
+     *
+     * Firstly we should check the case of rdm < 4G because we may
+     * need to expand highmem_end.
+     */
+    for (i = 0; i < d_config->num_rdms; i++) {
+        rdm_start = d_config->rdms[i].start;
+        rdm_size = d_config->rdms[i].size;
+        conflict = overlaps_rdm(0, dom->lowmem_end, rdm_start, rdm_size);
+
+        if (!conflict)
+            continue;
+
+        /* Just check if RDM > our memory boundary. */
+        if (rdm_start > rdm_mem_boundary) {
+            /*
+             * For HAP guests round down to a 2Mb boundary to allow use
+             * of large page mappings.
+             */
+            if (libxl_defbool_val(d_config->c_info.hap)
+                && rdm_start > MB(2))
+                rdm_start &= ~(MB(2) - 1);
+            /*
+             * We will move downwards lowmem_end so we have to expand
+             * highmem_end.
+             */
+            if (!highmem_end)
+                highmem_end = 1ull << 32;
+            highmem_end += (dom->lowmem_end - rdm_start);
+            /* Now move downwards lowmem_end. */
+            dom->lowmem_end = rdm_start;
+        }
+    }
+
+    /* Sync highmem_end. */
+    dom->highmem_end = highmem_end;
+
+    /*
+     * Finally we can take same policy to check lowmem(< 2G) and
+     * highmem adjusted above.
+     */
+    for (i = 0; i < d_config->num_rdms; i++) {
+        rdm_start = d_config->rdms[i].start;
+        rdm_size = d_config->rdms[i].size;
+        /* Does this entry conflict with lowmem? */
+        conflict = overlaps_rdm(0, dom->lowmem_end,
+                                rdm_start, rdm_size);
+        /* Does this entry conflict with highmem? */
+        if (highmem_end)
+            conflict |= overlaps_rdm((1ULL << 32),
+                                     highmem_end - (1ULL << 32),
+                                     rdm_start, rdm_size);
+
+        if (!conflict)
+            continue;
+
+        if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
+            LOG(ERROR, "RDM conflict at 0x%"PRIx64".\n",
+                       d_config->rdms[i].start);
+            goto out;
+        } else {
+            LOG(WARN, "Ignoring RDM conflict at 0x%"PRIx64".\n",
+                      d_config->rdms[i].start);
+
+            /*
+             * Then mask this INVALID to indicate we shouldn't expose this
+             * to hvmloader.
+             */
+            d_config->rdms[i].policy = LIBXL_RDM_RESERVE_POLICY_INVALID;
+        }
+    }
+
+    return 0;
+
+ out:
+    return ERROR_FAIL;
+}
+
+/* XSA-180 / CVE-2014-3672
+ *
+ * The QEMU shipped with Xen has a bodge. It checks for
+ * XEN_QEMU_CONSOLE_LIMIT to see how much data QEMU is allowed
+ * to write to stderr. We set that to 1MB if it is not set by
+ * system administrator.
+ */
+static void libxl__set_qemu_env_for_xsa_180(libxl__gc *gc,
+                                            flexarray_t *dm_envs)
+{
+    if (getenv("XEN_QEMU_CONSOLE_LIMIT")) return;
+    flexarray_append_pair(dm_envs, "XEN_QEMU_CONSOLE_LIMIT", "1048576");
+}
+
+const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *guest_config)
+{
+    const libxl_vnc_info *vnc = NULL;
+    if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+        vnc = &guest_config->b_info.u.hvm.vnc;
+    } else if (guest_config->num_vfbs > 0) {
+        vnc = &guest_config->vfbs[0].vnc;
+    }
+    return vnc && libxl_defbool_val(vnc->enable) ? vnc : NULL;
+}
+
+static const libxl_sdl_info *dm_sdl(const libxl_domain_config *guest_config)
+{
+    const libxl_sdl_info *sdl = NULL;
+    if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+        sdl = &guest_config->b_info.u.hvm.sdl;
+    } else if (guest_config->num_vfbs > 0) {
+        sdl = &guest_config->vfbs[0].sdl;
+    }
+    return sdl && libxl_defbool_val(sdl->enable) ? sdl : NULL;
+}
+
+static const char *dm_keymap(const libxl_domain_config *guest_config)
+{
+    if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
+        return guest_config->b_info.u.hvm.keymap;
+    } else if (guest_config->num_vfbs > 0) {
+        return guest_config->vfbs[0].keymap;
+    } else
+        return NULL;
+}
+
+static int libxl__build_device_model_args_old(libxl__gc *gc,
+                                        const char *dm, int domid,
+                                        const libxl_domain_config *guest_config,
+                                        char ***args, char ***envs,
+                                        const libxl__domain_build_state *state)
+{
+    const libxl_domain_create_info *c_info = &guest_config->c_info;
+    const libxl_domain_build_info *b_info = &guest_config->b_info;
+    const libxl_device_nic *nics = guest_config->nics;
+    const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
+    const libxl_sdl_info *sdl = dm_sdl(guest_config);
+    const int num_nics = guest_config->num_nics;
+    const char *keymap = dm_keymap(guest_config);
+    int i;
+    flexarray_t *dm_args, *dm_envs;
+    dm_args = flexarray_make(gc, 16, 1);
+    dm_envs = flexarray_make(gc, 16, 1);
+
+    assert(state->dm_monitor_fd == -1);
+
+    libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
+
+    flexarray_vappend(dm_args, dm,
+                      "-d", GCSPRINTF("%d", domid), NULL);
+
+    if (c_info->name)
+        flexarray_vappend(dm_args, "-domain-name", c_info->name, NULL);
+
+    if (vnc) {
+        char *vncarg = NULL;
+
+        flexarray_append(dm_args, "-vnc");
+
+        /*
+         * If vnc->listen is present and contains a :, and
+         *  - vnc->display is 0, use vnc->listen
+         *  - vnc->display is non-zero, be confused
+         * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
+         * If vnc->listen is not present, use 127.0.0.1:vnc->display
+         * (Remembering that vnc->display already defaults to 0.)
+         */
+        if (vnc->listen) {
+            if (strchr(vnc->listen, ':') != NULL) {
+                if (vnc->display) {
+                    LOGD(ERROR, domid, "vncdisplay set, vnclisten contains display");
+                    return ERROR_INVAL;
+                }
+                vncarg = vnc->listen;
+            } else {
+                vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
+            }
+        } else
+            vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
+
+        if (vnc->passwd && vnc->passwd[0]) {
+            vncarg = GCSPRINTF("%s,password", vncarg);
+        }
+
+        flexarray_append(dm_args, vncarg);
+
+        if (libxl_defbool_val(vnc->findunused)) {
+            flexarray_append(dm_args, "-vncunused");
+        }
+    } else if (!sdl) {
+        /*
+         * VNC is not enabled by default by qemu-xen-traditional,
+         * however skipping -vnc causes SDL to be
+         * (unexpectedly) enabled by default. If undesired, disable graphics at
+         * all.
+         */
+        flexarray_append(dm_args, "-nographic");
+    }
+
+    if (sdl) {
+        flexarray_append(dm_args, "-sdl");
+        if (!libxl_defbool_val(sdl->opengl)) {
+            flexarray_append(dm_args, "-disable-opengl");
+        }
+        if (sdl->display)
+            flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
+        if (sdl->xauthority)
+            flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
+    }
+    if (keymap) {
+        flexarray_vappend(dm_args, "-k", keymap, NULL);
+    }
+    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        int ioemu_nics = 0;
+        int nr_set_cpus = 0;
+        char *s;
+
+        if (b_info->kernel) {
+            LOGD(ERROR, domid, "HVM direct kernel boot is not supported by "
+                 "qemu-xen-traditional");
+            return ERROR_INVAL;
+        }
+
+        if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
+            if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
+            {
+                LOGD(ERROR, domid, "Both serial and serial_list set");
+                return ERROR_INVAL;
+            }
+            if (b_info->u.hvm.serial) {
+                flexarray_vappend(dm_args,
+                                  "-serial", b_info->u.hvm.serial, NULL);
+            } else if (b_info->u.hvm.serial_list) {
+                char **p;
+                for (p = b_info->u.hvm.serial_list;
+                     *p;
+                     p++) {
+                    flexarray_vappend(dm_args,
+                                      "-serial",
+                                      *p, NULL);
+                }
+            }
+        }
+
+        if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
+            flexarray_append(dm_args, "-nographic");
+        }
+
+        if (b_info->video_memkb) {
+            flexarray_vappend(dm_args, "-videoram",
+                    GCSPRINTF("%d", libxl__sizekb_to_mb(b_info->video_memkb)),
+                    NULL);
+        }
+
+        switch (b_info->u.hvm.vga.kind) {
+        case LIBXL_VGA_INTERFACE_TYPE_STD:
+            flexarray_append(dm_args, "-std-vga");
+            break;
+        case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+            break;
+        case LIBXL_VGA_INTERFACE_TYPE_NONE:
+            flexarray_append_pair(dm_args, "-vga", "none");
+            break;
+        case LIBXL_VGA_INTERFACE_TYPE_QXL:
+            break;
+        default:
+            LOGD(ERROR, domid, "Invalid emulated video card specified");
+            return ERROR_INVAL;
+        }
+
+        if (b_info->u.hvm.boot) {
+            flexarray_vappend(dm_args, "-boot", b_info->u.hvm.boot, NULL);
+        }
+        if (libxl_defbool_val(b_info->u.hvm.usb)
+            || b_info->u.hvm.usbdevice
+            || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+            if (b_info->u.hvm.usbdevice
+                && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+                LOGD(ERROR, domid, "Both usbdevice and usbdevice_list set");
+                return ERROR_INVAL;
+            }
+            flexarray_append(dm_args, "-usb");
+            if (b_info->u.hvm.usbdevice) {
+                flexarray_vappend(dm_args,
+                                  "-usbdevice", b_info->u.hvm.usbdevice, NULL);
+            } else if (b_info->u.hvm.usbdevice_list) {
+                char **p;
+                for (p = b_info->u.hvm.usbdevice_list;
+                     *p;
+                     p++) {
+                    flexarray_vappend(dm_args,
+                                      "-usbdevice",
+                                      *p, NULL);
+                }
+            }
+        }
+        if (b_info->u.hvm.soundhw) {
+            flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
+        }
+        if (libxl__acpi_defbool_val(b_info)) {
+            flexarray_append(dm_args, "-acpi");
+        }
+        if (b_info->max_vcpus > 1) {
+            flexarray_vappend(dm_args, "-vcpus",
+                              GCSPRINTF("%d", b_info->max_vcpus),
+                              NULL);
+        }
+
+        nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
+        s = libxl_bitmap_to_hex_string(CTX, &b_info->avail_vcpus);
+        flexarray_vappend(dm_args, "-vcpu_avail",
+                              GCSPRINTF("%s", s), NULL);
+        free(s);
+
+        for (i = 0; i < num_nics; i++) {
+            if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
+                char *smac = GCSPRINTF(
+                                   LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac));
+                const char *ifname = libxl__device_nic_devname(gc,
+                                                domid, nics[i].devid,
+                                                LIBXL_NIC_TYPE_VIF_IOEMU);
+                flexarray_vappend(dm_args,
+                                  "-net",
+                                  GCSPRINTF(
+                                      "nic,vlan=%d,macaddr=%s,model=%s",
+                                      nics[i].devid, smac, nics[i].model),
+                                  "-net",
+                                  GCSPRINTF(
+                                      "tap,vlan=%d,ifname=%s,bridge=%s,"
+                                      "script=%s,downscript=%s",
+                                      nics[i].devid, ifname, nics[i].bridge,
+                                      libxl_tapif_script(gc),
+                                      libxl_tapif_script(gc)),
+                                  NULL);
+                ioemu_nics++;
+            }
+        }
+        /* If we have no emulated nics, tell qemu not to create any */
+        if ( ioemu_nics == 0 ) {
+            flexarray_vappend(dm_args, "-net", "none", NULL);
+        }
+        if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
+            switch (b_info->u.hvm.gfx_passthru_kind) {
+            case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
+            case LIBXL_GFX_PASSTHRU_KIND_IGD:
+                flexarray_append(dm_args, "-gfx_passthru");
+                break;
+            default:
+                LOGD(ERROR, domid, "unsupported gfx_passthru_kind.");
+                return ERROR_INVAL;
+            }
+        }
+    } else {
+        if (!sdl && !vnc)
+            flexarray_append(dm_args, "-nographic");
+    }
+
+    if (libxl_defbool_val(b_info->dm_restrict)) {
+        LOGD(ERROR, domid,
+             "dm_restrict not supported by qemu-xen-traditional");
+        return ERROR_INVAL;
+    }
+
+    if (state->saved_state) {
+        flexarray_vappend(dm_args, "-loadvm", state->saved_state, NULL);
+    }
+    for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
+        flexarray_append(dm_args, b_info->extra[i]);
+    flexarray_append(dm_args, "-M");
+    switch (b_info->type) {
+    case LIBXL_DOMAIN_TYPE_PVH:
+    case LIBXL_DOMAIN_TYPE_PV:
+        flexarray_append(dm_args, "xenpv");
+        for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
+            flexarray_append(dm_args, b_info->extra_pv[i]);
+        break;
+    case LIBXL_DOMAIN_TYPE_HVM:
+        flexarray_append(dm_args, "xenfv");
+        for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
+            flexarray_append(dm_args, b_info->extra_hvm[i]);
+        break;
+    default:
+        abort();
+    }
+    flexarray_append(dm_args, NULL);
+    *args = (char **) flexarray_contents(dm_args);
+    flexarray_append(dm_envs, NULL);
+    if (envs)
+        *envs = (char **) flexarray_contents(dm_envs);
+    return 0;
+}
+
+static char *dm_spice_options(libxl__gc *gc,
+                                    const libxl_spice_info *spice)
+{
+    char *opt;
+
+    if (!spice->port && !spice->tls_port) {
+        LOG(ERROR,
+            "at least one of the spiceport or tls_port must be provided");
+        return NULL;
+    }
+
+    if (!libxl_defbool_val(spice->disable_ticketing)) {
+        if (!spice->passwd) {
+            LOG(ERROR, "spice ticketing is enabled but missing password");
+            return NULL;
+        }
+        else if (!spice->passwd[0]) {
+            LOG(ERROR, "spice password can't be empty");
+            return NULL;
+        }
+    }
+    opt = GCSPRINTF("port=%d,tls-port=%d", spice->port, spice->tls_port);
+    if (spice->host)
+        opt = GCSPRINTF("%s,addr=%s", opt, spice->host);
+    if (libxl_defbool_val(spice->disable_ticketing))
+        opt = GCSPRINTF("%s,disable-ticketing", opt);
+    else
+        opt = GCSPRINTF("%s,password=%s", opt, spice->passwd);
+    opt = GCSPRINTF("%s,agent-mouse=%s", opt,
+                    libxl_defbool_val(spice->agent_mouse) ? "on" : "off");
+
+    if (!libxl_defbool_val(spice->clipboard_sharing))
+        opt = GCSPRINTF("%s,disable-copy-paste", opt);
+
+    if (spice->image_compression)
+        opt = GCSPRINTF("%s,image-compression=%s", opt,
+                        spice->image_compression);
+
+    if (spice->streaming_video)
+        opt = GCSPRINTF("%s,streaming-video=%s", opt, spice->streaming_video);
+
+    return opt;
+}
+
+static enum libxl_gfx_passthru_kind
+libxl__detect_gfx_passthru_kind(libxl__gc *gc,
+                                const libxl_domain_config *guest_config)
+{
+    const libxl_domain_build_info *b_info = &guest_config->b_info;
+
+    if (b_info->u.hvm.gfx_passthru_kind != LIBXL_GFX_PASSTHRU_KIND_DEFAULT)
+        return b_info->u.hvm.gfx_passthru_kind;
+
+    if (libxl__is_igd_vga_passthru(gc, guest_config)) {
+        return LIBXL_GFX_PASSTHRU_KIND_IGD;
+    }
+
+    return LIBXL_GFX_PASSTHRU_KIND_DEFAULT;
+}
+
+/* colo mode */
+enum {
+    LIBXL__COLO_NONE = 0,
+    LIBXL__COLO_PRIMARY,
+    LIBXL__COLO_SECONDARY,
+};
+
+static char *qemu_disk_scsi_drive_string(libxl__gc *gc, const char *target_path,
+                                         int unit, const char *format,
+                                         const libxl_device_disk *disk,
+                                         int colo_mode, const char **id_ptr)
+{
+    char *drive = NULL;
+    char *common = GCSPRINTF("if=none,readonly=%s,cache=writeback",
+                             disk->readwrite ? "off" : "on");
+    const char *exportname = disk->colo_export;
+    const char *active_disk = disk->active_disk;
+    const char *hidden_disk = disk->hidden_disk;
+    const char *id;
+
+    switch (colo_mode) {
+    case LIBXL__COLO_NONE:
+        id = GCSPRINTF("scsi0-hd%d", unit);
+        drive = GCSPRINTF("file=%s,id=%s,format=%s,%s",
+                          target_path, id, format, common);
+        break;
+    case LIBXL__COLO_PRIMARY:
+        id = exportname;
+        drive = GCSPRINTF(
+            "%s,id=%s,driver=quorum,"
+            "children.0.file.filename=%s,"
+            "children.0.driver=%s,"
+            "read-pattern=fifo,"
+            "vote-threshold=1",
+            common, id, target_path, format);
+        break;
+    case LIBXL__COLO_SECONDARY:
+        id = "top-colo";
+        drive = GCSPRINTF(
+            "%s,id=%s,driver=replication,"
+            "mode=secondary,"
+            "top-id=top-colo,"
+            "file.driver=qcow2,"
+            "file.file.filename=%s,"
+            "file.backing.driver=qcow2,"
+            "file.backing.file.filename=%s,"
+            "file.backing.backing=%s",
+            common, id, active_disk, hidden_disk, exportname);
+        break;
+    default:
+        abort();
+    }
+
+    *id_ptr = id;
+
+    return drive;
+}
+
+static char *qemu_disk_ide_drive_string(libxl__gc *gc, const char *target_path,
+                                        int unit, const char *format,
+                                        const libxl_device_disk *disk,
+                                        int colo_mode)
+{
+    char *drive = NULL;
+    const char *exportname = disk->colo_export;
+    const char *active_disk = disk->active_disk;
+    const char *hidden_disk = disk->hidden_disk;
+
+    assert(disk->readwrite); /* should have been checked earlier */
+
+    switch (colo_mode) {
+    case LIBXL__COLO_NONE:
+        drive = GCSPRINTF
+            ("file=%s,if=ide,index=%d,media=disk,format=%s,cache=writeback",
+             target_path, unit, format);
+        break;
+    case LIBXL__COLO_PRIMARY:
+        /*
+         * primary:
+         *  -dirve if=ide,index=x,media=disk,cache=writeback,driver=quorum,\
+         *  id=exportname,\
+         *  children.0.file.filename=target_path,\
+         *  children.0.driver=format,\
+         *  read-pattern=fifo,\
+         *  vote-threshold=1
+         */
+        drive = GCSPRINTF(
+            "if=ide,index=%d,media=disk,cache=writeback,driver=quorum,"
+            "id=%s,"
+            "children.0.file.filename=%s,"
+            "children.0.driver=%s,"
+            "read-pattern=fifo,"
+            "vote-threshold=1",
+             unit, exportname, target_path, format);
+        break;
+    case LIBXL__COLO_SECONDARY:
+        /*
+         * secondary:
+         *  -drive if=ide,index=x,media=disk,cache=writeback,driver=replication,\
+         *  mode=secondary,\
+         *  file.driver=qcow2,\
+         *  file.file.filename=active_disk,\
+         *  file.backing.driver=qcow2,\
+         *  file.backing.file.filename=hidden_disk,\
+         *  file.backing.backing=exportname,
+         */
+        drive = GCSPRINTF(
+            "if=ide,index=%d,id=top-colo,media=disk,cache=writeback,"
+            "driver=replication,"
+            "mode=secondary,"
+            "top-id=top-colo,"
+            "file.driver=qcow2,"
+            "file.file.filename=%s,"
+            "file.backing.driver=qcow2,"
+            "file.backing.file.filename=%s,"
+            "file.backing.backing=%s",
+            unit, active_disk, hidden_disk, exportname);
+        break;
+    default:
+         abort();
+    }
+
+    return drive;
+}
+
+static int libxl__pre_open_qmp_socket(libxl__gc *gc, libxl_domid domid,
+                                      int *fd_r)
+{
+    int rc, r;
+    int fd;
+    struct sockaddr_un un;
+    const char *path = libxl__qemu_qmp_path(gc, domid);
+
+    fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (fd < 0) {
+        LOGED(ERROR, domid, "socket() failed");
+        return ERROR_FAIL;
+    }
+
+    rc = libxl__prepare_sockaddr_un(gc, &un, path, "QEMU's QMP socket");
+    if (rc)
+        goto out;
+
+    rc = libxl__remove_file(gc, path);
+    if (rc)
+        goto out;
+
+    r = bind(fd, (struct sockaddr *) &un, sizeof(un));
+    if (r < 0) {
+        LOGED(ERROR, domid, "bind('%s') failed", path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    r = listen(fd, 1);
+    if (r < 0) {
+        LOGED(ERROR, domid, "listen() failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    *fd_r = fd;
+    rc = 0;
+
+out:
+    if (rc && fd >= 0)
+        close(fd);
+    return rc;
+}
+
+static int libxl__build_device_model_args_new(libxl__gc *gc,
+                                        const char *dm, int guest_domid,
+                                        const libxl_domain_config *guest_config,
+                                        char ***args, char ***envs,
+                                        const libxl__domain_build_state *state,
+                                        int *dm_state_fd)
+{
+    const libxl_domain_create_info *c_info = &guest_config->c_info;
+    const libxl_domain_build_info *b_info = &guest_config->b_info;
+    const libxl_device_disk *disks = guest_config->disks;
+    const libxl_device_nic *nics = guest_config->nics;
+    const int num_disks = guest_config->num_disks;
+    const int num_nics = guest_config->num_nics;
+    const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
+    const libxl_sdl_info *sdl = dm_sdl(guest_config);
+    const char *keymap = dm_keymap(guest_config);
+    char *machinearg;
+    flexarray_t *dm_args, *dm_envs;
+    int i, connection, devid;
+    uint64_t ram_size;
+    const char *path, *chardev;
+    bool is_stubdom = libxl_defbool_val(b_info->device_model_stubdomain);
+
+    dm_args = flexarray_make(gc, 16, 1);
+    dm_envs = flexarray_make(gc, 16, 1);
+
+    libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
+
+    flexarray_vappend(dm_args, dm,
+                      "-xen-domid",
+                      GCSPRINTF("%d", guest_domid), NULL);
+    flexarray_append(dm_args, "-no-shutdown");
+
+    /*
+     * QMP access to qemu running in stubdomain is done over vchan. The
+     * stubdomain init script adds the appropriate monitor options for
+     * vchan-socket-proxy.
+     */
+    if (!is_stubdom) {
+        flexarray_append(dm_args, "-chardev");
+        if (state->dm_monitor_fd >= 0) {
+            flexarray_append(dm_args,
+                GCSPRINTF("socket,id=libxl-cmd,fd=%d,server,nowait",
+                          state->dm_monitor_fd));
+
+            /*
+             * Start QEMU with its "CPU" paused, it will not start any emulation
+             * until the QMP command "cont" is used. This also prevent QEMU from
+             * writing "running" to the "state" xenstore node so we only use this
+             * flag when we have the QMP based startup notification.
+             * */
+            flexarray_append(dm_args, "-S");
+        } else {
+            flexarray_append(dm_args,
+                             GCSPRINTF("socket,id=libxl-cmd,"
+                                       "path=%s,server,nowait",
+                                       libxl__qemu_qmp_path(gc, guest_domid)));
+        }
+
+        flexarray_append(dm_args, "-mon");
+        flexarray_append(dm_args, "chardev=libxl-cmd,mode=control");
+
+        flexarray_append(dm_args, "-chardev");
+        flexarray_append(dm_args,
+                         GCSPRINTF("socket,id=libxenstat-cmd,"
+                                        "path=%s/qmp-libxenstat-%d,server,nowait",
+                                        libxl__run_dir_path(), guest_domid));
+
+        flexarray_append(dm_args, "-mon");
+        flexarray_append(dm_args, "chardev=libxenstat-cmd,mode=control");
+    }
+
+    for (i = 0; i < guest_config->num_channels; i++) {
+        connection = guest_config->channels[i].connection;
+        devid = guest_config->channels[i].devid;
+        switch (connection) {
+            case LIBXL_CHANNEL_CONNECTION_PTY:
+                chardev = GCSPRINTF("pty,id=libxl-channel%d", devid);
+                break;
+            case LIBXL_CHANNEL_CONNECTION_SOCKET:
+                path = guest_config->channels[i].u.socket.path;
+                chardev = GCSPRINTF("socket,id=libxl-channel%d,path=%s,"
+                                    "server,nowait", devid, path);
+                break;
+            default:
+                /* We've forgotten to add the clause */
+                LOGD(ERROR, guest_domid, "%s: unknown channel connection %d",
+                    __func__, connection);
+                return ERROR_INVAL;
+        }
+        flexarray_append(dm_args, "-chardev");
+        flexarray_append(dm_args, (void*)chardev);
+    }
+
+    /*
+     * Remove default devices created by qemu. Qemu will create only devices
+     * defined by xen, since the devices not defined by xen are not usable.
+     */
+    flexarray_append(dm_args, "-nodefaults");
+
+    /*
+     * Do not use any of the user-provided config files in sysconfdir,
+     * avoiding unknown and uncontrolled configuration.
+     */
+    flexarray_append(dm_args, "-no-user-config");
+
+    if (b_info->type != LIBXL_DOMAIN_TYPE_HVM) {
+        flexarray_append(dm_args, "-xen-attach");
+    }
+
+    if (c_info->name) {
+        flexarray_vappend(dm_args, "-name", c_info->name, NULL);
+    }
+
+    if (vnc && !is_stubdom) {
+        char *vncarg = NULL;
+
+        flexarray_append(dm_args, "-vnc");
+
+        /*
+         * If vnc->listen is present and contains a :, and
+         *  - vnc->display is 0, use vnc->listen
+         *  - vnc->display is non-zero, be confused
+         * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
+         * If vnc->listen is not present, use 127.0.0.1:vnc->display
+         * (Remembering that vnc->display already defaults to 0.)
+         */
+        if (vnc->listen) {
+            if (strchr(vnc->listen, ':') != NULL) {
+                if (vnc->display) {
+                    LOGD(ERROR, guest_domid,
+                         "vncdisplay set, vnclisten contains display");
+                    return ERROR_INVAL;
+                }
+                vncarg = vnc->listen;
+            } else {
+                vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
+            }
+        } else
+            vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
+
+        if (vnc->passwd && vnc->passwd[0]) {
+            vncarg = GCSPRINTF("%s,password", vncarg);
+        }
+
+        if (libxl_defbool_val(vnc->findunused)) {
+            /* This option asks to QEMU to try this number of port before to
+             * give up.  So QEMU will try ports between $display and $display +
+             * 99.  This option needs to be the last one of the vnc options. */
+            vncarg = GCSPRINTF("%s,to=99", vncarg);
+        }
+
+        flexarray_append(dm_args, vncarg);
+    } else if (!is_stubdom) {
+        /*
+         * Ensure that by default no vnc server is created.
+         */
+        flexarray_append_pair(dm_args, "-vnc", "none");
+    }
+
+    /*
+     * Ensure that by default no display backend is created. Further
+     * options given below might then enable more.
+     */
+    flexarray_append_pair(dm_args, "-display", "none");
+
+    if (sdl && !is_stubdom) {
+        flexarray_append(dm_args, "-sdl");
+        if (sdl->display)
+            flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
+        if (sdl->xauthority)
+            flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
+    }
+
+    if (keymap) {
+        flexarray_vappend(dm_args, "-k", keymap, NULL);
+    }
+
+    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        int ioemu_nics = 0;
+
+        if (b_info->kernel)
+            flexarray_vappend(dm_args, "-kernel", b_info->kernel, NULL);
+
+        if (b_info->ramdisk)
+            flexarray_vappend(dm_args, "-initrd", b_info->ramdisk, NULL);
+
+        if (b_info->cmdline)
+            flexarray_vappend(dm_args, "-append", b_info->cmdline, NULL);
+
+        /* Find out early if one of the disk is on the scsi bus and add a scsi
+         * controller. This is done ahead to keep the same behavior as previous
+         * version of QEMU (have the controller on the same PCI slot). */
+        for (i = 0; i < num_disks; i++) {
+            if (disks[i].is_cdrom) {
+                continue;
+            }
+            if (strncmp(disks[i].vdev, "sd", 2) == 0) {
+                flexarray_vappend(dm_args, "-device", "lsi53c895a", NULL);
+                break;
+            }
+        }
+
+        if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
+            if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
+            {
+                LOGD(ERROR, guest_domid, "Both serial and serial_list set");
+                return ERROR_INVAL;
+            } else {
+                if (b_info->u.hvm.serial) {
+                    if (is_stubdom) {
+                        /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
+                        flexarray_vappend(dm_args,
+                                          "-serial",
+                                          GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL),
+                                          NULL);
+                    } else {
+                        flexarray_vappend(dm_args,
+                                          "-serial", b_info->u.hvm.serial, NULL);
+                    }
+                } else if (b_info->u.hvm.serial_list) {
+                    char **p;
+                    /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
+                    for (p = b_info->u.hvm.serial_list, i = 0;
+                         *p;
+                         p++, i++) {
+                        if (is_stubdom)
+                            flexarray_vappend(dm_args,
+                                              "-serial",
+                                              GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL + i),
+                                              NULL);
+                        else
+                            flexarray_vappend(dm_args,
+                                              "-serial",
+                                              *p, NULL);
+                    }
+                }
+            }
+        }
+
+        if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
+            flexarray_append(dm_args, "-nographic");
+        }
+
+        if (libxl_defbool_val(b_info->u.hvm.spice.enable) && !is_stubdom) {
+            const libxl_spice_info *spice = &b_info->u.hvm.spice;
+            char *spiceoptions = dm_spice_options(gc, spice);
+            if (!spiceoptions)
+                return ERROR_INVAL;
+
+            flexarray_append(dm_args, "-spice");
+            flexarray_append(dm_args, spiceoptions);
+            if (libxl_defbool_val(b_info->u.hvm.spice.vdagent)) {
+                flexarray_vappend(dm_args, "-device", "virtio-serial",
+                    "-chardev", "spicevmc,id=vdagent,name=vdagent", "-device",
+                    "virtserialport,chardev=vdagent,name=com.redhat.spice.0",
+                    NULL);
+            }
+        }
+
+        switch (b_info->u.hvm.vga.kind) {
+        case LIBXL_VGA_INTERFACE_TYPE_STD:
+            flexarray_append_pair(dm_args, "-device",
+                GCSPRINTF("VGA,vgamem_mb=%d",
+                libxl__sizekb_to_mb(b_info->video_memkb)));
+            break;
+        case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
+            flexarray_append_pair(dm_args, "-device",
+                GCSPRINTF("cirrus-vga,vgamem_mb=%d",
+                libxl__sizekb_to_mb(b_info->video_memkb)));
+            break;
+        case LIBXL_VGA_INTERFACE_TYPE_NONE:
+            break;
+        case LIBXL_VGA_INTERFACE_TYPE_QXL:
+            /* QXL have 2 ram regions, ram and vram */
+            flexarray_append_pair(dm_args, "-device",
+                GCSPRINTF("qxl-vga,vram_size_mb=%"PRIu64",ram_size_mb=%"PRIu64,
+                (b_info->video_memkb/2/1024), (b_info->video_memkb/2/1024) ) );
+            break;
+        default:
+            LOGD(ERROR, guest_domid, "Invalid emulated video card specified");
+            return ERROR_INVAL;
+        }
+
+        if (b_info->u.hvm.boot) {
+            flexarray_vappend(dm_args, "-boot",
+                    GCSPRINTF("order=%s", b_info->u.hvm.boot), NULL);
+        }
+        if (libxl_defbool_val(b_info->u.hvm.usb)
+            || b_info->u.hvm.usbdevice
+            || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+            if (b_info->u.hvm.usbdevice
+                && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
+                LOGD(ERROR, guest_domid, "Both usbdevice and usbdevice_list set");
+                return ERROR_INVAL;
+            }
+            flexarray_append(dm_args, "-usb");
+            if (b_info->u.hvm.usbdevice) {
+                flexarray_vappend(dm_args,
+                                  "-usbdevice", b_info->u.hvm.usbdevice, NULL);
+            } else if (b_info->u.hvm.usbdevice_list) {
+                char **p;
+                for (p = b_info->u.hvm.usbdevice_list;
+                     *p;
+                     p++) {
+                    flexarray_vappend(dm_args,
+                                      "-usbdevice",
+                                      *p, NULL);
+                }
+            }
+        } else if (b_info->u.hvm.usbversion) {
+            switch (b_info->u.hvm.usbversion) {
+            case 1:
+                flexarray_vappend(dm_args,
+                    "-device", "piix3-usb-uhci,id=usb", NULL);
+                break;
+            case 2:
+                flexarray_append_pair(dm_args, "-device",
+                    "ich9-usb-ehci1,id=usb,addr=0x1d.0x7,multifunction=on");
+                for (i = 1; i < 4; i++)
+                    flexarray_append_pair(dm_args, "-device",
+                        GCSPRINTF("ich9-usb-uhci%d,masterbus=usb.0,"
+                        "firstport=%d,addr=0x1d.%#x,multifunction=on",
+                        i, 2*(i-1), i-1));
+                break;
+            case 3:
+                flexarray_vappend(dm_args,
+                    "-device", "nec-usb-xhci,id=usb", NULL);
+                break;
+            default:
+                LOGD(ERROR, guest_domid, "usbversion parameter is invalid, "
+                    "must be between 1 and 3");
+                return ERROR_INVAL;
+            }
+            if (b_info->u.hvm.spice.usbredirection >= 0 &&
+                b_info->u.hvm.spice.usbredirection < 5) {
+                for (i = 1; i <= b_info->u.hvm.spice.usbredirection; i++)
+                    flexarray_vappend(dm_args, "-chardev",
+                        GCSPRINTF("spicevmc,name=usbredir,id=usbrc%d", i),
+                        "-device",
+                        GCSPRINTF("usb-redir,chardev=usbrc%d,"
+                        "id=usbrc%d", i, i), NULL);
+            } else {
+                LOGD(ERROR, guest_domid, "usbredirection parameter is invalid, "
+                    "it must be between 1 and 4");
+                return ERROR_INVAL;
+            }
+        }
+        if (b_info->u.hvm.soundhw) {
+            flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
+        }
+        if (!libxl__acpi_defbool_val(b_info)) {
+            flexarray_append(dm_args, "-no-acpi");
+        }
+        if (b_info->max_vcpus > 1) {
+            flexarray_append(dm_args, "-smp");
+            if (b_info->avail_vcpus.size) {
+                int nr_set_cpus = 0;
+                nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
+
+                flexarray_append(dm_args, GCSPRINTF("%d,maxcpus=%d",
+                                                    nr_set_cpus,
+                                                    b_info->max_vcpus));
+            } else
+                flexarray_append(dm_args, GCSPRINTF("%d", b_info->max_vcpus));
+        }
+        for (i = 0; i < num_nics; i++) {
+            if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
+                char *smac = GCSPRINTF(LIBXL_MAC_FMT,
+                                       LIBXL_MAC_BYTES(nics[i].mac));
+                const char *ifname = libxl__device_nic_devname(gc,
+                                                guest_domid, nics[i].devid,
+                                                LIBXL_NIC_TYPE_VIF_IOEMU);
+                flexarray_append(dm_args, "-device");
+                flexarray_append(dm_args,
+                   GCSPRINTF("%s,id=nic%d,netdev=net%d,mac=%s",
+                             nics[i].model, nics[i].devid,
+                             nics[i].devid, smac));
+                flexarray_append(dm_args, "-netdev");
+                flexarray_append(dm_args,
+                                 GCSPRINTF("type=tap,id=net%d,ifname=%s,"
+                                           "script=%s,downscript=%s",
+                                           nics[i].devid, ifname,
+                                           libxl_tapif_script(gc),
+                                           libxl_tapif_script(gc)));
+
+                /* Userspace COLO Proxy need this */
+#define APPEND_COLO_SOCK_SERVER(sock_id, sock_ip, sock_port) ({             \
+    if (nics[i].colo_##sock_id &&                                           \
+        nics[i].colo_##sock_ip &&                                           \
+        nics[i].colo_##sock_port) {                                         \
+        flexarray_append(dm_args, "-chardev");                              \
+        flexarray_append(dm_args,                                           \
+            GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait",         \
+                      nics[i].colo_##sock_id,                               \
+                      nics[i].colo_##sock_ip,                               \
+                      nics[i].colo_##sock_port));                           \
+        }                                                                   \
+})
+
+#define APPEND_COLO_SOCK_CLIENT(sock_id, sock_ip, sock_port) ({             \
+    if (nics[i].colo_##sock_id &&                                           \
+        nics[i].colo_##sock_ip &&                                           \
+        nics[i].colo_##sock_port) {                                         \
+        flexarray_append(dm_args, "-chardev");                              \
+        flexarray_append(dm_args,                                           \
+            GCSPRINTF("socket,id=%s,host=%s,port=%s",                       \
+                      nics[i].colo_##sock_id,                               \
+                      nics[i].colo_##sock_ip,                               \
+                      nics[i].colo_##sock_port));                           \
+        }                                                                   \
+})
+
+                if (state->saved_state) {
+                    /* secondary colo run */
+
+                    APPEND_COLO_SOCK_CLIENT(sock_sec_redirector0_id,
+                                            sock_sec_redirector0_ip,
+                                            sock_sec_redirector0_port);
+
+                    APPEND_COLO_SOCK_CLIENT(sock_sec_redirector1_id,
+                                            sock_sec_redirector1_ip,
+                                            sock_sec_redirector1_port);
+
+                    if (nics[i].colo_filter_sec_redirector0_queue &&
+                        nics[i].colo_filter_sec_redirector0_indev) {
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args,
+                           GCSPRINTF("filter-redirector,id=rs1,netdev=net%d,queue=%s,indev=%s",
+                                     nics[i].devid,
+                                     nics[i].colo_filter_sec_redirector0_queue,
+                                     nics[i].colo_filter_sec_redirector0_indev));
+                    }
+                    if (nics[i].colo_filter_sec_redirector1_queue &&
+                        nics[i].colo_filter_sec_redirector1_outdev) {
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args,
+                           GCSPRINTF("filter-redirector,id=rs2,netdev=net%d,queue=%s,outdev=%s",
+                                     nics[i].devid,
+                                     nics[i].colo_filter_sec_redirector1_queue,
+                                     nics[i].colo_filter_sec_redirector1_outdev));
+                    }
+                    if (nics[i].colo_filter_sec_rewriter0_queue) {
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args,
+                           GCSPRINTF("filter-rewriter,id=rs3,netdev=net%d,queue=%s",
+                                     nics[i].devid,
+                                     nics[i].colo_filter_sec_rewriter0_queue));
+                    }
+                } else {
+                    /* primary colo run */
+
+                    APPEND_COLO_SOCK_SERVER(sock_mirror_id,
+                                            sock_mirror_ip,
+                                            sock_mirror_port);
+
+                    APPEND_COLO_SOCK_SERVER(sock_compare_pri_in_id,
+                                            sock_compare_pri_in_ip,
+                                            sock_compare_pri_in_port);
+
+                    APPEND_COLO_SOCK_SERVER(sock_compare_sec_in_id,
+                                            sock_compare_sec_in_ip,
+                                            sock_compare_sec_in_port);
+
+                    APPEND_COLO_SOCK_SERVER(sock_compare_notify_id,
+                                            sock_compare_notify_ip,
+                                            sock_compare_notify_port);
+
+                    APPEND_COLO_SOCK_SERVER(sock_redirector0_id,
+                                            sock_redirector0_ip,
+                                            sock_redirector0_port);
+
+                    APPEND_COLO_SOCK_CLIENT(sock_redirector1_id,
+                                            sock_redirector1_ip,
+                                            sock_redirector1_port);
+
+                    APPEND_COLO_SOCK_CLIENT(sock_redirector2_id,
+                                            sock_redirector2_ip,
+                                            sock_redirector2_port);
+
+                    if (nics[i].colo_filter_mirror_queue &&
+                        nics[i].colo_filter_mirror_outdev) {
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args,
+                           GCSPRINTF("filter-mirror,id=m1,netdev=net%d,queue=%s,outdev=%s",
+                                     nics[i].devid,
+                                     nics[i].colo_filter_mirror_queue,
+                                     nics[i].colo_filter_mirror_outdev));
+                    }
+                    if (nics[i].colo_filter_redirector0_queue &&
+                        nics[i].colo_filter_redirector0_indev) {
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args,
+                           GCSPRINTF("filter-redirector,id=r1,netdev=net%d,queue=%s,indev=%s",
+                                     nics[i].devid,
+                                     nics[i].colo_filter_redirector0_queue,
+                                     nics[i].colo_filter_redirector0_indev));
+                    }
+                    if (nics[i].colo_filter_redirector1_queue &&
+                        nics[i].colo_filter_redirector1_outdev) {
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args,
+                          GCSPRINTF("filter-redirector,id=r2,netdev=net%d,queue=%s,outdev=%s",
+                                     nics[i].devid,
+                                     nics[i].colo_filter_redirector1_queue,
+                                     nics[i].colo_filter_redirector1_outdev));
+                    }
+                    if (nics[i].colo_compare_pri_in &&
+                        nics[i].colo_compare_sec_in &&
+                        nics[i].colo_compare_out &&
+                        nics[i].colo_compare_notify_dev) {
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args, "iothread,id=colo-compare-iothread-1");
+                        flexarray_append(dm_args, "-object");
+                        flexarray_append(dm_args,
+                           GCSPRINTF("colo-compare,id=c1,primary_in=%s,secondary_in=%s,outdev=%s,notify_dev=%s,iothread=colo-compare-iothread-1",
+                                     nics[i].colo_compare_pri_in,
+                                     nics[i].colo_compare_sec_in,
+                                     nics[i].colo_compare_out,
+                                     nics[i].colo_compare_notify_dev));
+                    }
+                }
+                ioemu_nics++;
+
+#undef APPEND_COLO_SOCK_SERVER
+#undef APPEND_COLO_SOCK_CLIENT
+            }
+        }
+        /* If we have no emulated nics, tell qemu not to create any */
+        if ( ioemu_nics == 0 ) {
+            flexarray_append(dm_args, "-net");
+            flexarray_append(dm_args, "none");
+        }
+    } else {
+        if (!sdl && !vnc) {
+            flexarray_append(dm_args, "-nographic");
+        }
+    }
+
+    if (libxl_defbool_val(b_info->dm_restrict)) {
+        char *chroot_dir = GCSPRINTF("%s/qemu-root-%d",
+                                      libxl__run_dir_path(), guest_domid);
+        int r;
+
+        flexarray_append(dm_args, "-xen-domid-restrict");
+
+        /*
+         * Run QEMU in a chroot at XEN_RUN_DIR/qemu-root-<domid>
+         *
+         * There is no library function to do the equivalent of `rm
+         * -rf`.  However deprivileged QEMU in theory shouldn't be
+         * able to write any files, as the chroot would be owned by
+         * root, but it would be running as an unprivileged process.
+         * So in theory, old chroots should always be empty.
+         *
+         * rmdir the directory before attempting to create
+         * it; if it returns anything other than ENOENT, fail domain
+         * creation.
+         */
+        r = rmdir(chroot_dir);
+        if (r != 0 && errno != ENOENT) {
+            LOGED(ERROR, guest_domid,
+                  "failed to remove existing chroot dir %s", chroot_dir);
+            return ERROR_FAIL;
+        }
+
+        for (;;) {
+            r = mkdir(chroot_dir, 0000);
+            if (!r)
+                break;
+            if (errno == EINTR) continue;
+            LOGED(ERROR, guest_domid,
+                  "failed to create chroot dir %s", chroot_dir);
+            return ERROR_FAIL;
+        }
+
+        /* Add "-chroot [dir]" to command-line */
+        flexarray_append(dm_args, "-chroot");
+        flexarray_append(dm_args, chroot_dir);
+    }
+
+    if (state->saved_state) {
+        if (is_stubdom) {
+            /* Linux stubdomain must replace $STUBDOM_RESTORE_INCOMING_ARG
+             * with the approriate fd:$num argument for the
+             * STUBDOM_CONSOLE_RESTORE console 2.
+             */
+            flexarray_append(dm_args, "-incoming");
+            flexarray_append(dm_args, "$STUBDOM_RESTORE_INCOMING_ARG");
+        } else {
+            /* This file descriptor is meant to be used by QEMU */
+            *dm_state_fd = open(state->saved_state, O_RDONLY);
+            flexarray_append(dm_args, "-incoming");
+            flexarray_append(dm_args, GCSPRINTF("fd:%d",*dm_state_fd));
+        }
+    }
+    for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
+        flexarray_append(dm_args, b_info->extra[i]);
+
+    flexarray_append(dm_args, "-machine");
+    switch (b_info->type) {
+    case LIBXL_DOMAIN_TYPE_PVH:
+    case LIBXL_DOMAIN_TYPE_PV:
+        flexarray_append(dm_args, "xenpv");
+        for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
+            flexarray_append(dm_args, b_info->extra_pv[i]);
+        break;
+    case LIBXL_DOMAIN_TYPE_HVM:
+        if (!libxl_defbool_val(b_info->u.hvm.xen_platform_pci)) {
+            /* Switching here to the machine "pc" which does not add
+             * the xen-platform device instead of the default "xenfv" machine.
+             */
+            machinearg = libxl__strdup(gc, "pc,accel=xen");
+        } else {
+            machinearg = libxl__strdup(gc, "xenfv");
+        }
+        if (b_info->u.hvm.mmio_hole_memkb) {
+            uint64_t max_ram_below_4g = (1ULL << 32) -
+                (b_info->u.hvm.mmio_hole_memkb << 10);
+
+            if (max_ram_below_4g > HVM_BELOW_4G_MMIO_START) {
+                LOGD(WARN, guest_domid, "mmio_hole_memkb=%"PRIu64
+                     " invalid ignored.\n",
+                    b_info->u.hvm.mmio_hole_memkb);
+            } else {
+                machinearg = GCSPRINTF("%s,max-ram-below-4g=%"PRIu64,
+                                            machinearg, max_ram_below_4g);
+            }
+        }
+
+        if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
+            enum libxl_gfx_passthru_kind gfx_passthru_kind =
+                            libxl__detect_gfx_passthru_kind(gc, guest_config);
+            switch (gfx_passthru_kind) {
+            case LIBXL_GFX_PASSTHRU_KIND_IGD:
+                machinearg = GCSPRINTF("%s,igd-passthru=on", machinearg);
+                break;
+            case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
+                LOGD(ERROR, guest_domid, "unable to detect required gfx_passthru_kind");
+                return ERROR_FAIL;
+            default:
+                LOGD(ERROR, guest_domid, "invalid value for gfx_passthru_kind");
+                return ERROR_INVAL;
+            }
+        }
+
+        flexarray_append(dm_args, machinearg);
+        for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
+            flexarray_append(dm_args, b_info->extra_hvm[i]);
+        break;
+    default:
+        abort();
+    }
+
+    ram_size = libxl__sizekb_to_mb(b_info->max_memkb - b_info->video_memkb);
+    flexarray_append(dm_args, "-m");
+    flexarray_append(dm_args, GCSPRINTF("%"PRId64, ram_size));
+
+    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        if (b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI)
+            flexarray_append_pair(dm_args, "-device", "ahci,id=ahci0");
+        for (i = 0; i < num_disks; i++) {
+            int disk, part;
+            int dev_number =
+                libxl__device_disk_dev_number(disks[i].vdev, &disk, &part);
+            const char *format;
+            char *drive;
+            const char *target_path = NULL;
+            int colo_mode;
+
+            if (dev_number == -1) {
+                LOGD(WARN, guest_domid, "unable to determine"" disk number for %s",
+                     disks[i].vdev);
+                continue;
+            }
+
+            /*
+             * If qemu isn't doing the interpreting, the parameter is
+             * always raw
+             */
+            if (libxl_defbool_val(b_info->device_model_stubdomain))
+                format = "host_device";
+            else if (disks[i].backend == LIBXL_DISK_BACKEND_QDISK)
+                format = libxl__qemu_disk_format_string(disks[i].format);
+            else
+                format = libxl__qemu_disk_format_string(LIBXL_DISK_FORMAT_RAW);
+
+            if (disks[i].format == LIBXL_DISK_FORMAT_EMPTY) {
+                if (!disks[i].is_cdrom) {
+                    LOGD(WARN, guest_domid, "Cannot support empty disk format for %s",
+                         disks[i].vdev);
+                    continue;
+                }
+            } else if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+                if (disk > 'z' - 'a') {
+                    LOGD(WARN, guest_domid,
+                            "Emulation of only first %d disks is supported with qemu-xen in stubdomain.\n"
+                            "Disk %d will be available via PV drivers but not as an emulated disk.",
+                            'z' - 'a',
+                            disk);
+                    continue;
+                }
+                target_path = GCSPRINTF("/dev/xvd%c", 'a' + disk);
+            } else {
+                if (format == NULL) {
+                    LOGD(WARN, guest_domid,
+                         "Unable to determine disk image format: %s\n"
+                         "Disk will be available via PV drivers but not as an"
+                         "emulated disk.",
+                         disks[i].vdev);
+                    continue;
+                }
+
+                assert(disks[i].backend != LIBXL_DISK_BACKEND_TAP);
+                target_path = libxl__device_disk_find_local_path(gc,
+                                    guest_domid, &disks[i], true);
+
+                if (!target_path) {
+                    LOGD(WARN, guest_domid, "No way to get local access disk to image: %s\n"
+                         "Disk will be available via PV drivers but not as an"
+                         "emulated disk.",
+                         disks[i].vdev);
+                    continue;
+                }
+            }
+
+            if (disks[i].is_cdrom) {
+                if (disk > 4) {
+                    LOGD(WARN, guest_domid, "Emulated CDROM can be only one of the first 4 disks.\n"
+                         "Disk %s will be available via PV drivers but not as an "
+                         "emulated disk.",
+                         disks[i].vdev);
+                    continue;
+                }
+                drive = libxl__sprintf(gc,
+                         "if=ide,index=%d,readonly=on,media=cdrom,id=ide-%i",
+                         disk, dev_number);
+
+                if (target_path)
+                    drive = libxl__sprintf(gc, "%s,file=%s,format=%s",
+                                           drive, target_path, format);
+            } else {
+                /*
+                 * Explicit sd disks are passed through as is.
+                 *
+                 * For other disks we translate devices 0..3 into
+                 * hd[a-d] and ignore the rest.
+                 */
+
+                if (libxl_defbool_val(disks[i].colo_enable)) {
+                    if (libxl_defbool_val(disks[i].colo_restore_enable))
+                        colo_mode = LIBXL__COLO_SECONDARY;
+                    else
+                        colo_mode = LIBXL__COLO_PRIMARY;
+                } else {
+                    colo_mode = LIBXL__COLO_NONE;
+                }
+
+                if (strncmp(disks[i].vdev, "sd", 2) == 0) {
+                    const char *drive_id;
+                    if (colo_mode == LIBXL__COLO_SECONDARY) {
+                        drive = libxl__sprintf
+                            (gc, "if=none,driver=%s,file=%s,id=%s,readonly=%s",
+                             format, target_path, disks[i].colo_export,
+                             disks[i].readwrite ? "off" : "on");
+
+                        flexarray_append(dm_args, "-drive");
+                        flexarray_append(dm_args, drive);
+                    }
+                    drive = qemu_disk_scsi_drive_string(gc, target_path, disk,
+                                                        format,
+                                                        &disks[i],
+                                                        colo_mode,
+                                                        &drive_id),
+                    flexarray_vappend(dm_args,
+                        "-drive", drive,
+                        "-device", GCSPRINTF("scsi-disk,drive=%s,scsi-id=%d",
+                                             drive_id, disk),
+                        NULL);
+                    continue;
+                } else if (disk < 6 && b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI) {
+                    if (!disks[i].readwrite) {
+                        LOGD(ERROR, guest_domid,
+                             "qemu-xen doesn't support read-only AHCI disk drivers");
+                        return ERROR_INVAL;
+                    }
+                    flexarray_vappend(dm_args, "-drive",
+                        GCSPRINTF("file=%s,if=none,id=ahcidisk-%d,format=%s,cache=writeback",
+                        target_path, disk, format),
+                        "-device", GCSPRINTF("ide-hd,bus=ahci0.%d,unit=0,drive=ahcidisk-%d",
+                        disk, disk), NULL);
+                    continue;
+                } else if (disk < 4) {
+                    if (!disks[i].readwrite) {
+                        LOGD(ERROR, guest_domid,
+                             "qemu-xen doesn't support read-only IDE disk drivers");
+                        return ERROR_INVAL;
+                    }
+                    if (colo_mode == LIBXL__COLO_SECONDARY) {
+                        drive = libxl__sprintf
+                            (gc, "if=none,driver=%s,file=%s,id=%s",
+                             format, target_path, disks[i].colo_export);
+
+                        flexarray_append(dm_args, "-drive");
+                        flexarray_append(dm_args, drive);
+                    }
+                    drive = qemu_disk_ide_drive_string(gc, target_path, disk,
+                                                       format,
+                                                       &disks[i],
+                                                       colo_mode);
+                } else {
+                    LOGD(WARN, guest_domid, "Only 4 emulated IDE disks are supported.\n"
+                         "Disk %s will be available via PV drivers but not as an "
+                         "emulated disk.",
+                         disks[i].vdev);
+                    continue; /* Do not emulate this disk */
+                }
+
+                if (!drive)
+                    continue;
+            }
+
+            flexarray_append(dm_args, "-drive");
+            flexarray_append(dm_args, drive);
+        }
+
+        switch (b_info->u.hvm.vendor_device) {
+        case LIBXL_VENDOR_DEVICE_XENSERVER:
+            flexarray_append(dm_args, "-device");
+            flexarray_append(dm_args, "xen-pvdevice,device-id=0xc000");
+            break;
+        default:
+            break;
+        }
+
+        if (state->dm_runas) {
+            flexarray_append(dm_args, "-runas");
+            flexarray_append(dm_args, state->dm_runas);
+        }
+    }
+    flexarray_append(dm_args, NULL);
+    *args = (char **) flexarray_contents(dm_args);
+    flexarray_append(dm_envs, NULL);
+    if (envs)
+        *envs = (char **) flexarray_contents(dm_envs);
+    return 0;
+}
+
+static int libxl__build_device_model_args(libxl__gc *gc,
+                                        const char *dm, int guest_domid,
+                                        const libxl_domain_config *guest_config,
+                                        char ***args, char ***envs,
+                                        const libxl__domain_build_state *state,
+                                        int *dm_state_fd)
+/* dm_state_fd may be NULL iff caller knows we are using stubdom
+ * and therefore will be passing a filename rather than a fd. */
+{
+    switch (guest_config->b_info.device_model_version) {
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+        return libxl__build_device_model_args_old(gc, dm,
+                                                  guest_domid, guest_config,
+                                                  args, envs,
+                                                  state);
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+        if (!libxl_defbool_val(guest_config->b_info.device_model_stubdomain)) {
+            assert(dm_state_fd != NULL);
+            assert(*dm_state_fd < 0);
+        }
+        return libxl__build_device_model_args_new(gc, dm,
+                                                  guest_domid, guest_config,
+                                                  args, envs,
+                                                  state, dm_state_fd);
+    default:
+        LOGED(ERROR, guest_domid, "unknown device model version %d",
+              guest_config->b_info.device_model_version);
+        return ERROR_INVAL;
+    }
+}
+
+static void libxl__dm_vifs_from_hvm_guest_config(libxl__gc *gc,
+                                    libxl_domain_config * const guest_config,
+                                    libxl_domain_config *dm_config)
+{
+    int i, nr = guest_config->num_nics;
+
+    GCNEW_ARRAY(dm_config->nics, nr);
+
+    for (i=0; i<nr; i++) {
+        dm_config->nics[i] = guest_config->nics[i];
+        dm_config->nics[i].nictype = LIBXL_NIC_TYPE_VIF;
+        if (dm_config->nics[i].ifname)
+            dm_config->nics[i].ifname = GCSPRINTF("%s" TAP_DEVICE_SUFFIX,
+                                                  dm_config->nics[i].ifname);
+    }
+
+    dm_config->num_nics = nr;
+}
+
+static int libxl__vfb_and_vkb_from_hvm_guest_config(libxl__gc *gc,
+                                        const libxl_domain_config *guest_config,
+                                        libxl_device_vfb *vfb,
+                                        libxl_device_vkb *vkb)
+{
+    const libxl_domain_build_info *b_info = &guest_config->b_info;
+
+    if (b_info->type != LIBXL_DOMAIN_TYPE_HVM)
+        return ERROR_INVAL;
+
+    libxl_device_vfb_init(vfb);
+    libxl_device_vkb_init(vkb);
+
+    vfb->backend_domid = 0;
+    vfb->devid = 0;
+    vfb->vnc = b_info->u.hvm.vnc;
+    vfb->keymap = b_info->u.hvm.keymap;
+    vfb->sdl = b_info->u.hvm.sdl;
+
+    vkb->backend_domid = 0;
+    vkb->devid = 0;
+
+    return 0;
+}
+
+static int libxl__write_stub_dmargs(libxl__gc *gc,
+                                    int dm_domid, int guest_domid,
+                                    char **args, bool is_linux_stubdom)
+{
+    struct xs_permissions roperm[2];
+    xs_transaction_t t = XBT_NULL;
+    char *dmargs;
+    int rc;
+
+    roperm[0].id = 0;
+    roperm[0].perms = XS_PERM_NONE;
+    roperm[1].id = dm_domid;
+    roperm[1].perms = XS_PERM_READ;
+
+    if (!is_linux_stubdom) {
+        int dmargs_size = 0;
+        int i = 0;
+
+        while (args[i] != NULL) {
+            dmargs_size = dmargs_size + strlen(args[i]) + 1;
+            i++;
+        }
+
+        dmargs_size++;
+        dmargs = (char *) libxl__malloc(gc, dmargs_size);
+
+        i = 1;
+        dmargs[0] = '\0';
+        while (args[i] != NULL) {
+            if (strcmp(args[i], "-sdl") && strcmp(args[i], "-M") && strcmp(args[i], "xenfv")) {
+                strcat(dmargs, " ");
+                strcat(dmargs, args[i]);
+            }
+            i++;
+        }
+    }
+
+    for (;;) {
+        const char *vm_path;
+        char *path;
+
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        rc = libxl__xs_read_mandatory(gc, t,
+                                      GCSPRINTF("/local/domain/%d/vm",
+                                                guest_domid),
+                                      &vm_path);
+        if (rc) goto out;
+
+        if (is_linux_stubdom) {
+            int i;
+
+            path = GCSPRINTF("%s/image/dm-argv", vm_path);
+
+            rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
+            if (rc) goto out;
+
+            for (i=1; args[i] != NULL; i++) {
+                rc = libxl__xs_write_checked(gc, t,
+                                             GCSPRINTF("%s/%03d", path, i),
+                                             args[i]);
+                if (rc) goto out;
+            }
+        } else {
+            path = GCSPRINTF("%s/image/dmargs", vm_path);
+
+            rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
+            if (rc) goto out;
+
+            rc = libxl__xs_write_checked(gc, t, path, dmargs);
+            if (rc) goto out;
+
+            rc = libxl__xs_mknod(gc, t, GCSPRINTF("%s/rtc/timeoffset", vm_path),
+                                 roperm, ARRAY_SIZE(roperm));
+            if (rc) goto out;
+        }
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc<0) goto out;
+    }
+
+    return 0;
+
+ out:
+    libxl__xs_transaction_abort(gc, &t);
+
+    return rc;
+}
+
+static int libxl__store_libxl_entry(libxl__gc *gc, uint32_t domid,
+                                    const char *name, const char *value)
+{
+    char *path = NULL;
+
+    path = libxl__xs_libxl_path(gc, domid);
+    path = libxl__sprintf(gc, "%s/%s", path, name);
+    return libxl__xs_printf(gc, XBT_NULL, path, "%s", value);
+}
+
+static void dmss_init(libxl__dm_spawn_state *dmss)
+{
+    libxl__ev_qmp_init(&dmss->qmp);
+    libxl__ev_time_init(&dmss->timeout);
+}
+
+static void dmss_dispose(libxl__gc *gc, libxl__dm_spawn_state *dmss)
+{
+    libxl__ev_qmp_dispose(gc, &dmss->qmp);
+    libxl__ev_time_deregister(gc, &dmss->timeout);
+}
+
+static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
+                                libxl__dm_spawn_state *stubdom_dmss,
+                                int rc);
+
+static void spawn_stub_launch_dm(libxl__egc *egc,
+                                 libxl__multidev *aodevs, int ret);
+
+static void stubdom_pvqemu_cb(libxl__egc *egc,
+                              libxl__multidev *aodevs,
+                              int rc);
+static void stubdom_pvqemu_unpaused(libxl__egc *egc,
+                                    libxl__dm_resume_state *dmrs,
+                                    int rc);
+
+static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
+                              int rc, const char *p);
+
+static void spawn_qmp_proxy(libxl__egc *egc,
+                            libxl__stub_dm_spawn_state *sdss);
+
+static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+                              const char *xsdata);
+
+static void qmp_proxy_startup_failed(libxl__egc *egc,
+                                     libxl__spawn_state *spawn,
+                                     int rc);
+
+static void qmp_proxy_detached(libxl__egc *egc,
+                               libxl__spawn_state *spawn);
+
+static void qmp_proxy_spawn_outcome(libxl__egc *egc,
+                                    libxl__stub_dm_spawn_state *sdss,
+                                    int rc);
+
+char *libxl__stub_dm_name(libxl__gc *gc, const char *guest_name)
+{
+    return GCSPRINTF("%s-dm", guest_name);
+}
+
+void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state *sdss)
+{
+    STATE_AO_GC(sdss->dm.spawn.ao);
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int ret;
+    libxl_device_vfb *vfb;
+    libxl_device_vkb *vkb;
+    char **args;
+    struct xs_permissions perm[2];
+    xs_transaction_t t;
+
+    /* convenience aliases */
+    libxl_domain_config *const dm_config = &sdss->dm_config;
+    libxl_domain_config *const guest_config = sdss->dm.guest_config;
+    const int guest_domid = sdss->dm.guest_domid;
+    libxl__domain_build_state *const d_state = sdss->dm.build_state;
+    libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
+
+    /* Initialise private part of sdss */
+    libxl__domain_build_state_init(stubdom_state);
+    dmss_init(&sdss->dm);
+    dmss_init(&sdss->pvqemu);
+    libxl__xswait_init(&sdss->xswait);
+
+    assert(libxl_defbool_val(guest_config->b_info.device_model_stubdomain));
+
+    sdss->pvqemu.guest_domid = INVALID_DOMID;
+
+    libxl_domain_create_info_init(&dm_config->c_info);
+    dm_config->c_info.type = LIBXL_DOMAIN_TYPE_PV;
+    dm_config->c_info.name = libxl__stub_dm_name(gc,
+                                    libxl__domid_to_name(gc, guest_domid));
+    /* When we are here to launch stubdom, ssidref is a valid value
+     * already, no need to parse it again.
+     */
+    dm_config->c_info.ssidref = guest_config->b_info.device_model_ssidref;
+    dm_config->c_info.ssid_label = NULL;
+
+    libxl_uuid_generate(&dm_config->c_info.uuid);
+
+    libxl_domain_build_info_init(&dm_config->b_info);
+    libxl_domain_build_info_init_type(&dm_config->b_info, LIBXL_DOMAIN_TYPE_PV);
+
+    dm_config->b_info.shadow_memkb = 0;
+    dm_config->b_info.max_vcpus = 1;
+    dm_config->b_info.max_memkb = guest_config->b_info.stubdomain_memkb;
+    dm_config->b_info.max_memkb += guest_config->b_info.video_memkb;
+    dm_config->b_info.target_memkb = dm_config->b_info.max_memkb;
+
+    dm_config->b_info.max_grant_frames = guest_config->b_info.max_grant_frames;
+    dm_config->b_info.max_maptrack_frames = 0;
+
+    dm_config->b_info.u.pv.features = "";
+
+    dm_config->b_info.device_model_version =
+        guest_config->b_info.device_model_version;
+    dm_config->b_info.device_model =
+        guest_config->b_info.device_model;
+    dm_config->b_info.extra = guest_config->b_info.extra;
+    dm_config->b_info.extra_pv = guest_config->b_info.extra_pv;
+    dm_config->b_info.extra_hvm = guest_config->b_info.extra_hvm;
+
+    dm_config->disks = guest_config->disks;
+    dm_config->num_disks = guest_config->num_disks;
+
+    libxl__dm_vifs_from_hvm_guest_config(gc, guest_config, dm_config);
+
+    dm_config->c_info.run_hotplug_scripts =
+        guest_config->c_info.run_hotplug_scripts;
+
+    ret = libxl__domain_config_setdefault(gc, dm_config, guest_domid);
+    if (ret) goto out;
+
+    if (libxl_defbool_val(guest_config->b_info.u.hvm.vnc.enable)
+        || libxl_defbool_val(guest_config->b_info.u.hvm.spice.enable)
+        || libxl_defbool_val(guest_config->b_info.u.hvm.sdl.enable)) {
+        GCNEW(vfb);
+        GCNEW(vkb);
+        libxl__vfb_and_vkb_from_hvm_guest_config(gc, guest_config, vfb, vkb);
+        dm_config->vfbs = vfb;
+        dm_config->num_vfbs = 1;
+        dm_config->vkbs = vkb;
+        dm_config->num_vkbs = 1;
+    }
+
+    if (guest_config->b_info.stubdomain_kernel &&
+        access(guest_config->b_info.stubdomain_kernel, R_OK) != 0) {
+        LOGED(ERROR, guest_domid, "could not access stubdomain kernel %s",
+              guest_config->b_info.stubdomain_kernel);
+        ret = ERROR_INVAL;
+        goto out;
+    }
+
+    if (guest_config->b_info.stubdomain_ramdisk &&
+        access(guest_config->b_info.stubdomain_ramdisk, R_OK) != 0) {
+        LOGED(ERROR, guest_domid, "could not access stubdomain ramdisk %s",
+              guest_config->b_info.stubdomain_ramdisk);
+        ret = ERROR_INVAL;
+        goto out;
+    }
+
+    stubdom_state->pv_kernel.path = guest_config->b_info.stubdomain_kernel;
+    stubdom_state->pv_ramdisk.path = guest_config->b_info.stubdomain_ramdisk;
+
+    /* fixme: this function can leak the stubdom if it fails */
+    ret = libxl__domain_make(gc, dm_config, stubdom_state,
+                             &sdss->pvqemu.guest_domid, false);
+    if (ret)
+        goto out;
+    uint32_t dm_domid = sdss->pvqemu.guest_domid;
+    ret = libxl__domain_build(gc, dm_config, dm_domid, stubdom_state);
+    if (ret)
+        goto out;
+
+    ret = libxl__build_device_model_args(gc, "stubdom-dm", guest_domid,
+                                         guest_config, &args, NULL,
+                                         d_state, NULL);
+    if (ret) {
+        ret = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl__store_libxl_entry(gc, guest_domid, "dm-version",
+        libxl_device_model_version_to_string(dm_config->b_info.device_model_version));
+
+    libxl__write_stub_dmargs(gc, dm_domid, guest_domid, args,
+                             libxl__stubdomain_is_linux(&guest_config->b_info));
+    libxl__xs_printf(gc, XBT_NULL,
+                     GCSPRINTF("%s/image/device-model-domid",
+                               libxl__xs_get_dompath(gc, guest_domid)),
+                     "%d", dm_domid);
+    libxl__xs_printf(gc, XBT_NULL,
+                     GCSPRINTF("%s/target",
+                               libxl__xs_get_dompath(gc, dm_domid)),
+                     "%d", guest_domid);
+    if (guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+        /* qemu-xen is used as a dm in the stubdomain, so we set the bios
+         * accroding to this */
+        libxl__xs_printf(gc, XBT_NULL,
+                        libxl__sprintf(gc, "%s/hvmloader/bios",
+                                       libxl__xs_get_dompath(gc, guest_domid)),
+                        "%s",
+                        libxl_bios_type_to_string(guest_config->b_info.u.hvm.bios));
+    }
+    ret = xc_domain_set_target(ctx->xch, dm_domid, guest_domid);
+    if (ret<0) {
+        LOGED(ERROR, guest_domid, "setting target domain %d -> %d",
+              dm_domid, guest_domid);
+        ret = ERROR_FAIL;
+        goto out;
+    }
+    xs_set_target(ctx->xsh, dm_domid, guest_domid);
+
+    perm[0].id = dm_domid;
+    perm[0].perms = XS_PERM_NONE;
+    perm[1].id = guest_domid;
+    perm[1].perms = XS_PERM_READ;
+retry_transaction:
+    t = xs_transaction_start(ctx->xsh);
+    xs_mkdir(ctx->xsh, t, DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""));
+    xs_set_permissions(ctx->xsh, t,
+                       DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""),
+                       perm, ARRAY_SIZE(perm));
+    if (!xs_transaction_end(ctx->xsh, t, 0))
+        if (errno == EAGAIN)
+            goto retry_transaction;
+
+    libxl__multidev_begin(ao, &sdss->multidev);
+    sdss->multidev.callback = spawn_stub_launch_dm;
+    libxl__add_disks(egc, ao, dm_domid, dm_config, &sdss->multidev);
+    libxl__multidev_prepared(egc, &sdss->multidev, 0);
+
+    return;
+
+out:
+    assert(ret);
+    spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, ret);
+}
+
+static void spawn_stub_launch_dm(libxl__egc *egc,
+                                 libxl__multidev *multidev, int ret)
+{
+    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
+    STATE_AO_GC(sdss->dm.spawn.ao);
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int i, num_console = STUBDOM_SPECIAL_CONSOLES;
+    libxl__device_console *console;
+
+    /* convenience aliases */
+    libxl_domain_config *const dm_config = &sdss->dm_config;
+    libxl_domain_config *const guest_config = sdss->dm.guest_config;
+    const int guest_domid = sdss->dm.guest_domid;
+    libxl__domain_build_state *const d_state = sdss->dm.build_state;
+    libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
+    uint32_t dm_domid = sdss->pvqemu.guest_domid;
+    int need_qemu;
+
+    if (ret) {
+        LOGD(ERROR, guest_domid, "error connecting disk devices");
+        goto out;
+     }
+
+    for (i = 0; i < dm_config->num_nics; i++) {
+         /* We have to init the nic here, because we still haven't
+         * called libxl_device_nic_add at this point, but qemu needs
+         * the nic information to be complete.
+         */
+        ret = libxl__nic_devtype.set_default(gc, dm_domid, &dm_config->nics[i],
+                                             false);
+        if (ret)
+            goto out;
+    }
+    if (dm_config->num_vfbs) {
+        ret = libxl__device_add(gc, dm_domid, &libxl__vfb_devtype,
+                                &dm_config->vfbs[0]);
+        if (ret) goto out;
+    }
+    if (dm_config->num_vkbs) {
+        ret = libxl__device_add(gc, dm_domid, &libxl__vkb_devtype,
+                                &dm_config->vkbs[0]);
+        if (ret) goto out;
+    }
+
+    if (guest_config->b_info.u.hvm.serial) {
+        num_console++;
+    } else if (guest_config->b_info.u.hvm.serial_list) {
+        char **serial = guest_config->b_info.u.hvm.serial_list;
+        while (*(serial++))
+            num_console++;
+    }
+
+    console = libxl__calloc(gc, num_console, sizeof(libxl__device_console));
+
+    for (i = 0; i < num_console; i++) {
+        console[i].devid = i;
+        console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
+        /* STUBDOM_CONSOLE_LOGGING (console 0) is for minios logging
+         * STUBDOM_CONSOLE_SAVE (console 1) is for writing the save file
+         * STUBDOM_CONSOLE_RESTORE (console 2) is for reading the save file
+         */
+        switch (i) {
+            char *filename;
+            char *name;
+            case STUBDOM_CONSOLE_LOGGING:
+                name = GCSPRINTF("qemu-dm-%s",
+                                 libxl_domid_to_name(ctx, guest_domid));
+                ret = libxl_create_logfile(ctx, name, &filename);
+                if (ret) goto out;
+                console[i].output = GCSPRINTF("file:%s", filename);
+                free(filename);
+                /* will be changed back to LIBXL__CONSOLE_BACKEND_IOEMU if qemu
+                 * will be in use */
+                console[i].consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
+                break;
+            case STUBDOM_CONSOLE_SAVE:
+                console[i].output = GCSPRINTF("file:%s",
+                                libxl__device_model_savefile(gc, guest_domid));
+                break;
+            case STUBDOM_CONSOLE_RESTORE:
+                if (d_state->saved_state)
+                    console[i].output =
+                        GCSPRINTF("pipe:%s", d_state->saved_state);
+                break;
+            case STUBDOM_CONSOLE_SERIAL:
+                if (guest_config->b_info.u.hvm.serial) {
+                    console[i].output = guest_config->b_info.u.hvm.serial;
+                    break;
+                }
+                /* fall-through */
+            default:
+                /* Serial_list is set, as otherwise num_consoles would be
+                 * smaller and consoles 0-2 are handled above. */
+                assert(guest_config->b_info.u.hvm.serial_list);
+                console[i].output = guest_config->b_info.u.hvm.serial_list[
+                    i-STUBDOM_CONSOLE_SERIAL];
+                break;
+        }
+    }
+
+    /*
+     * Until xenconsoled learns how to handle multiple consoles, require qemu
+     * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
+     */
+    need_qemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
+
+    for (i = 0; i < num_console; i++) {
+        libxl__device device;
+        if (need_qemu)
+            console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
+        ret = libxl__device_console_add(gc, dm_domid, &console[i],
+                        i == STUBDOM_CONSOLE_LOGGING ? stubdom_state : NULL,
+                        &device);
+        if (ret)
+            goto out;
+    }
+
+    sdss->qmp_proxy_spawn.ao = ao;
+    if (libxl__stubdomain_is_linux(&guest_config->b_info)) {
+        spawn_qmp_proxy(egc, sdss);
+    } else {
+        qmp_proxy_spawn_outcome(egc, sdss, 0);
+    }
+
+    return;
+
+out:
+    assert(ret);
+    qmp_proxy_spawn_outcome(egc, sdss, ret);
+}
+
+static void spawn_qmp_proxy(libxl__egc *egc,
+                            libxl__stub_dm_spawn_state *sdss)
+{
+    STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
+    const uint32_t guest_domid = sdss->dm.guest_domid;
+    const uint32_t dm_domid = sdss->pvqemu.guest_domid;
+    const char *dom_path = libxl__xs_get_dompath(gc, dm_domid);
+    char **args;
+    int nr = 0;
+    int rc, logfile_w, null;
+
+    if (access(STUBDOM_QMP_PROXY_PATH, X_OK) < 0) {
+        LOGED(ERROR, guest_domid, "qmp proxy %s is not executable", STUBDOM_QMP_PROXY_PATH);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    sdss->qmp_proxy_spawn.what = GCSPRINTF("domain %d device model qmp proxy", guest_domid);
+    sdss->qmp_proxy_spawn.pidpath = GCSPRINTF("%s/image/qmp-proxy-pid", dom_path);
+    sdss->qmp_proxy_spawn.xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID,
+                                                        dm_domid, "/qmp-proxy-state");
+    sdss->qmp_proxy_spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+    sdss->qmp_proxy_spawn.midproc_cb = libxl__spawn_record_pid;
+    sdss->qmp_proxy_spawn.confirm_cb = qmp_proxy_confirm;
+    sdss->qmp_proxy_spawn.failure_cb = qmp_proxy_startup_failed;
+    sdss->qmp_proxy_spawn.detached_cb = qmp_proxy_detached;
+
+    const int arraysize = 6;
+    GCNEW_ARRAY(args, arraysize);
+    args[nr++] = STUBDOM_QMP_PROXY_PATH;
+    args[nr++] = GCSPRINTF("--state-path=%s", sdss->qmp_proxy_spawn.xspath);
+    args[nr++] = GCSPRINTF("%u", dm_domid);
+    args[nr++] = GCSPRINTF("%s/device-model/%u/qmp-vchan", dom_path, guest_domid);
+    args[nr++] = (char*)libxl__qemu_qmp_path(gc, guest_domid);
+    args[nr++] = NULL;
+    assert(nr == arraysize);
+
+    logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qmp-proxy-%s",
+                                                         sdss->dm_config.c_info.name));
+    if (logfile_w < 0) {
+        rc = logfile_w;
+        goto out;
+    }
+    null = open("/dev/null", O_RDWR);
+    if (null < 0) {
+        LOGED(ERROR, guest_domid, "unable to open /dev/null");
+        rc = ERROR_FAIL;
+        goto out_close;
+    }
+
+    rc = libxl__spawn_spawn(egc, &sdss->qmp_proxy_spawn);
+    if (rc < 0)
+        goto out_close;
+    if (!rc) { /* inner child */
+        setsid();
+        libxl__exec(gc, null, null, logfile_w, STUBDOM_QMP_PROXY_PATH, args, NULL);
+        /* unreachable */
+    }
+
+    rc = 0;
+
+out_close:
+    if (logfile_w >= 0)
+        close(logfile_w);
+    if (null >= 0)
+        close(null);
+out:
+    if (rc)
+        qmp_proxy_spawn_outcome(egc, sdss, rc);
+}
+
+static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+                              const char *xsdata)
+{
+    STATE_AO_GC(spawn->ao);
+
+    if (!xsdata)
+        return;
+
+    if (strcmp(xsdata, "running"))
+        return;
+
+    libxl__spawn_initiate_detach(gc, spawn);
+}
+
+static void qmp_proxy_startup_failed(libxl__egc *egc,
+                                     libxl__spawn_state *spawn,
+                                     int rc)
+{
+    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
+    qmp_proxy_spawn_outcome(egc, sdss, rc);
+}
+
+static void qmp_proxy_detached(libxl__egc *egc,
+                               libxl__spawn_state *spawn)
+{
+    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
+    qmp_proxy_spawn_outcome(egc, sdss, 0);
+}
+
+static void qmp_proxy_spawn_outcome(libxl__egc *egc,
+                                    libxl__stub_dm_spawn_state *sdss,
+                                    int rc)
+{
+    STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
+    /*
+     * Until xenconsoled learns how to handle multiple consoles, require qemu
+     * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
+     */
+    int need_pvqemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
+
+    if (rc) goto out;
+
+    if (need_pvqemu < 0) {
+        rc = need_pvqemu;
+        goto out;
+    }
+
+    sdss->pvqemu.spawn.ao = ao;
+    sdss->pvqemu.guest_config = &sdss->dm_config;
+    sdss->pvqemu.build_state = &sdss->dm_state;
+    sdss->pvqemu.callback = spawn_stubdom_pvqemu_cb;
+    if (need_pvqemu) {
+        libxl__spawn_local_dm(egc, &sdss->pvqemu);
+    } else {
+        /* If dom0 qemu not needed, do not launch it */
+        spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, 0);
+    }
+
+    return;
+
+out:
+    assert(rc);
+    spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, rc);
+}
+
+static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
+                                libxl__dm_spawn_state *stubdom_dmss,
+                                int rc)
+{
+    libxl__stub_dm_spawn_state *sdss =
+        CONTAINER_OF(stubdom_dmss, *sdss, pvqemu);
+    STATE_AO_GC(sdss->dm.spawn.ao);
+    uint32_t dm_domid = sdss->pvqemu.guest_domid;
+    libxl_domain_config *d_config = stubdom_dmss->guest_config;
+
+    if (rc) goto out;
+
+    if (d_config->num_nics > 0) {
+        libxl__multidev_begin(ao, &sdss->multidev);
+        sdss->multidev.callback = stubdom_pvqemu_cb;
+        libxl__add_nics(egc, ao, dm_domid, d_config, &sdss->multidev);
+        libxl__multidev_prepared(egc, &sdss->multidev, 0);
+        return;
+    }
+
+out:
+    stubdom_pvqemu_cb(egc, &sdss->multidev, rc);
+}
+
+static void stubdom_pvqemu_cb(libxl__egc *egc,
+                              libxl__multidev *multidev,
+                              int rc)
+{
+    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
+    STATE_AO_GC(sdss->dm.spawn.ao);
+    uint32_t dm_domid = sdss->pvqemu.guest_domid;
+
+    if (rc) {
+        LOGED(ERROR, sdss->dm.guest_domid,
+              "error connecting nics devices");
+        goto out;
+    }
+
+    sdss->pvqemu.dmrs.ao = ao;
+    sdss->pvqemu.dmrs.domid = dm_domid;
+    sdss->pvqemu.dmrs.callback = stubdom_pvqemu_unpaused;
+    libxl__domain_unpause(egc, &sdss->pvqemu.dmrs); /* must be last */
+    return;
+out:
+    stubdom_pvqemu_unpaused(egc, &sdss->pvqemu.dmrs, rc);
+}
+
+static void stubdom_pvqemu_unpaused(libxl__egc *egc,
+                                    libxl__dm_resume_state *dmrs,
+                                    int rc)
+{
+    libxl__stub_dm_spawn_state *sdss =
+        CONTAINER_OF(dmrs, *sdss, pvqemu.dmrs);
+    STATE_AO_GC(sdss->dm.spawn.ao);
+    uint32_t dm_domid = sdss->pvqemu.guest_domid;
+
+    if (rc) goto out;
+
+    sdss->xswait.ao = ao;
+    sdss->xswait.what = GCSPRINTF("Stubdom %u for %u startup",
+                                  dm_domid, sdss->dm.guest_domid);
+    sdss->xswait.path = DEVICE_MODEL_XS_PATH(gc, dm_domid, sdss->dm.guest_domid,
+                                             "/state");
+    sdss->xswait.timeout_ms = LIBXL_STUBDOM_START_TIMEOUT * 1000;
+    sdss->xswait.callback = stubdom_xswait_cb;
+    rc = libxl__xswait_start(gc, &sdss->xswait);
+    if (rc) goto out;
+
+    return;
+
+ out:
+    stubdom_xswait_cb(egc, &sdss->xswait, rc, NULL);
+}
+
+static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
+                              int rc, const char *p)
+{
+    EGC_GC;
+    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(xswait, *sdss, xswait);
+
+    if (rc) {
+        if (rc == ERROR_TIMEDOUT)
+            LOGD(ERROR, sdss->dm.guest_domid,
+                 "%s: startup timed out", xswait->what);
+        goto out;
+    }
+
+    if (!p) return;
+
+    if (strcmp(p, "running"))
+        return;
+ out:
+    libxl__domain_build_state_dispose(&sdss->dm_state);
+    libxl__xswait_stop(gc, xswait);
+    dmss_dispose(gc, &sdss->dm);
+    dmss_dispose(gc, &sdss->pvqemu);
+    sdss->callback(egc, &sdss->dm, rc);
+}
+
+/* callbacks passed to libxl__spawn_spawn */
+static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+                                 const char *xsdata);
+static void device_model_startup_failed(libxl__egc *egc,
+                                        libxl__spawn_state *spawn,
+                                        int rc);
+static void device_model_detached(libxl__egc *egc,
+                                  libxl__spawn_state *spawn);
+static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
+                                const libxl__json_object *response,
+                                int rc);
+
+/* our "next step" function, called from those callbacks and elsewhere */
+static void device_model_spawn_outcome(libxl__egc *egc,
+                                       libxl__dm_spawn_state *dmss,
+                                       int rc);
+static void device_model_postconfig_chardev(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void device_model_postconfig_vnc(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void devise_model_postconfig_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_model_postconfig_done(libxl__egc *egc,
+    libxl__dm_spawn_state *dmss, int rc);
+
+void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss)
+{
+    /* convenience aliases */
+    const int domid = dmss->guest_domid;
+    libxl__domain_build_state *const state = dmss->build_state;
+    libxl__spawn_state *const spawn = &dmss->spawn;
+
+    STATE_AO_GC(dmss->spawn.ao);
+
+    libxl_ctx *ctx = CTX;
+    libxl_domain_config *guest_config = dmss->guest_config;
+    const libxl_domain_create_info *c_info = &guest_config->c_info;
+    const libxl_domain_build_info *b_info = &guest_config->b_info;
+    const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
+    char *path;
+    int logfile_w, null;
+    int rc;
+    char **args, **arg, **envs;
+    xs_transaction_t t;
+    char *vm_path;
+    char **pass_stuff;
+    const char *dm;
+    int dm_state_fd = -1;
+
+    dmss_init(dmss);
+
+    if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+        abort();
+    }
+
+    dm = libxl__domain_device_model(gc, b_info);
+    if (!dm) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (access(dm, X_OK) < 0) {
+        LOGED(ERROR, domid, "device model %s is not executable", dm);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__domain_get_device_model_uid(gc, dmss);
+    if (rc)
+        goto out;
+
+    if (b_info->device_model_version
+            == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
+        libxl_defbool_val(b_info->dm_restrict)) {
+        /* If we have to use dm_restrict, QEMU needs to be new enough
+         * and will have the new interface where we can pre-open the
+         * QMP socket. */
+        rc = libxl__pre_open_qmp_socket(gc, domid, &state->dm_monitor_fd);
+        if (rc) goto out;
+    }
+
+    rc = libxl__build_device_model_args(gc, dm, domid, guest_config,
+                                          &args, &envs, state,
+                                          &dm_state_fd);
+    if (rc)
+        goto out;
+
+    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        path = xs_get_domain_path(ctx->xsh, domid);
+        libxl__xs_printf(gc, XBT_NULL,
+                         GCSPRINTF("%s/hvmloader/bios", path),
+                         "%s", libxl_bios_type_to_string(b_info->u.hvm.bios));
+        /* Disable relocating memory to make the MMIO hole larger
+         * unless we're running qemu-traditional and vNUMA is not
+         * configured. */
+        libxl__xs_printf(gc, XBT_NULL,
+                         GCSPRINTF("%s/hvmloader/allow-memory-relocate", path),
+                         "%d",
+                         b_info->device_model_version==LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL &&
+                         !libxl__vnuma_configured(b_info));
+        free(path);
+    }
+
+    path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
+    xs_mkdir(ctx->xsh, XBT_NULL, path);
+
+    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        b_info->device_model_version
+        == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL)
+        libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/disable_pf", path),
+                         "%d", !libxl_defbool_val(b_info->u.hvm.xen_platform_pci));
+
+    logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qemu-dm-%s",
+                                                         c_info->name));
+    if (logfile_w < 0) {
+        rc = logfile_w;
+        goto out;
+    }
+    null = open("/dev/null", O_RDONLY);
+    if (null < 0) {
+        LOGED(ERROR, domid, "unable to open /dev/null");
+        rc = ERROR_FAIL;
+        goto out_close;
+    }
+
+    const char *dom_path = libxl__xs_get_dompath(gc, domid);
+
+    /*
+     * If we're starting the dm with a non-root UID, save the UID so
+     * that we can reliably kill it and any subprocesses
+     */
+    if (state->dm_kill_uid)
+        libxl__xs_printf(gc, XBT_NULL,
+                         GCSPRINTF("%s/image/device-model-kill-uid", dom_path),
+                         "%s", state->dm_kill_uid);
+
+    if (vnc && vnc->passwd) {
+        /* This xenstore key will only be used by qemu-xen-traditionnal.
+         * The code to supply vncpasswd to qemu-xen is later. */
+retry_transaction:
+        /* Find uuid and the write the vnc password to xenstore for qemu. */
+        t = xs_transaction_start(ctx->xsh);
+        vm_path = libxl__xs_read(gc,t,GCSPRINTF("%s/vm", dom_path));
+        if (vm_path) {
+            /* Now write the vncpassword into it. */
+            pass_stuff = libxl__calloc(gc, 3, sizeof(char *));
+            pass_stuff[0] = "vncpasswd";
+            pass_stuff[1] = vnc->passwd;
+            libxl__xs_writev(gc,t,vm_path,pass_stuff);
+            if (!xs_transaction_end(ctx->xsh, t, 0))
+                if (errno == EAGAIN)
+                    goto retry_transaction;
+        }
+    }
+
+    LOGD(DEBUG, domid, "Spawning device-model %s with arguments:", dm);
+    for (arg = args; *arg; arg++)
+        LOGD(DEBUG, domid, "  %s", *arg);
+    if (*envs) {
+        LOGD(DEBUG, domid, "Spawning device-model %s with additional environment:", dm);
+        for (arg = envs; *arg; arg += 2)
+            LOGD(DEBUG, domid, "  %s=%s", arg[0], arg[1]);
+    }
+
+    spawn->what = GCSPRINTF("domain %d device model", domid);
+    spawn->xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid,
+                                         "/state");
+    spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+    spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path);
+    spawn->midproc_cb = libxl__spawn_record_pid;
+    spawn->confirm_cb = device_model_confirm;
+    spawn->failure_cb = device_model_startup_failed;
+    spawn->detached_cb = device_model_detached;
+
+    if (state->dm_monitor_fd >= 0) {
+        /* There is a valid QMP socket available now,
+         * use it to find out when QEMU is ready */
+        dmss->qmp.ao = ao;
+        dmss->qmp.callback = device_model_qmp_cb;
+        dmss->qmp.domid = domid;
+        dmss->qmp.payload_fd = -1;
+        rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-status", NULL);
+        if (rc) goto out_close;
+    }
+
+    rc = libxl__spawn_spawn(egc, spawn);
+    if (rc < 0)
+        goto out_close;
+    if (!rc) { /* inner child */
+        setsid();
+        if (libxl_defbool_val(b_info->dm_restrict)) {
+            rc = libxl__local_dm_preexec_restrict(gc);
+            if (rc)
+                _exit(-1);
+        }
+        libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
+    }
+
+    rc = 0;
+
+out_close:
+    if (null >= 0) close(null);
+    if (logfile_w >= 0) close(logfile_w);
+out:
+    if (dm_state_fd >= 0) close(dm_state_fd);
+    if (rc)
+        device_model_spawn_outcome(egc, dmss, rc);
+}
+
+bool libxl__query_qemu_backend(libxl__gc *gc, uint32_t domid,
+                               uint32_t backend_id, const char *type, bool def)
+{
+    char *path;
+    char **dir;
+    unsigned int n;
+
+    path = GCSPRINTF("%s/device-model/%u/backends",
+                     libxl__xs_get_dompath(gc, backend_id), domid);
+    dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
+    if (!dir)
+        return def;
+
+    path = GCSPRINTF("%s/device-model/%u/backends/%s",
+                     libxl__xs_get_dompath(gc, backend_id), domid, type);
+    dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
+
+    return !!dir;
+}
+
+static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
+                                 const char *xsdata)
+{
+    STATE_AO_GC(spawn->ao);
+
+    if (!xsdata)
+        return;
+
+    if (strcmp(xsdata, "running"))
+        return;
+
+    libxl__spawn_initiate_detach(gc, spawn);
+}
+
+static void device_model_startup_failed(libxl__egc *egc,
+                                        libxl__spawn_state *spawn,
+                                        int rc)
+{
+    libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
+    device_model_spawn_outcome(egc, dmss, rc);
+}
+
+static void device_model_detached(libxl__egc *egc,
+                                  libxl__spawn_state *spawn)
+{
+    libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
+    device_model_spawn_outcome(egc, dmss, 0);
+}
+
+static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
+                                const libxl__json_object *response,
+                                int rc)
+{
+    EGC_GC;
+    libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, qmp);
+    const libxl__json_object *o;
+    const char *status;
+    const char *expected_state;
+
+    libxl__ev_qmp_dispose(gc, ev);
+
+    if (rc)
+        goto failed;
+
+    o = libxl__json_map_get("status", response, JSON_STRING);
+    if (!o) {
+        LOGD(ERROR, ev->domid,
+             "Missing 'status' in response to 'query-status'");
+        LOGD(DEBUG, ev->domid, ".. instead, got: %s", JSON(response));
+        rc = ERROR_QEMU_API;
+        goto failed;
+    }
+    status = libxl__json_object_get_string(o);
+    if (!dmss->build_state->saved_state)
+        expected_state = "prelaunch";
+    else
+        expected_state = "paused";
+    if (strcmp(status, expected_state)) {
+        LOGD(ERROR, ev->domid, "Unexpected QEMU status: %s", status);
+        rc = ERROR_NOT_READY;
+        goto failed;
+    }
+
+    libxl__spawn_initiate_detach(gc, &dmss->spawn);
+    return;
+
+failed:
+    LOGD(ERROR, ev->domid, "QEMU did not start properly, rc=%d", rc);
+    libxl__spawn_initiate_failure(egc, &dmss->spawn, rc);
+}
+
+static void device_model_spawn_outcome(libxl__egc *egc,
+                                       libxl__dm_spawn_state *dmss,
+                                       int rc)
+{
+    STATE_AO_GC(dmss->spawn.ao);
+    int ret2;
+
+    /* Convenience aliases */
+    libxl_domain_config *const d_config = dmss->guest_config;
+
+    if (rc)
+        LOGD(ERROR, dmss->guest_domid,
+             "%s: spawn failed (rc=%d)", dmss->spawn.what, rc);
+
+    libxl__domain_build_state *state = dmss->build_state;
+
+    if (state->saved_state) {
+        ret2 = unlink(state->saved_state);
+        if (ret2) {
+            LOGED(ERROR, dmss->guest_domid, "%s: failed to remove device-model state %s",
+                 dmss->spawn.what, state->saved_state);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    /* Check if spawn failed */
+    if (rc) goto out;
+
+    if (d_config->b_info.device_model_version
+            == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+        rc = libxl__ev_time_register_rel(ao, &dmss->timeout,
+                                         devise_model_postconfig_timeout,
+                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
+        if (rc) goto out;
+        dmss->qmp.ao = ao;
+        dmss->qmp.domid = dmss->guest_domid;
+        dmss->qmp.payload_fd = -1;
+        dmss->qmp.callback = device_model_postconfig_chardev;
+        rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-chardev", NULL);
+        if (rc) goto out;
+        return;
+    }
+
+ out:
+    device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+static void device_model_postconfig_chardev(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
+    const libxl__json_object *item = NULL;
+    const libxl__json_object *o = NULL;
+    int i = 0;
+    const char *label;
+    const char *filename;
+    int port;
+    char *endptr;
+    const char *dompath;
+    const char serial[] = "serial";
+    const size_t seriall = sizeof(serial) - 1;
+    const char pty[] = "pty:";
+    const size_t ptyl = sizeof(pty) - 1;
+
+    if (rc) goto out;
+
+    /*
+     * query-chardev response:
+     * [{ 'label': 'str',
+     *    'filename': 'str',
+     *    'frontend-open': 'bool' }, ...]
+     */
+
+    for (i = 0; (item = libxl__json_array_get(response, i)); i++) {
+        o = libxl__json_map_get("label", item, JSON_STRING);
+        if (!o) goto protocol_error;
+        label = libxl__json_object_get_string(o);
+
+        /* Check if the "label" start with "serial". */
+        if (!label || strncmp(label, serial, seriall))
+            continue;
+        port = strtol(label + seriall, &endptr, 10);
+        if (*(label + seriall) == '\0' || *endptr != '\0') {
+            LOGD(ERROR, qmp->domid,
+                 "Invalid serial port number: %s", label);
+            rc = ERROR_QEMU_API;
+            goto out;
+        }
+
+        o = libxl__json_map_get("filename", item, JSON_STRING);
+        if (!o) goto protocol_error;
+        filename = libxl__json_object_get_string(o);
+
+        /* Check if filename start with "pty:" */
+        if (!filename || strncmp(filename, pty, ptyl))
+            continue;
+
+        dompath = libxl__xs_get_dompath(gc, qmp->domid);
+        if (!dompath) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        rc = libxl__xs_printf(gc, XBT_NULL,
+                              GCSPRINTF("%s/serial/%d/tty", dompath, port),
+                              "%s", filename + ptyl);
+        if (rc) goto out;
+    }
+
+    qmp->callback = device_model_postconfig_vnc;
+    rc = libxl__ev_qmp_send(egc, qmp, "query-vnc", NULL);
+    if (rc) goto out;
+    return;
+
+protocol_error:
+    rc = ERROR_QEMU_API;
+    LOGD(ERROR, qmp->domid,
+         "unexpected response to QMP cmd 'query-chardev', received:\n%s",
+         JSON(response));
+out:
+    device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+static void device_model_postconfig_vnc(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
+    const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
+    const libxl__json_object *o;
+    libxl__json_object *args = NULL;
+
+    if (rc) goto out;
+
+    /*
+     * query-vnc response:
+     * { 'enabled': 'bool', '*host': 'str', '*service': 'str' }
+     */
+
+    o = libxl__json_map_get("enabled", response, JSON_BOOL);
+    if (!o) goto protocol_error;
+    if (libxl__json_object_get_bool(o)) {
+        const char *addr, *port;
+        const char *dompath;
+
+        o = libxl__json_map_get("host", response, JSON_STRING);
+        if (!o) goto protocol_error;
+        addr = libxl__json_object_get_string(o);
+        o = libxl__json_map_get("service", response, JSON_STRING);
+        if (!o) goto protocol_error;
+        port = libxl__json_object_get_string(o);
+
+        dompath = libxl__xs_get_dompath(gc, qmp->domid);
+        if (!dompath) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        rc = libxl__xs_printf(gc, XBT_NULL,
+                              GCSPRINTF("%s/console/vnc-listen", dompath),
+                              "%s", addr);
+        if (rc) goto out;
+        rc = libxl__xs_printf(gc, XBT_NULL,
+                              GCSPRINTF("%s/console/vnc-port", dompath),
+                              "%s", port);
+        if (rc) goto out;
+    }
+
+    if (vnc && vnc->passwd && vnc->passwd[0]) {
+        qmp->callback = device_model_postconfig_vnc_passwd;
+        libxl__qmp_param_add_string(gc, &args, "password", vnc->passwd);
+        rc = libxl__ev_qmp_send(egc, qmp, "change-vnc-password", args);
+        if (rc) goto out;
+        return;
+    }
+
+    rc = 0;
+    goto out;
+
+protocol_error:
+    rc = ERROR_QEMU_API;
+    LOGD(ERROR, qmp->domid,
+         "unexpected response to QMP cmd 'query-vnc', received:\n%s",
+         JSON(response));
+out:
+    device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
+    const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
+    const char *dompath;
+
+    if (rc) goto out;
+
+    dompath = libxl__xs_get_dompath(gc, qmp->domid);
+    if (!dompath) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = libxl__xs_printf(gc, XBT_NULL,
+                          GCSPRINTF("%s/console/vnc-pass", dompath),
+                          "%s", vnc->passwd);
+
+out:
+    device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+void devise_model_postconfig_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                                     const struct timeval *requested_abs,
+                                     int rc)
+{
+    libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, timeout);
+    device_model_postconfig_done(egc, dmss, rc); /* must be last */
+}
+
+
+static void device_model_postconfig_done(libxl__egc *egc,
+                                         libxl__dm_spawn_state *dmss,
+                                         int rc)
+{
+    EGC_GC;
+
+    if (rc)
+        LOGD(ERROR, dmss->guest_domid,
+             "Post DM startup configs failed, rc=%d", rc);
+    dmss_dispose(gc, dmss);
+    dmss->callback(egc, dmss, rc);
+}
+
+void libxl__spawn_qdisk_backend(libxl__egc *egc, libxl__dm_spawn_state *dmss)
+{
+    STATE_AO_GC(dmss->spawn.ao);
+    flexarray_t *dm_args, *dm_envs;
+    char **args, **envs;
+    const char *dm;
+    int logfile_w, null = -1, rc;
+    uint32_t domid = dmss->guest_domid;
+
+    dmss_init(dmss);
+
+    /* Always use qemu-xen as device model */
+    dm = qemu_xen_path(gc);
+
+    dm_args = flexarray_make(gc, 15, 1);
+    dm_envs = flexarray_make(gc, 1, 1);
+
+    flexarray_vappend(dm_args, dm, "-xen-domid",
+                      GCSPRINTF("%d", domid), NULL);
+    flexarray_append(dm_args, "-xen-attach");
+    flexarray_vappend(dm_args, "-name",
+                      GCSPRINTF("domain-%u", domid), NULL);
+    flexarray_append(dm_args, "-nographic");
+    flexarray_vappend(dm_args, "-M", "xenpv", NULL);
+    flexarray_vappend(dm_args, "-monitor", "/dev/null", NULL);
+    flexarray_vappend(dm_args, "-serial", "/dev/null", NULL);
+    flexarray_vappend(dm_args, "-parallel", "/dev/null", NULL);
+    flexarray_append(dm_args, NULL);
+    args = (char **) flexarray_contents(dm_args);
+
+    libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
+    envs = (char **) flexarray_contents(dm_envs);
+
+    logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qdisk-%u", domid));
+    if (logfile_w < 0) {
+        rc = logfile_w;
+        goto out;
+    }
+    null = open("/dev/null", O_RDONLY);
+    if (null < 0) {
+       rc = ERROR_FAIL;
+       goto out;
+    }
+
+    dmss->guest_config = NULL;
+    /*
+     * Clearly specify Qemu not using a saved state, so
+     * device_model_spawn_outcome doesn't try to unlink it.
+     */
+    dmss->build_state = libxl__zalloc(gc, sizeof(*dmss->build_state));
+    dmss->build_state->saved_state = 0;
+
+    dmss->spawn.what = GCSPRINTF("domain %u Qdisk backend", domid);
+    dmss->spawn.xspath = GCSPRINTF("device-model/%u/state", domid);
+    dmss->spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+    /*
+     * We cannot save Qemu pid anywhere in the xenstore guest dir,
+     * because we will call this from unprivileged driver domains,
+     * so save it in the current domain libxl private dir.
+     */
+    dmss->spawn.pidpath = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
+    dmss->spawn.midproc_cb = libxl__spawn_record_pid;
+    dmss->spawn.confirm_cb = device_model_confirm;
+    dmss->spawn.failure_cb = device_model_startup_failed;
+    dmss->spawn.detached_cb = device_model_detached;
+    rc = libxl__spawn_spawn(egc, &dmss->spawn);
+    if (rc < 0)
+        goto out;
+    if (!rc) { /* inner child */
+        setsid();
+        libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
+    }
+
+    rc = 0;
+out:
+    dmss_dispose(gc, dmss);
+    if (logfile_w >= 0) close(logfile_w);
+    if (null >= 0) close(null);
+    /* callback on error only, success goes via dmss->spawn.*_cb */
+    if (rc) dmss->callback(egc, dmss, rc);
+    return;
+}
+
+/* Generic function to signal a Qemu instance to exit */
+static int kill_device_model(libxl__gc *gc, const char *xs_path_pid)
+{
+    return libxl__kill_xs_path(gc, xs_path_pid, "Device Model");
+}
+
+/* Helper to destroy a Qdisk backend */
+int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid)
+{
+    char *pid_path;
+    int rc;
+
+    pid_path = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
+
+    rc = kill_device_model(gc, pid_path);
+    if (rc)
+        goto out;
+
+    libxl__xs_rm_checked(gc, XBT_NULL, pid_path);
+    libxl__xs_rm_checked(gc, XBT_NULL,
+                         GCSPRINTF("device-model/%u", domid));
+
+out:
+    return rc;
+}
+
+/* Asynchronous device model destroy functions */
+
+static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
+                                       const char *dm_kill_uid_str);
+
+static void kill_device_model_uid_cb(libxl__egc *egc,
+                                     libxl__ev_child *destroyer,
+                                     pid_t pid, int status);
+
+/*
+ * If we have a uid, we shouldn't kill by pid.  This is because a
+ * hostile QEMU might have exited, in which case the pid we have may
+ * be that of another process.
+ *
+ * The running devicemodel has permission over a specific domain id;
+ * this means that ideally we wouldn't the domain in question (freeing
+ * up the domain id for reuse) until we're confident that we've killed
+ * the domain.
+ *
+ * In general, destroy as much as we can; but return an error if there
+ * are any errors, so that the domain destroy will be aborted, and the
+ * domain itself will remain, giving the admin an opportunity to fix
+ * any issues and re-try the domain destroy.
+ */
+void libxl__destroy_device_model(libxl__egc *egc,
+                                 libxl__destroy_devicemodel_state *ddms)
+{
+    STATE_AO_GC(ddms->ao);
+    int rc;
+    int domid = ddms->domid;
+    char *path;
+    const char *dm_kill_uid_str = NULL;
+    int reaper_pid;
+
+    ddms->rc = 0;
+
+    path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
+    rc = libxl__xs_rm_checked(gc, XBT_NULL, path);
+    if (rc) {
+        ACCUMULATE_RC(ddms->rc);
+        LOGD(ERROR, domid, "xs_rm failed for %s", path);
+    }
+
+    /*
+     * See if we should try to kill by uid
+     */
+    path = GCSPRINTF("/local/domain/%d/image/device-model-kill-uid", domid);
+    rc = libxl__xs_read_checked(gc, XBT_NULL, path, &dm_kill_uid_str);
+
+    /*
+     * If there was an error here, accumulate the error and fall back
+     * to killing by pid.
+     */
+    if (rc) {
+        /* 
+         * Technically the state of the string passed to libxl__xs_read_checked() is
+         * "undefined" in the case rc == 0 (according to libxl_internal.h).  Set it to
+         * NULL to prevent undefined behavior.
+         */
+        dm_kill_uid_str = NULL;
+        ACCUMULATE_RC(ddms->rc);
+        LOGD(ERROR, domid, "Reading dm UID path failed for %s", path);
+    }
+
+    /* The DM has its own uid; Attempt to kill all processes with that UID */
+    if (dm_kill_uid_str) {
+        LOGD(DEBUG, domid, "Found DM uid %s, destroying by uid",
+             dm_kill_uid_str);
+
+        reaper_pid = libxl__ev_child_fork(gc, &ddms->destroyer,
+                                          kill_device_model_uid_cb);
+        if (reaper_pid < 0) {
+            rc = ERROR_FAIL;
+            ACCUMULATE_RC(ddms->rc);
+            /*
+             * Note that if this fails, we still don't kill by pid, to
+             * make sure that an untrusted DM has not "maliciously"
+             * exited (potentially causing us to kill an unrelated
+             * process which happened to get the same pid).
+             */
+            goto out;
+        }
+
+        if (!reaper_pid) {  /* child */
+            rc = kill_device_model_uid_child(ddms, dm_kill_uid_str);
+            assert(rc <= 0 && rc >= -125);
+            _exit(-rc);
+        }
+
+        /*
+         * Parent of successful fork; execution will pick up in
+         * kill_device_model_uid_cb when child exits
+         */
+        return;
+    }
+
+    /*
+     * No uid to kill; attept to kill by pid.
+     */
+    LOGD(DEBUG, domid, "Didn't find dm UID; destroying by pid");
+
+    path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
+    rc = kill_device_model(gc, path);
+
+    if (rc) {
+        ACCUMULATE_RC(ddms->rc);
+        LOGD(ERROR, domid, "Killing device model pid from path %s", path);
+    }
+
+out:
+    /*
+     * NB that we always pass '0' here for the "status of exited
+     * process"; since there is no process, it always "succeeds".
+     * Errors are accumulated in ddms->rc and will be handled
+     * correctly.
+     */
+    kill_device_model_uid_cb(egc, &ddms->destroyer, -1, 0);
+    return;
+}
+
+/* 
+ * Note that this attempts to grab a file lock, so must be called from
+ * a sub-process.
+ */
+static int get_reaper_lock_and_uid(libxl__destroy_devicemodel_state *ddms,
+                                   uid_t *reaper_uid)
+{
+    STATE_AO_GC(ddms->ao);
+    int domid = ddms->domid;
+    int r;
+    const char * lockfile;
+    int fd; /* "leaked" into the general process context (even on failure) */
+
+    /* Try to lock the "reaper uid" */
+    lockfile = GCSPRINTF("%s/dm-reaper-lock", libxl__run_dir_path());
+
+    /*
+     * NB that since we've just forked, we can't have any
+     * threads; so we don't need the libxl__carefd
+     * infrastructure here.
+     */
+    fd = open(lockfile, O_RDWR|O_CREAT, 0644);
+    if (fd < 0) {
+        LOGED(ERROR, domid,
+              "unexpected error while trying to open lockfile %s, errno=%d",
+              lockfile, errno);
+        return ERROR_FAIL;
+    }
+
+    /* Try to lock the file, retrying on EINTR */
+    for (;;) {
+        r = flock(fd, LOCK_EX);
+        if (!r)
+            break;
+        if (errno != EINTR) {
+            /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
+            LOGED(ERROR, domid,
+                  "unexpected error while trying to lock %s, fd=%d, errno=%d",
+                  lockfile, fd, errno);
+            return ERROR_FAIL;
+        }
+    }
+
+    /*
+     * Get reaper_uid.  If we can't find such a uid, return an error.
+     */
+    return libxl__get_reaper_uid(gc, reaper_uid);
+}
+
+
+/*
+ * Destroy all processes of the given uid by setresuid to the
+ * specified uid and kill(-1).  NB this MUST BE CALLED FROM A SEPARATE
+ * PROCESS from the normal libxl process, and should exit immediately
+ * after return.  Returns a libxl-style error code that is guaranteed
+ * to be >= -125.
+ */
+static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
+                                       const char *dm_kill_uid_str) {
+    STATE_AO_GC(ddms->ao);
+    int domid = ddms->domid;
+    int r, rc = 0;
+    uid_t dm_kill_uid = atoi(dm_kill_uid_str);
+    uid_t reaper_uid;
+
+    /*
+     * Try to kill the devicemodel by uid.  The safest way to do this
+     * is to set euid == dm_uid, but the ruid to something else.  If
+     * we can't get a separate ruid, carry on trying to kill the
+     * process anyway using dm_uid for the ruid.  This is racy (the dm
+     * may be able to kill(-1) us before we kill them), but worth
+     * trying.
+     *
+     * NB: Even if we don't have a separate reaper_uid, the parent can
+     * know whether we won the race by looking at the status variable;
+     * so we don't strictly need to return failure in this case.  But
+     * if there's a misconfiguration, it's better to alert the
+     * administator sooner rather than later; so if we fail to get a
+     * reaper uid, report an error even if the kill succeeds.
+     */
+    rc = get_reaper_lock_and_uid(ddms, &reaper_uid);
+    if (rc) {
+        reaper_uid = dm_kill_uid;
+        LOGD(WARN, domid, "Couldn't get separate reaper uid;"
+            "carrying on with unsafe kill");
+    }
+
+    /*
+     * Should never happen; but if it does, better to have the
+     * toolstack crash with an error than nuking dom0.
+      */
+    assert(reaper_uid);
+    assert(dm_kill_uid);
+
+    LOGD(DEBUG, domid, "DM reaper: calling setresuid(%d, %d, 0)",
+         reaper_uid, dm_kill_uid);
+    r = setresuid(reaper_uid, dm_kill_uid, 0);
+    if (r) {
+        LOGED(ERROR, domid, "setresuid to (%d, %d, 0)",
+              reaper_uid, dm_kill_uid);
+        rc = rc ?: ERROR_FAIL;
+        goto out;
+    }
+
+    /*
+     * And kill everyone but me.
+     *
+     * NB that it's not clear from either POSIX or the Linux man page
+     * that ESRCH would be returned with a pid value of -1, but it
+     * doesn't hurt to check.
+     */
+    r = kill(-1, 9);
+    if (r && errno != ESRCH) {
+        LOGED(ERROR, domid, "kill(-1,9)");
+        rc = rc ?: ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static void kill_device_model_uid_cb(libxl__egc *egc,
+                                    libxl__ev_child *destroyer,
+                                    pid_t pid, int status)
+{
+    libxl__destroy_devicemodel_state *ddms = CONTAINER_OF(destroyer, *ddms, destroyer);
+    STATE_AO_GC(ddms->ao);
+
+    if (status) {
+        int rc = ERROR_FAIL;
+
+        if (WIFEXITED(status) && WEXITSTATUS(status) <= 125)
+            rc = -WEXITSTATUS(status);
+
+        ACCUMULATE_RC(ddms->rc);
+        libxl_report_child_exitstatus(CTX, XTL_ERROR,
+                                      "async domain destroy", pid, status);
+    }
+
+    /* Always try to clean up qmp, even if something went wrong */
+    libxl__qmp_cleanup(gc, ddms->domid);
+
+    ddms->callback(egc, ddms, ddms->rc);
+}
+
+/* Return 0 if no dm needed, 1 if needed and <0 if error. */
+int libxl__need_xenpv_qemu(libxl__gc *gc, libxl_domain_config *d_config)
+{
+    int idx, i, ret, num;
+    uint32_t domid;
+    const libxl__device_type *dt;
+
+    ret = libxl__get_domid(gc, &domid);
+    if (ret) {
+        LOG(ERROR, "unable to get domain id");
+        goto out;
+    }
+
+    if (d_config->num_vfbs > 0 || d_config->num_p9s > 0) {
+        ret = 1;
+        goto out;
+    }
+
+    for (idx = 0;; idx++) {
+        dt = device_type_tbl[idx];
+        if (!dt)
+            break;
+
+        num = *libxl__device_type_get_num(dt, d_config);
+        if (!dt->dm_needed || !num)
+            continue;
+
+        for (i = 0; i < num; i++) {
+            if (dt->dm_needed(libxl__device_type_get_elem(dt, d_config, i),
+                              domid)) {
+                ret = 1;
+                goto out;
+            }
+        }
+    }
+
+    for (i = 0; i < d_config->num_channels; i++) {
+        if (d_config->channels[i].backend_domid == domid) {
+            /* xenconsoled is limited to the first console only.
+               Until this restriction is removed we must use qemu for
+               secondary consoles which includes all channels. */
+            ret = 1;
+            goto out;
+        }
+    }
+
+out:
+    return ret;
+}
+
+int libxl__dm_active(libxl__gc *gc, uint32_t domid)
+{
+    char *pid, *dm_domid, *path;
+
+    path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
+    pid = libxl__xs_read(gc, XBT_NULL, path);
+
+    if (pid)
+        return true;
+
+    path = GCSPRINTF("/local/domain/%d/image/device-model-domid", domid);
+    dm_domid = libxl__xs_read(gc, XBT_NULL, path);
+
+    return dm_domid != NULL;
+}
+
+int libxl__dm_check_start(libxl__gc *gc, libxl_domain_config *d_config,
+                          uint32_t domid)
+{
+    int rc;
+
+    if (libxl__dm_active(gc, domid))
+        return 0;
+
+    rc = libxl__need_xenpv_qemu(gc, d_config);
+    if (rc < 0)
+        goto out;
+
+    if (!rc)
+        return 0;
+
+    LOGD(ERROR, domid, "device model required but not running");
+    rc = ERROR_FAIL;
+
+out:
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_dom.c b/tools/libs/light/libxl_dom.c
new file mode 100644 (file)
index 0000000..597a682
--- /dev/null
@@ -0,0 +1,1469 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <glob.h>
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+#include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/hvm_xs_strings.h>
+#include <xen/hvm/e820.h>
+
+#include "_paths.h"
+
+//#define DEBUG 1
+
+libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    xc_domaininfo_t info;
+    int ret;
+
+    ret = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
+    if (ret != 1 || info.domain != domid) {
+        LOG(ERROR, "unable to get domain type for domid=%"PRIu32, domid);
+        return LIBXL_DOMAIN_TYPE_INVALID;
+    }
+    if (info.flags & XEN_DOMINF_hvm_guest) {
+        const char *type_path = GCSPRINTF("%s/type",
+                                          libxl__xs_libxl_path(gc, domid));
+        const char *type;
+        libxl_domain_type t;
+        int rc;
+
+        rc = libxl__xs_read_mandatory(gc, XBT_NULL, type_path, &type);
+        if (rc) {
+            LOG(WARN,
+            "unable to get domain type for domid=%"PRIu32", assuming HVM",
+                domid);
+            return LIBXL_DOMAIN_TYPE_HVM;
+        }
+
+        rc = libxl_domain_type_from_string(type, &t);
+        if (rc) {
+            LOG(WARN,
+            "unable to get domain type for domid=%"PRIu32", assuming HVM",
+                domid);
+            return LIBXL_DOMAIN_TYPE_HVM;
+        }
+
+        return t;
+    } else
+        return LIBXL_DOMAIN_TYPE_PV;
+}
+
+int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid)
+{
+    xc_domaininfo_t info;
+    int ret;
+
+    ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
+    if (ret != 1)
+    {
+        LOGE(ERROR, "getinfolist failed %d", ret);
+        return ERROR_FAIL;
+    }
+    if (info.domain != domid)
+    {
+        LOGE(ERROR, "got info for dom%d, wanted dom%d\n", info.domain, domid);
+        return ERROR_FAIL;
+    }
+    return info.cpupool;
+}
+
+libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid)
+{
+    int cpupool = libxl__domain_cpupool(gc, domid);
+    libxl_cpupoolinfo poolinfo;
+    libxl_scheduler sched = LIBXL_SCHEDULER_UNKNOWN;
+    int rc;
+
+    if (cpupool < 0)
+        return sched;
+
+    libxl_cpupoolinfo_init(&poolinfo);
+    rc = libxl_cpupool_info(CTX, &poolinfo, cpupool);
+    if (rc < 0)
+        goto out;
+
+    sched = poolinfo.sched;
+
+out:
+    libxl_cpupoolinfo_dispose(&poolinfo);
+    return sched;
+}
+
+/*
+ * Two NUMA placement candidates are compared by means of the following
+ * heuristics:
+
+ *  - the number of vcpus runnable on the candidates is considered, and
+ *    candidates with fewer of them are preferred. If two candidate have
+ *    the same number of runnable vcpus,
+ *  - the amount of free memory in the candidates is considered, and the
+ *    candidate with greater amount of it is preferred.
+ *
+ * In fact, leaving larger memory holes, maximizes the probability of being
+ * able to put other domains on the node. That hopefully means many domains
+ * will benefit from local memory accesses, but also introduces the risk of
+ * overloading large (from a memory POV) nodes. That's right the effect
+ * that counting the vcpus able to run on the nodes tries to prevent.
+ *
+ * Note that this completely ignore the number of nodes each candidate span,
+ * as the fact that fewer nodes is better is already accounted for in the
+ * algorithm.
+ */
+static int numa_cmpf(const libxl__numa_candidate *c1,
+                     const libxl__numa_candidate *c2)
+{
+    if (c1->nr_vcpus != c2->nr_vcpus)
+        return c1->nr_vcpus - c2->nr_vcpus;
+
+    return c2->free_memkb - c1->free_memkb;
+}
+
+/* The actual automatic NUMA placement routine */
+static int numa_place_domain(libxl__gc *gc, uint32_t domid,
+                             libxl_domain_config *d_config)
+{
+    libxl_domain_build_info *info = &d_config->b_info;
+    int found;
+    libxl__numa_candidate candidate;
+    libxl_bitmap cpumap, cpupool_nodemap, *map;
+    libxl_cpupoolinfo cpupool_info;
+    int i, cpupool, rc = 0;
+    uint64_t memkb;
+
+    libxl__numa_candidate_init(&candidate);
+    libxl_bitmap_init(&cpumap);
+    libxl_bitmap_init(&cpupool_nodemap);
+    libxl_cpupoolinfo_init(&cpupool_info);
+
+    /*
+     * Extract the cpumap from the cpupool the domain belong to. In fact,
+     * it only makes sense to consider the cpus/nodes that are in there
+     * for placement.
+     */
+    rc = cpupool = libxl__domain_cpupool(gc, domid);
+    if (rc < 0)
+        goto out;
+    rc = libxl_cpupool_info(CTX, &cpupool_info, cpupool);
+    if (rc)
+        goto out;
+    map = &cpupool_info.cpumap;
+
+    /*
+     * If there's a well defined hard affinity mask (i.e., the same one for all
+     * the vcpus), we can try to run the placement considering only the pcpus
+     * within such mask.
+     */
+    if (info->num_vcpu_hard_affinity)
+    {
+#ifdef DEBUG
+        int j;
+
+        for (j = 0; j < info->num_vcpu_hard_affinity; j++)
+            assert(libxl_bitmap_equal(&info->vcpu_hard_affinity[0],
+                                      &info->vcpu_hard_affinity[j], 0));
+#endif /* DEBUG */
+
+        rc = libxl_bitmap_and(CTX, &cpumap, &info->vcpu_hard_affinity[0],
+                              &cpupool_info.cpumap);
+        if (rc)
+            goto out;
+
+        /* Hard affinity must contain at least one cpu of our cpupool */
+        if (libxl_bitmap_is_empty(&cpumap)) {
+            LOG(ERROR, "Hard affinity completely outside of domain's cpupool!");
+            rc = ERROR_INVAL;
+            goto out;
+        }
+    }
+
+    rc = libxl__domain_need_memory_calculate(gc, info, &memkb);
+    if (rc)
+        goto out;
+    if (libxl_node_bitmap_alloc(CTX, &cpupool_nodemap, 0)) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* Find the best candidate with enough free memory and at least
+     * as much pcpus as the domain has vcpus.  */
+    rc = libxl__get_numa_candidate(gc, memkb, info->max_vcpus,
+                                   0, 0, map, numa_cmpf, &candidate, &found);
+    if (rc)
+        goto out;
+
+    /* Not even a suitable placement candidate! Let's just don't touch the
+     * domain's info->cpumap. It will have affinity with all nodes/cpus. */
+    if (found == 0)
+        goto out;
+
+    /* Map the candidate's node map to the domain's info->nodemap */
+    libxl__numa_candidate_get_nodemap(gc, &candidate, &info->nodemap);
+
+    /* Avoid trying to set the affinity to nodes that might be in the
+     * candidate's nodemap but out of our cpupool. */
+    rc = libxl_cpumap_to_nodemap(CTX, &cpupool_info.cpumap,
+                                 &cpupool_nodemap);
+    if (rc)
+        goto out;
+
+    libxl_for_each_set_bit(i, info->nodemap) {
+        if (!libxl_bitmap_test(&cpupool_nodemap, i))
+            libxl_bitmap_reset(&info->nodemap, i);
+    }
+
+    LOG(DETAIL, "NUMA placement candidate with %d nodes, %d cpus and "
+                "%"PRIu64" KB free selected", candidate.nr_nodes,
+                candidate.nr_cpus, candidate.free_memkb / 1024);
+
+ out:
+    libxl__numa_candidate_dispose(&candidate);
+    libxl_bitmap_dispose(&cpupool_nodemap);
+    libxl_bitmap_dispose(&cpumap);
+    libxl_cpupoolinfo_dispose(&cpupool_info);
+    return rc;
+}
+
+int libxl__build_pre(libxl__gc *gc, uint32_t domid,
+              libxl_domain_config *d_config, libxl__domain_build_state *state)
+{
+    libxl_domain_build_info *const info = &d_config->b_info;
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *xs_domid, *con_domid;
+    int rc;
+    uint64_t size;
+
+    if (xc_domain_max_vcpus(ctx->xch, domid, info->max_vcpus) != 0) {
+        LOG(ERROR, "Couldn't set max vcpu count");
+        return ERROR_FAIL;
+    }
+
+    /*
+     * Check if the domain has any CPU or node affinity already. If not, try
+     * to build up the latter via automatic NUMA placement. In fact, in case
+     * numa_place_domain() manage to find a placement, in info->nodemap is
+     * updated accordingly; if it does not manage, info->nodemap is just left
+     * alone. It is then the the subsequent call to
+     * libxl_domain_set_nodeaffinity() that enacts the actual placement.
+     *
+     * As far as scheduling is concerned, we achieve NUMA-aware scheduling
+     * by having the results of placement affect the soft affinity of all
+     * the vcpus of the domain. Of course, we want that iff placement is
+     * enabled and actually happens, so we only change info->cpumap_soft to
+     * reflect the placement result if that is the case
+     */
+    if (libxl_defbool_val(info->numa_placement)) {
+        if (info->cpumap.size || info->num_vcpu_soft_affinity)
+            LOG(WARN, "Can't run NUMA placement, as a soft "
+                      "affinity has been specified explicitly");
+        else if (info->nodemap.size)
+            LOG(WARN, "Can't run NUMA placement, as the domain has "
+                      "NUMA node affinity set already");
+        else {
+            libxl_bitmap cpumap_soft;
+
+            rc = libxl_node_bitmap_alloc(ctx, &info->nodemap, 0);
+            if (rc)
+                return rc;
+            libxl_bitmap_set_any(&info->nodemap);
+
+            rc = libxl_cpu_bitmap_alloc(ctx, &cpumap_soft, 0);
+            if (rc)
+                return rc;
+
+            rc = numa_place_domain(gc, domid, d_config);
+            if (rc) {
+                libxl_bitmap_dispose(&cpumap_soft);
+                return rc;
+            }
+
+            /*
+             * All we need to do now is converting the result of automatic
+             * placement from nodemap to cpumap, and then use such cpumap
+             * as the soft affinity for all the vcpus of the domain.
+             *
+             * When calling libxl_set_vcpuaffinity_all(), it is ok to use
+             * NULL as hard affinity, as we know we don't have one, or we
+             * won't be here.
+             */
+            libxl_nodemap_to_cpumap(ctx, &info->nodemap, &cpumap_soft);
+            libxl_set_vcpuaffinity_all(ctx, domid, info->max_vcpus,
+                                       NULL, &cpumap_soft);
+
+            libxl_bitmap_dispose(&cpumap_soft);
+
+            /*
+             * Placement has run, so avoid for it to be re-run, if this
+             * same config we are using and building here is ever re-used.
+             * This means that people re-using configs will get the same
+             * results, consistently, across every re-use, which is what
+             * we expect most people to want.
+             */
+            libxl_defbool_set(&info->numa_placement, false);
+        }
+    }
+
+    if (info->nodemap.size)
+        libxl_domain_set_nodeaffinity(ctx, domid, &info->nodemap);
+
+    if (info->num_vcpu_hard_affinity || info->num_vcpu_soft_affinity) {
+        libxl_bitmap *hard_affinity, *soft_affinity;
+        int i, n_vcpus;
+
+        n_vcpus = info->num_vcpu_hard_affinity > info->num_vcpu_soft_affinity ?
+            info->num_vcpu_hard_affinity : info->num_vcpu_soft_affinity;
+
+        for (i = 0; i < n_vcpus; i++) {
+            /*
+             * Prepare hard and soft affinity pointers in a way that allows
+             * us to issue only one call to libxl_set_vcpuaffinity(), setting,
+             * for each vcpu, both hard and soft affinity "atomically".
+             */
+            hard_affinity = NULL;
+            if (info->num_vcpu_hard_affinity &&
+                i < info->num_vcpu_hard_affinity)
+                hard_affinity = &info->vcpu_hard_affinity[i];
+
+            soft_affinity = NULL;
+            if (info->num_vcpu_soft_affinity &&
+                i < info->num_vcpu_soft_affinity)
+                soft_affinity = &info->vcpu_soft_affinity[i];
+
+            if (libxl_set_vcpuaffinity(ctx, domid, i,
+                                       hard_affinity, soft_affinity)) {
+                LOG(ERROR, "setting affinity failed on vcpu `%d'", i);
+                return ERROR_FAIL;
+            }
+        }
+    }
+
+
+    rc = libxl__arch_extra_memory(gc, info, &size);
+    if (rc < 0) {
+        LOGE(ERROR, "Couldn't get arch extra constant memory size");
+        return ERROR_FAIL;
+    }
+
+    if (xc_domain_setmaxmem(ctx->xch, domid, info->target_memkb + size) < 0) {
+        LOGE(ERROR, "Couldn't set max memory");
+        return ERROR_FAIL;
+    }
+
+    xs_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenstored/domid", NULL);
+    state->store_domid = xs_domid ? atoi(xs_domid) : 0;
+    free(xs_domid);
+
+    con_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenconsoled/domid", NULL);
+    state->console_domid = con_domid ? atoi(con_domid) : 0;
+    free(con_domid);
+
+    state->store_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->store_domid);
+    state->console_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->console_domid);
+
+    rc = libxl__arch_domain_create(gc, d_config, domid);
+
+    /* Construct a CPUID policy, but only for brand new domains.  Domains
+     * being migrated-in/restored have CPUID handled during the
+     * static_data_done() callback. */
+    if (!state->restore)
+        libxl__cpuid_legacy(ctx, domid, false, info);
+
+    return rc;
+}
+
+static int set_vnuma_affinity(libxl__gc *gc, uint32_t domid,
+                              libxl_domain_build_info *info)
+{
+    libxl_bitmap cpumap;
+    libxl_vnode_info *v;
+    unsigned int i, j;
+    int rc = 0;
+
+    libxl_bitmap_init(&cpumap);
+
+    rc = libxl_cpu_bitmap_alloc(CTX, &cpumap, 0);
+    if (rc) {
+        LOG(ERROR, "Can't allocate nodemap");
+        goto out;
+    }
+
+    /*
+     * For each vcpu in each vnode, set its soft affinity to
+     * the pcpus belonging to the pnode the vnode is on
+     */
+    for (i = 0; i < info->num_vnuma_nodes; i++) {
+        v = &info->vnuma_nodes[i];
+
+        rc = libxl_node_to_cpumap(CTX, v->pnode, &cpumap);
+        if (rc) {
+            LOG(ERROR, "Can't get cpumap for vnode %d", i);
+            goto out;
+        }
+
+        libxl_for_each_set_bit(j, v->vcpus) {
+            rc = libxl_set_vcpuaffinity(CTX, domid, j, NULL, &cpumap);
+            if (rc) {
+                LOG(ERROR, "Can't set cpu affinity for %d", j);
+                goto out;
+            }
+        }
+    }
+
+out:
+    libxl_bitmap_dispose(&cpumap);
+    return rc;
+}
+
+int libxl__build_post(libxl__gc *gc, uint32_t domid,
+                      libxl_domain_build_info *info,
+                      libxl__domain_build_state *state,
+                      char **vms_ents, char **local_ents)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *dom_path, *vm_path;
+    xs_transaction_t t;
+    char **ents;
+    int i, rc;
+
+    if (info->num_vnuma_nodes && !info->num_vcpu_soft_affinity) {
+        rc = set_vnuma_affinity(gc, domid, info);
+        if (rc)
+            return rc;
+    }
+
+    rc = libxl_domain_sched_params_set(CTX, domid, &info->sched_params);
+    if (rc)
+        return rc;
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM
+        && !libxl_ms_vm_genid_is_zero(&info->u.hvm.ms_vm_genid)) {
+        rc = libxl__ms_vm_genid_set(gc, domid,
+                                    &info->u.hvm.ms_vm_genid);
+        if (rc) {
+            LOG(ERROR, "Failed to set VM Generation ID");
+            return rc;
+        }
+    }
+
+    ents = libxl__calloc(gc, 12 + (info->max_vcpus * 2) + 2, sizeof(char *));
+    ents[0] = "memory/static-max";
+    ents[1] = GCSPRINTF("%"PRId64, info->max_memkb);
+    ents[2] = "memory/target";
+    ents[3] = GCSPRINTF("%"PRId64, info->target_memkb -
+                        libxl__get_targetmem_fudge(gc, info));
+    ents[4] = "memory/videoram";
+    ents[5] = GCSPRINTF("%"PRId64, info->video_memkb);
+    ents[6] = "domid";
+    ents[7] = GCSPRINTF("%d", domid);
+    ents[8] = "store/port";
+    ents[9] = GCSPRINTF("%"PRIu32, state->store_port);
+    ents[10] = "store/ring-ref";
+    ents[11] = GCSPRINTF("%lu", state->store_mfn);
+    for (i = 0; i < info->max_vcpus; i++) {
+        ents[12+(i*2)]   = GCSPRINTF("cpu/%d/availability", i);
+        ents[12+(i*2)+1] = libxl_bitmap_test(&info->avail_vcpus, i)
+                            ? "online" : "offline";
+    }
+
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path) {
+        return ERROR_FAIL;
+    }
+
+    vm_path = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/vm", dom_path), NULL);
+retry_transaction:
+    t = xs_transaction_start(ctx->xsh);
+
+    libxl__xs_writev(gc, t, dom_path, ents);
+    libxl__xs_writev(gc, t, dom_path, local_ents);
+    libxl__xs_writev(gc, t, vm_path, vms_ents);
+
+    if (!xs_transaction_end(ctx->xsh, t, 0))
+        if (errno == EAGAIN)
+            goto retry_transaction;
+    xs_introduce_domain(ctx->xsh, domid, state->store_mfn, state->store_port);
+    free(vm_path);
+    return 0;
+}
+
+static int set_vnuma_info(libxl__gc *gc, uint32_t domid,
+                          const libxl_domain_build_info *info,
+                          const libxl__domain_build_state *state)
+{
+    int rc = 0;
+    unsigned int i, nr_vdistance;
+    unsigned int *vcpu_to_vnode, *vnode_to_pnode, *vdistance = NULL;
+
+    vcpu_to_vnode = libxl__calloc(gc, info->max_vcpus,
+                                  sizeof(unsigned int));
+    vnode_to_pnode = libxl__calloc(gc, info->num_vnuma_nodes,
+                                   sizeof(unsigned int));
+
+    nr_vdistance = info->num_vnuma_nodes * info->num_vnuma_nodes;
+    vdistance = libxl__calloc(gc, nr_vdistance, sizeof(unsigned int));
+
+    for (i = 0; i < info->num_vnuma_nodes; i++) {
+        libxl_vnode_info *v = &info->vnuma_nodes[i];
+        int j;
+
+        /* vnode to pnode mapping */
+        vnode_to_pnode[i] = v->pnode;
+
+        /* vcpu to vnode mapping */
+        libxl_for_each_set_bit(j, v->vcpus)
+            vcpu_to_vnode[j] = i;
+
+        /* node distances */
+        assert(info->num_vnuma_nodes == v->num_distances);
+        memcpy(vdistance + (i * info->num_vnuma_nodes),
+               v->distances,
+               v->num_distances * sizeof(unsigned int));
+    }
+
+    if (xc_domain_setvnuma(CTX->xch, domid, info->num_vnuma_nodes,
+                           state->num_vmemranges, info->max_vcpus,
+                           state->vmemranges, vdistance,
+                           vcpu_to_vnode, vnode_to_pnode) < 0) {
+        LOGE(ERROR, "xc_domain_setvnuma failed");
+        rc = ERROR_FAIL;
+    }
+
+    return rc;
+}
+
+static int libxl__build_dom(libxl__gc *gc, uint32_t domid,
+             libxl_domain_config *d_config, libxl__domain_build_state *state,
+             struct xc_dom_image *dom)
+{
+    libxl_domain_build_info *const info = &d_config->b_info;
+    uint64_t mem_kb;
+    int ret;
+
+    if ( (ret = xc_dom_boot_xen_init(dom, CTX->xch, domid)) != 0 ) {
+        LOGE(ERROR, "xc_dom_boot_xen_init failed");
+        goto out;
+    }
+#ifdef GUEST_RAM_BASE
+    if ( (ret = xc_dom_rambase_init(dom, GUEST_RAM_BASE)) != 0 ) {
+        LOGE(ERROR, "xc_dom_rambase failed");
+        goto out;
+    }
+#endif
+    if ( (ret = xc_dom_parse_image(dom)) != 0 ) {
+        LOG(ERROR, "xc_dom_parse_image failed");
+        goto out;
+    }
+    if ( (ret = libxl__arch_domain_init_hw_description(gc, info, state, dom)) != 0 ) {
+        LOGE(ERROR, "libxl__arch_domain_init_hw_description failed");
+        goto out;
+    }
+
+    mem_kb = dom->container_type == XC_DOM_HVM_CONTAINER ?
+             (info->max_memkb - info->video_memkb) : info->target_memkb;
+    if ( (ret = xc_dom_mem_init(dom, mem_kb / 1024)) != 0 ) {
+        LOGE(ERROR, "xc_dom_mem_init failed");
+        goto out;
+    }
+    if ( (ret = xc_dom_boot_mem_init(dom)) != 0 ) {
+        LOGE(ERROR, "xc_dom_boot_mem_init failed");
+        goto out;
+    }
+    if ( (ret = libxl__arch_domain_finalise_hw_description(gc, domid, d_config, dom)) != 0 ) {
+        LOGE(ERROR, "libxl__arch_domain_finalise_hw_description failed");
+        goto out;
+    }
+    if ( (ret = xc_dom_build_image(dom)) != 0 ) {
+        LOGE(ERROR, "xc_dom_build_image failed");
+        goto out;
+    }
+    if ( (ret = xc_dom_boot_image(dom)) != 0 ) {
+        LOGE(ERROR, "xc_dom_boot_image failed");
+        goto out;
+    }
+    if ( (ret = xc_dom_gnttab_init(dom)) != 0 ) {
+        LOGE(ERROR, "xc_dom_gnttab_init failed");
+        goto out;
+    }
+    if ((ret = libxl__arch_build_dom_finish(gc, info, dom, state)) != 0) {
+        LOGE(ERROR, "libxl__arch_build_dom_finish failed");
+        goto out;
+    }
+
+out:
+    return ret != 0 ? ERROR_FAIL : 0;
+}
+
+int libxl__build_pv(libxl__gc *gc, uint32_t domid,
+             libxl_domain_config *d_config, libxl__domain_build_state *state)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    libxl_domain_build_info *const info = &d_config->b_info;
+    struct xc_dom_image *dom;
+    int ret;
+    int flags = 0;
+
+    xc_dom_loginit(ctx->xch);
+
+    dom = xc_dom_allocate(ctx->xch, state->pv_cmdline, info->u.pv.features);
+    if (!dom) {
+        LOGE(ERROR, "xc_dom_allocate failed");
+        return ERROR_FAIL;
+    }
+
+    dom->container_type = XC_DOM_PV_CONTAINER;
+
+    LOG(DEBUG, "pv kernel mapped %d path %s", state->pv_kernel.mapped, state->pv_kernel.path);
+
+    if (state->pv_kernel.mapped) {
+        ret = xc_dom_kernel_mem(dom,
+                                state->pv_kernel.data,
+                                state->pv_kernel.size);
+        if ( ret != 0) {
+            LOGE(ERROR, "xc_dom_kernel_mem failed");
+            goto out;
+        }
+    } else {
+        ret = xc_dom_kernel_file(dom, state->pv_kernel.path);
+        if ( ret != 0) {
+            LOGE(ERROR, "xc_dom_kernel_file failed");
+            goto out;
+        }
+    }
+
+    if ( state->pv_ramdisk.path && strlen(state->pv_ramdisk.path) ) {
+        if (state->pv_ramdisk.mapped) {
+            if ( (ret = xc_dom_module_mem(dom, state->pv_ramdisk.data, state->pv_ramdisk.size, NULL)) != 0 ) {
+                LOGE(ERROR, "xc_dom_ramdisk_mem failed");
+                goto out;
+            }
+        } else {
+            if ( (ret = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL)) != 0 ) {
+                LOGE(ERROR, "xc_dom_ramdisk_file failed");
+                goto out;
+            }
+        }
+    }
+
+    dom->flags = flags;
+    dom->console_evtchn = state->console_port;
+    dom->console_domid = state->console_domid;
+    dom->xenstore_evtchn = state->store_port;
+    dom->xenstore_domid = state->store_domid;
+    dom->claim_enabled = libxl_defbool_val(info->claim_mode);
+    dom->max_vcpus = info->max_vcpus;
+
+    if (info->num_vnuma_nodes != 0) {
+        unsigned int i;
+
+        ret = libxl__vnuma_build_vmemrange_pv(gc, domid, info, state);
+        if (ret) {
+            LOGE(ERROR, "cannot build vmemranges");
+            goto out;
+        }
+        ret = libxl__vnuma_config_check(gc, info, state);
+        if (ret) goto out;
+
+        ret = set_vnuma_info(gc, domid, info, state);
+        if (ret) goto out;
+
+        dom->nr_vmemranges = state->num_vmemranges;
+        dom->vmemranges = xc_dom_malloc(dom, sizeof(*dom->vmemranges) *
+                                        dom->nr_vmemranges);
+
+        for (i = 0; i < dom->nr_vmemranges; i++) {
+            dom->vmemranges[i].start = state->vmemranges[i].start;
+            dom->vmemranges[i].end   = state->vmemranges[i].end;
+            dom->vmemranges[i].flags = state->vmemranges[i].flags;
+            dom->vmemranges[i].nid   = state->vmemranges[i].nid;
+        }
+
+        dom->nr_vnodes = info->num_vnuma_nodes;
+        dom->vnode_to_pnode = xc_dom_malloc(dom, sizeof(*dom->vnode_to_pnode) *
+                                            dom->nr_vnodes);
+        for (i = 0; i < info->num_vnuma_nodes; i++)
+            dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
+    }
+
+    ret = libxl__build_dom(gc, domid, d_config, state, dom);
+    if (ret != 0)
+        goto out;
+
+    if (xc_dom_translated(dom)) {
+        state->console_mfn = dom->console_pfn;
+        state->store_mfn = dom->xenstore_pfn;
+        state->vuart_gfn = dom->vuart_gfn;
+    } else {
+        state->console_mfn = xc_dom_p2m(dom, dom->console_pfn);
+        state->store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn);
+    }
+
+    ret = 0;
+out:
+    xc_dom_release(dom);
+    return ret == 0 ? 0 : ERROR_FAIL;
+}
+
+static int hvm_build_set_params(xc_interface *handle, uint32_t domid,
+                                libxl_domain_build_info *info,
+                                int store_evtchn, unsigned long *store_mfn,
+                                int console_evtchn, unsigned long *console_mfn,
+                                domid_t store_domid, domid_t console_domid)
+{
+    struct hvm_info_table *va_hvm;
+    uint8_t *va_map, sum;
+    uint64_t str_mfn, cons_mfn;
+    int i;
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        va_map = xc_map_foreign_range(handle, domid,
+                                      XC_PAGE_SIZE, PROT_READ | PROT_WRITE,
+                                      HVM_INFO_PFN);
+        if (va_map == NULL)
+            return ERROR_FAIL;
+
+        va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET);
+        va_hvm->apic_mode = libxl_defbool_val(info->apic);
+        va_hvm->nr_vcpus = info->max_vcpus;
+        memset(va_hvm->vcpu_online, 0, sizeof(va_hvm->vcpu_online));
+        memcpy(va_hvm->vcpu_online, info->avail_vcpus.map, info->avail_vcpus.size);
+        for (i = 0, sum = 0; i < va_hvm->length; i++)
+            sum += ((uint8_t *) va_hvm)[i];
+        va_hvm->checksum -= sum;
+        munmap(va_map, XC_PAGE_SIZE);
+    }
+
+    xc_hvm_param_get(handle, domid, HVM_PARAM_STORE_PFN, &str_mfn);
+    xc_hvm_param_get(handle, domid, HVM_PARAM_CONSOLE_PFN, &cons_mfn);
+    xc_hvm_param_set(handle, domid, HVM_PARAM_STORE_EVTCHN, store_evtchn);
+    xc_hvm_param_set(handle, domid, HVM_PARAM_CONSOLE_EVTCHN, console_evtchn);
+
+    *store_mfn = str_mfn;
+    *console_mfn = cons_mfn;
+
+    return 0;
+}
+
+static int hvm_build_set_xs_values(libxl__gc *gc,
+                                   uint32_t domid,
+                                   struct xc_dom_image *dom,
+                                   const libxl_domain_build_info *info)
+{
+    char *path = NULL;
+    int ret = 0;
+
+    if (dom->smbios_module.guest_addr_out) {
+        path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_ADDRESS, domid);
+
+        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
+                               dom->smbios_module.guest_addr_out);
+        if (ret)
+            goto err;
+
+        path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_LENGTH, domid);
+
+        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
+                               dom->smbios_module.length);
+        if (ret)
+            goto err;
+    }
+
+    /* Only one module can be passed. PVHv2 guests do not support this. */
+    if (dom->acpi_modules[0].guest_addr_out && 
+        info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_ADDRESS, domid);
+
+        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
+                               dom->acpi_modules[0].guest_addr_out);
+        if (ret)
+            goto err;
+
+        path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_LENGTH, domid);
+
+        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
+                               dom->acpi_modules[0].length);
+        if (ret)
+            goto err;
+    }
+
+    return 0;
+
+err:
+    LOG(ERROR, "failed to write firmware xenstore value, err: %d", ret);
+    return ret;
+}
+
+static int libxl__load_hvm_firmware_module(libxl__gc *gc,
+                                           const char *filename,
+                                           const char *what,
+                                           struct xc_hvm_firmware_module *m)
+{
+    int datalen = 0;
+    void *data = NULL;
+    int r, rc;
+
+    LOG(DEBUG, "Loading %s: %s", what, filename);
+    r = libxl_read_file_contents(CTX, filename, &data, &datalen);
+    if (r) {
+        /*
+         * Print a message only on ENOENT, other errors are logged by the
+         * function libxl_read_file_contents().
+         */
+        if (r == ENOENT)
+            LOGEV(ERROR, r, "failed to read %s file", what);
+        rc =  ERROR_FAIL;
+        goto out;
+    }
+    libxl__ptr_add(gc, data);
+    if (datalen) {
+        /* Only accept non-empty files */
+        m->data = data;
+        m->length = datalen;
+    } else {
+        LOG(ERROR, "file %s for %s is empty", filename, what);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    rc = 0;
+out:
+    return rc;
+}
+
+static int libxl__domain_firmware(libxl__gc *gc,
+                                  libxl_domain_build_info *info,
+                                  libxl__domain_build_state *state,
+                                  struct xc_dom_image *dom)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    const char *firmware = NULL;
+    int e, rc;
+    int datalen = 0;
+    void *data;
+    const char *bios_filename = NULL;
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        if (info->u.hvm.firmware) {
+            firmware = info->u.hvm.firmware;
+        } else {
+            switch (info->device_model_version)
+            {
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+                firmware = "hvmloader";
+                break;
+            default:
+                LOG(ERROR, "invalid device model version %d",
+                    info->device_model_version);
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+    }
+
+    if (state->pv_kernel.path != NULL &&
+        info->type == LIBXL_DOMAIN_TYPE_PVH) {
+
+        if (state->shim_path) {
+            rc = xc_dom_kernel_file(dom, state->shim_path);
+            if (rc) {
+                LOGE(ERROR, "xc_dom_kernel_file failed");
+                goto out;
+            }
+
+            /* We've loaded the shim, so load the kernel as a secondary module */
+            if (state->pv_kernel.mapped) {
+                LOG(DEBUG, "xc_dom_module_mem, cmdline %s",
+                    state->pv_cmdline);
+                rc = xc_dom_module_mem(dom, state->pv_kernel.data,
+                                       state->pv_kernel.size, state->pv_cmdline);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_kernel_mem failed");
+                    goto out;
+                }
+            } else {
+                LOG(DEBUG, "xc_dom_module_file, path %s cmdline %s",
+                    state->pv_kernel.path, state->pv_cmdline);
+                rc = xc_dom_module_file(dom, state->pv_kernel.path, state->pv_cmdline);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_kernel_file failed");
+                    goto out;
+                }
+            }
+        } else {
+            /* No shim, so load the kernel directly */
+            if (state->pv_kernel.mapped) {
+                rc = xc_dom_kernel_mem(dom, state->pv_kernel.data,
+                                       state->pv_kernel.size);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_kernel_mem failed");
+                    goto out;
+                }
+            } else {
+                rc = xc_dom_kernel_file(dom, state->pv_kernel.path);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_kernel_file failed");
+                    goto out;
+                }
+            }
+        }
+
+        if (state->pv_ramdisk.path && strlen(state->pv_ramdisk.path)) {
+            if (state->pv_ramdisk.mapped) {
+                rc = xc_dom_module_mem(dom, state->pv_ramdisk.data,
+                                       state->pv_ramdisk.size, NULL);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_ramdisk_mem failed");
+                    goto out;
+                }
+            } else {
+                rc = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_ramdisk_file failed");
+                    goto out;
+                }
+            }
+        }
+    } else {
+        /*
+         * Only HVM guests should get here, PVH should always have a set
+         * kernel at this point.
+         */
+        assert(info->type == LIBXL_DOMAIN_TYPE_HVM);
+        rc = xc_dom_kernel_file(dom, libxl__abs_path(gc, firmware,
+                                                 libxl__xenfirmwaredir_path()));
+    }
+
+    if (rc != 0) {
+        LOGE(ERROR, "xc_dom_{kernel_file/ramdisk_file} failed");
+        goto out;
+    }
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+        if (info->u.hvm.system_firmware) {
+            bios_filename = info->u.hvm.system_firmware;
+        } else {
+            switch (info->u.hvm.bios) {
+            case LIBXL_BIOS_TYPE_SEABIOS:
+                bios_filename = libxl__seabios_path();
+                break;
+            case LIBXL_BIOS_TYPE_OVMF:
+                bios_filename = libxl__ovmf_path();
+                break;
+            case LIBXL_BIOS_TYPE_ROMBIOS:
+            default:
+                abort();
+            }
+        }
+    }
+
+    if (bios_filename) {
+        rc = libxl__load_hvm_firmware_module(gc, bios_filename, "BIOS",
+                                             &dom->system_firmware_module);
+        if (rc) goto out;
+    }
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS &&
+        libxl__ipxe_path()) {
+        const char *fp = libxl__ipxe_path();
+        rc = xc_dom_module_file(dom, fp, "ipxe");
+
+        if (rc) {
+            LOGE(ERROR, "failed to load IPXE %s (%d)", fp, rc);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        info->u.hvm.smbios_firmware) {
+        data = NULL;
+        e = libxl_read_file_contents(ctx, info->u.hvm.smbios_firmware,
+                                     &data, &datalen);
+        if (e) {
+            LOGEV(ERROR, e, "failed to read SMBIOS firmware file %s",
+                info->u.hvm.smbios_firmware);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        libxl__ptr_add(gc, data);
+        if (datalen) {
+            /* Only accept non-empty files */
+            dom->smbios_module.data = data;
+            dom->smbios_module.length = (uint32_t)datalen;
+        }
+    }
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        info->u.hvm.acpi_firmware) {
+        data = NULL;
+        e = libxl_read_file_contents(ctx, info->u.hvm.acpi_firmware,
+                                     &data, &datalen);
+        if (e) {
+            LOGEV(ERROR, e, "failed to read ACPI firmware file %s",
+                info->u.hvm.acpi_firmware);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        libxl__ptr_add(gc, data);
+        if (datalen) {
+            /* Only accept a non-empty file */
+            dom->acpi_modules[0].data = data;
+            dom->acpi_modules[0].length = (uint32_t)datalen;
+        }
+    }
+
+    return 0;
+out:
+    assert(rc != 0);
+    return rc;
+}
+
+int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
+              libxl_domain_config *d_config,
+              libxl__domain_build_state *state)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int rc;
+    uint64_t mmio_start, lowmem_end, highmem_end, mem_size;
+    libxl_domain_build_info *const info = &d_config->b_info;
+    struct xc_dom_image *dom = NULL;
+    bool device_model = info->type == LIBXL_DOMAIN_TYPE_HVM ? true : false;
+
+    xc_dom_loginit(ctx->xch);
+
+    /*
+     * If PVH and we have a shim override, use the shim cmdline.
+     * If PVH and no shim override, use the pv cmdline.
+     * If not PVH, use info->cmdline.
+     */
+    dom = xc_dom_allocate(ctx->xch, info->type == LIBXL_DOMAIN_TYPE_PVH ?
+                          (state->shim_path ? state->shim_cmdline : state->pv_cmdline) :
+                          info->cmdline, NULL);
+    if (!dom) {
+        LOGE(ERROR, "xc_dom_allocate failed");
+        rc = ERROR_NOMEM;
+        goto out;
+    }
+
+    dom->container_type = XC_DOM_HVM_CONTAINER;
+
+    /* The params from the configuration file are in Mb, which are then
+     * multiplied by 1 Kb. This was then divided off when calling
+     * the old xc_hvm_build_target_mem() which then turned them to bytes.
+     * Do all this in one step here...
+     */
+    mem_size = (uint64_t)(info->max_memkb - info->video_memkb) << 10;
+    dom->target_pages = (uint64_t)(info->target_memkb - info->video_memkb) >> 2;
+    dom->claim_enabled = libxl_defbool_val(info->claim_mode);
+    if (info->u.hvm.mmio_hole_memkb) {
+        uint64_t max_ram_below_4g = (1ULL << 32) -
+            (info->u.hvm.mmio_hole_memkb << 10);
+
+        if (max_ram_below_4g < HVM_BELOW_4G_MMIO_START)
+            dom->mmio_size = info->u.hvm.mmio_hole_memkb << 10;
+    }
+
+    rc = libxl__domain_firmware(gc, info, state, dom);
+    if (rc != 0) {
+        LOG(ERROR, "initializing domain firmware failed");
+        goto out;
+    }
+
+    if (dom->target_pages == 0)
+        dom->target_pages = mem_size >> XC_PAGE_SHIFT;
+    if (dom->mmio_size == 0 && device_model)
+        dom->mmio_size = HVM_BELOW_4G_MMIO_LENGTH;
+    else if (dom->mmio_size == 0 && !device_model) {
+#if defined(__i386__) || defined(__x86_64__)
+        /*
+         * Make sure the local APIC page, the ACPI tables and the special pages
+         * are inside the MMIO hole.
+         */
+        xen_paddr_t start =
+            (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES) <<
+            XC_PAGE_SHIFT;
+
+        start = min_t(xen_paddr_t, start, LAPIC_BASE_ADDRESS);
+        start = min_t(xen_paddr_t, start, ACPI_INFO_PHYSICAL_ADDRESS);
+        dom->mmio_size = GB(4) - start;
+#else
+        assert(1);
+#endif
+    }
+    lowmem_end = mem_size;
+    highmem_end = 0;
+    mmio_start = (1ull << 32) - dom->mmio_size;
+    if (lowmem_end > mmio_start)
+    {
+        highmem_end = (1ull << 32) + (lowmem_end - mmio_start);
+        lowmem_end = mmio_start;
+    }
+    dom->lowmem_end = lowmem_end;
+    dom->highmem_end = highmem_end;
+    dom->mmio_start = mmio_start;
+    dom->vga_hole_size = device_model ? LIBXL_VGA_HOLE_SIZE : 0;
+    dom->device_model = device_model;
+    dom->max_vcpus = info->max_vcpus;
+    dom->console_domid = state->console_domid;
+    dom->xenstore_domid = state->store_domid;
+
+    rc = libxl__domain_device_construct_rdm(gc, d_config,
+                                            info->u.hvm.rdm_mem_boundary_memkb*1024,
+                                            dom);
+    if (rc) {
+        LOG(ERROR, "checking reserved device memory failed");
+        goto out;
+    }
+
+    if (info->num_vnuma_nodes != 0) {
+        int i;
+
+        rc = libxl__vnuma_build_vmemrange_hvm(gc, domid, info, state, dom);
+        if (rc != 0) {
+            LOG(ERROR, "hvm build vmemranges failed");
+            goto out;
+        }
+        rc = libxl__vnuma_config_check(gc, info, state);
+        if (rc != 0) goto out;
+        rc = set_vnuma_info(gc, domid, info, state);
+        if (rc != 0) goto out;
+
+        dom->nr_vmemranges = state->num_vmemranges;
+        dom->vmemranges = libxl__malloc(gc, sizeof(*dom->vmemranges) *
+                                        dom->nr_vmemranges);
+
+        for (i = 0; i < dom->nr_vmemranges; i++) {
+            dom->vmemranges[i].start = state->vmemranges[i].start;
+            dom->vmemranges[i].end   = state->vmemranges[i].end;
+            dom->vmemranges[i].flags = state->vmemranges[i].flags;
+            dom->vmemranges[i].nid   = state->vmemranges[i].nid;
+        }
+
+        dom->nr_vnodes = info->num_vnuma_nodes;
+        dom->vnode_to_pnode = libxl__malloc(gc, sizeof(*dom->vnode_to_pnode) *
+                                            dom->nr_vnodes);
+        for (i = 0; i < dom->nr_vnodes; i++)
+            dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
+    }
+
+    rc = libxl__build_dom(gc, domid, d_config, state, dom);
+    if (rc != 0)
+        goto out;
+
+    rc = hvm_build_set_params(ctx->xch, domid, info, state->store_port,
+                               &state->store_mfn, state->console_port,
+                               &state->console_mfn, state->store_domid,
+                               state->console_domid);
+    if (rc != 0) {
+        LOG(ERROR, "hvm build set params failed");
+        goto out;
+    }
+
+    rc = hvm_build_set_xs_values(gc, domid, dom, info);
+    if (rc != 0) {
+        LOG(ERROR, "hvm build set xenstore values failed");
+        goto out;
+    }
+
+    xc_dom_release(dom);
+    return 0;
+
+out:
+    assert(rc != 0);
+    if (dom != NULL) xc_dom_release(dom);
+    return rc;
+}
+
+int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
+                                const char *cmd)
+{
+    char *path = NULL;
+    uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/command");
+    return libxl__xs_printf(gc, XBT_NULL, path, "%s", cmd);
+}
+
+/*==================== Miscellaneous ====================*/
+
+char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid)
+{
+    return GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(uuid));
+}
+
+const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
+                                 const char *userdata_userid,
+                                 const char *wh)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *uuid_string, *path;
+    libxl_dominfo info;
+    int rc;
+
+    libxl_dominfo_init(&info);
+
+    rc = libxl_domain_info(ctx, &info, domid);
+    if (rc) {
+        LOGE(ERROR, "unable to find domain info for domain %"PRIu32, domid);
+        path = NULL;
+        goto out;
+    }
+    uuid_string = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
+    path = GCSPRINTF(XEN_LIB_DIR "/userdata-%s.%u.%s.%s",
+                     wh, domid, uuid_string, userdata_userid);
+
+ out:
+    libxl_dominfo_dispose(&info);
+    return path;
+}
+
+static int userdata_delete(libxl__gc *gc, const char *path)
+{
+    int r;
+    r = unlink(path);
+    if (r) {
+        LOGE(ERROR, "remove failed for %s", path);
+        return errno;
+    }
+    return 0;
+}
+
+void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid)
+{
+    const char *pattern;
+    glob_t gl;
+    int r, i;
+
+    pattern = libxl__userdata_path(gc, domid, "*", "?");
+    if (!pattern)
+        goto out;
+
+    gl.gl_pathc = 0;
+    gl.gl_pathv = 0;
+    gl.gl_offs = 0;
+    r = glob(pattern, GLOB_ERR|GLOB_NOSORT|GLOB_MARK, 0, &gl);
+    if (r == GLOB_NOMATCH)
+        goto out;
+    if (r)
+        LOGE(ERROR, "glob failed for %s", pattern);
+
+    /* Note: don't delete domain-userdata-lock, it will be handled by
+     * unlock function.
+     */
+    for (i=0; i<gl.gl_pathc; i++) {
+        if (!strstr(gl.gl_pathv[i], "domain-userdata-lock"))
+            userdata_delete(gc, gl.gl_pathv[i]);
+    }
+    globfree(&gl);
+out:
+    return;
+}
+
+int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
+                          const char *userdata_userid,
+                          const uint8_t *data, int datalen)
+{
+    const char *filename;
+    const char *newfilename;
+    int e, rc;
+    int fd = -1;
+
+    filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
+    if (!filename) {
+        rc = ERROR_NOMEM;
+        goto out;
+    }
+
+    if (!datalen) {
+        rc = userdata_delete(gc, filename);
+        goto out;
+    }
+
+    newfilename = libxl__userdata_path(gc, domid, userdata_userid, "n");
+    if (!newfilename) {
+        rc = ERROR_NOMEM;
+        goto out;
+    }
+
+    rc = ERROR_FAIL;
+
+    fd = open(newfilename, O_RDWR | O_CREAT | O_TRUNC, 0600);
+    if (fd < 0)
+        goto err;
+
+    if (libxl_write_exactly(CTX, fd, data, datalen, "userdata", newfilename))
+        goto err;
+
+    if (close(fd) < 0) {
+        fd = -1;
+        goto err;
+    }
+    fd = -1;
+
+    if (rename(newfilename, filename))
+        goto err;
+
+    rc = 0;
+
+err:
+    if (fd >= 0) {
+        e = errno;
+        close(fd);
+        errno = e;
+    }
+
+    if (rc)
+        LOGE(ERROR, "cannot write/rename %s for %s", newfilename, filename);
+out:
+    return rc;
+}
+
+int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
+                              const char *userdata_userid,
+                              const uint8_t *data, int datalen)
+{
+    GC_INIT(ctx);
+    int rc;
+    libxl__flock *lock;
+
+    CTX_LOCK;
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    rc = libxl__userdata_store(gc, domid, userdata_userid,
+                               data, datalen);
+
+    libxl__unlock_file(lock);
+
+out:
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
+                             const char *userdata_userid,
+                             uint8_t **data_r, int *datalen_r)
+{
+    const char *filename;
+    int e, rc;
+    int datalen = 0;
+    void *data = 0;
+
+    filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
+    if (!filename) {
+        rc = ERROR_NOMEM;
+        goto out;
+    }
+
+    e = libxl_read_file_contents(CTX, filename, data_r ? &data : 0, &datalen);
+    if (e && errno != ENOENT) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (!e && !datalen) {
+        LOG(ERROR, "userdata file %s is empty", filename);
+        if (data_r) assert(!*data_r);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (data_r) *data_r = data;
+    if (datalen_r) *datalen_r = datalen;
+    rc = 0;
+
+out:
+    return rc;
+}
+
+int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
+                                 const char *userdata_userid,
+                                 uint8_t **data_r, int *datalen_r)
+{
+    GC_INIT(ctx);
+    int rc;
+    libxl__flock *lock;
+
+    CTX_LOCK;
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    rc = libxl__userdata_retrieve(gc, domid, userdata_userid,
+                                  data_r, datalen_r);
+
+
+    libxl__unlock_file(lock);
+out:
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
+                          const char *userdata_userid)
+{
+    GC_INIT(ctx);
+    CTX_LOCK;
+
+    int rc;
+    libxl__flock *lock = NULL;
+    const char *filename;
+
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
+    if (!filename) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (unlink(filename)) {
+        LOGE(ERROR, "error deleting userdata file: %s", filename);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+out:
+    if (lock)
+        libxl__unlock_file(lock);
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_dom_save.c b/tools/libs/light/libxl_dom_save.c
new file mode 100644 (file)
index 0000000..32e3cb5
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <xen/errno.h>
+
+/*========================= Domain save ============================*/
+
+static void stream_done(libxl__egc *egc,
+                        libxl__stream_write_state *sws, int rc);
+static void domain_save_done(libxl__egc *egc,
+                             libxl__domain_save_state *dss, int rc);
+
+/*----- complicated callback, called by xc_domain_save -----*/
+
+/*
+ * We implement the other end of protocol for controlling qemu-dm's
+ * logdirty.  There is no documentation for this protocol, but our
+ * counterparty's implementation is in
+ * qemu-xen-traditional.git:xenstore.c in the function
+ * xenstore_process_logdirty_event
+ */
+
+static void domain_suspend_switch_qemu_xen_traditional_logdirty
+                               (libxl__egc *egc, int domid, unsigned enable,
+                                libxl__logdirty_switch *lds);
+static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch*,
+                            const char *watch_path, const char *event_path);
+static void domain_suspend_switch_qemu_xen_logdirty
+                               (libxl__egc *egc, int domid, unsigned enable,
+                                libxl__logdirty_switch *lds);
+static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
+                                          libxl__ev_qmp *qmp,
+                                          const libxl__json_object *,
+                                          int rc);
+static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                                    const struct timeval *requested_abs,
+                                    int rc);
+static void switch_logdirty_done(libxl__egc *egc,
+                                 libxl__logdirty_switch *lds, int rc);
+
+void libxl__logdirty_init(libxl__logdirty_switch *lds)
+{
+    lds->cmd_path = 0;
+    libxl__ev_xswatch_init(&lds->watch);
+    libxl__ev_time_init(&lds->timeout);
+    libxl__ev_qmp_init(&lds->qmp);
+}
+
+void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
+                                               int domid, unsigned enable,
+                                               libxl__logdirty_switch *lds)
+{
+    STATE_AO_GC(lds->ao);
+
+    switch (libxl__device_model_version_running(gc, domid)) {
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+        domain_suspend_switch_qemu_xen_traditional_logdirty(egc, domid, enable,
+                                                            lds);
+        break;
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+        domain_suspend_switch_qemu_xen_logdirty(egc, domid, enable, lds);
+        break;
+    default:
+        LOGD(ERROR, domid, "logdirty switch failed"
+             ", no valid device model version found, abandoning suspend");
+        lds->callback(egc, lds, ERROR_FAIL);
+    }
+}
+
+static void domain_suspend_switch_qemu_xen_traditional_logdirty
+                               (libxl__egc *egc, int domid, unsigned enable,
+                                libxl__logdirty_switch *lds)
+{
+    STATE_AO_GC(lds->ao);
+    int rc;
+    xs_transaction_t t = 0;
+    const char *got;
+
+    if (!lds->cmd_path) {
+        uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+        lds->cmd_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
+                                             "/logdirty/cmd");
+        lds->ret_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
+                                             "/logdirty/ret");
+    }
+    lds->cmd = enable ? "enable" : "disable";
+
+    rc = libxl__ev_xswatch_register(gc, &lds->watch,
+                                switch_logdirty_xswatch, lds->ret_path);
+    if (rc) goto out;
+
+    rc = libxl__ev_time_register_rel(ao, &lds->timeout,
+                                switch_logdirty_timeout, 10*1000);
+    if (rc) goto out;
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        rc = libxl__xs_read_checked(gc, t, lds->cmd_path, &got);
+        if (rc) goto out;
+
+        if (got) {
+            const char *got_ret;
+            rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got_ret);
+            if (rc) goto out;
+
+            if (!got_ret || strcmp(got, got_ret)) {
+                LOGD(ERROR, domid, "controlling logdirty: qemu was already sent"
+                     " command `%s' (xenstore path `%s') but result is `%s'",
+                     got, lds->cmd_path, got_ret ? got_ret : "<none>");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+            rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
+            if (rc) goto out;
+        }
+
+        rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
+        if (rc) goto out;
+
+        rc = libxl__xs_write_checked(gc, t, lds->cmd_path, lds->cmd);
+        if (rc) goto out;
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc<0) goto out;
+    }
+
+    /* OK, wait for some callback */
+    return;
+
+ out:
+    LOGD(ERROR, domid, "logdirty switch failed (rc=%d), abandoning suspend",rc);
+    libxl__xs_transaction_abort(gc, &t);
+    switch_logdirty_done(egc,lds,rc);
+}
+
+static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch *watch,
+                            const char *watch_path, const char *event_path)
+{
+    libxl__logdirty_switch *lds = CONTAINER_OF(watch, *lds, watch);
+    STATE_AO_GC(lds->ao);
+    const char *got;
+    xs_transaction_t t = 0;
+    int rc;
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got);
+        if (rc) goto out;
+
+        if (!got) {
+            rc = +1;
+            goto out;
+        }
+
+        if (strcmp(got, lds->cmd)) {
+            LOG(ERROR,"logdirty switch: sent command `%s' but got reply `%s'"
+                " (xenstore paths `%s' / `%s')", lds->cmd, got,
+                lds->cmd_path, lds->ret_path);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
+        if (rc) goto out;
+
+        rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
+        if (rc) goto out;
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc<0) goto out;
+    }
+
+ out:
+    /* rc < 0: error
+     * rc == 0: ok, we are done
+     * rc == +1: need to keep waiting
+     */
+    libxl__xs_transaction_abort(gc, &t);
+
+    if (rc <= 0) {
+        if (rc < 0)
+            LOG(ERROR,"logdirty switch: failed (rc=%d)",rc);
+        switch_logdirty_done(egc,lds,rc);
+    }
+}
+
+static void domain_suspend_switch_qemu_xen_logdirty
+                               (libxl__egc *egc, int domid, unsigned enable,
+                                libxl__logdirty_switch *lds)
+{
+    STATE_AO_GC(lds->ao);
+    int rc;
+    libxl__json_object *args = NULL;
+
+    /* Convenience aliases. */
+    libxl__ev_qmp *const qmp = &lds->qmp;
+
+    rc = libxl__ev_time_register_rel(ao, &lds->timeout,
+                                     switch_logdirty_timeout, 10 * 1000);
+    if (rc) goto out;
+
+    qmp->ao = ao;
+    qmp->domid = domid;
+    qmp->payload_fd = -1;
+    qmp->callback = switch_qemu_xen_logdirty_done;
+    libxl__qmp_param_add_bool(gc, &args, "enable", enable);
+    rc = libxl__ev_qmp_send(egc, qmp, "xen-set-global-dirty-log", args);
+    if (rc) goto out;
+
+    return;
+out:
+    switch_qemu_xen_logdirty_done(egc, qmp, NULL, rc);
+}
+
+static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
+                                          libxl__ev_qmp *qmp,
+                                          const libxl__json_object *r,
+                                          int rc)
+{
+    EGC_GC;
+    libxl__logdirty_switch *lds = CONTAINER_OF(qmp, *lds, qmp);
+
+    if (rc)
+        LOGD(ERROR, qmp->domid,
+             "logdirty switch failed (rc=%d), abandoning suspend",rc);
+    switch_logdirty_done(egc, lds, rc);
+}
+
+static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                                    const struct timeval *requested_abs,
+                                    int rc)
+{
+    libxl__logdirty_switch *lds = CONTAINER_OF(ev, *lds, timeout);
+    STATE_AO_GC(lds->ao);
+    LOG(ERROR,"logdirty switch: wait for device model timed out");
+    switch_logdirty_done(egc,lds,ERROR_FAIL);
+}
+
+static void switch_logdirty_done(libxl__egc *egc,
+                                 libxl__logdirty_switch *lds,
+                                 int rc)
+{
+    STATE_AO_GC(lds->ao);
+
+    libxl__ev_xswatch_deregister(gc, &lds->watch);
+    libxl__ev_time_deregister(gc, &lds->timeout);
+    libxl__ev_qmp_dispose(gc, &lds->qmp);
+
+    lds->callback(egc, lds, rc);
+}
+
+static void domain_suspend_switch_qemu_logdirty_done
+                        (libxl__egc *egc, libxl__logdirty_switch *lds, int rc);
+
+void libxl__domain_suspend_common_switch_qemu_logdirty
+                               (uint32_t domid, unsigned enable, void *user)
+{
+    libxl__save_helper_state *shs = user;
+    libxl__egc *egc = shs->egc;
+    libxl__domain_save_state *dss = shs->caller_state;
+
+    /* Convenience aliases. */
+    libxl__logdirty_switch *const lds = &dss->logdirty;
+
+    if (dss->type == LIBXL_DOMAIN_TYPE_PVH) {
+        domain_suspend_switch_qemu_logdirty_done(egc, lds, 0);
+        return;
+    }
+
+    lds->callback = domain_suspend_switch_qemu_logdirty_done;
+    libxl__domain_common_switch_qemu_logdirty(egc, domid, enable, lds);
+}
+
+static void domain_suspend_switch_qemu_logdirty_done
+                        (libxl__egc *egc, libxl__logdirty_switch *lds, int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(lds, *dss, logdirty);
+
+    if (rc) {
+        dss->rc = rc;
+        libxl__xc_domain_saverestore_async_callback_done(egc,
+                                                         &dss->sws.shs, -1);
+    } else
+        libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+/*----- callbacks, called by xc_domain_save -----*/
+
+/*
+ * Expand the buffer 'buf' of length 'len', to append 'str' including its NUL
+ * terminator.
+ */
+static void append_string(libxl__gc *gc, char **buf, uint32_t *len,
+                          const char *str)
+{
+    size_t extralen = strlen(str) + 1;
+    char *new = libxl__realloc(gc, *buf, *len + extralen);
+
+    *buf = new;
+    memcpy(new + *len, str, extralen);
+    *len += extralen;
+}
+
+int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
+                                       char **callee_buf,
+                                       uint32_t *callee_len)
+{
+    STATE_AO_GC(dss->ao);
+    const char *xs_root;
+    char **entries, *buf = NULL;
+    unsigned int nr_entries, i, j, len = 0;
+    int rc;
+
+    const uint32_t domid = dss->domid;
+    const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+    xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
+
+    entries = libxl__xs_directory(gc, 0, GCSPRINTF("%s/physmap", xs_root),
+                                  &nr_entries);
+    if (!entries || nr_entries == 0) { rc = 0; goto out; }
+
+    for (i = 0; i < nr_entries; ++i) {
+        static const char *const physmap_subkeys[] = {
+            "start_addr", "size", "name"
+        };
+
+        for (j = 0; j < ARRAY_SIZE(physmap_subkeys); ++j) {
+            const char *key = GCSPRINTF("physmap/%s/%s",
+                                        entries[i], physmap_subkeys[j]);
+
+            const char *val =
+                libxl__xs_read(gc, XBT_NULL,
+                               GCSPRINTF("%s/%s", xs_root, key));
+
+            if (!val) { rc = ERROR_FAIL; goto out; }
+
+            append_string(gc, &buf, &len, key);
+            append_string(gc, &buf, &len, val);
+        }
+    }
+
+    rc = 0;
+
+ out:
+    if (!rc) {
+        *callee_buf = buf;
+        *callee_len = len;
+    }
+
+    return rc;
+}
+
+/*----- main code for saving, in order of execution -----*/
+
+void libxl__domain_save(libxl__egc *egc, libxl__domain_save_state *dss)
+{
+    STATE_AO_GC(dss->ao);
+    int rc, ret;
+
+    /* Convenience aliases */
+    const uint32_t domid = dss->domid;
+    const libxl_domain_type type = dss->type;
+    const int live = dss->live;
+    const int debug = dss->debug;
+    const libxl_domain_remus_info *const r_info = dss->remus;
+    libxl__srm_save_autogen_callbacks *const callbacks =
+        &dss->sws.shs.callbacks.save.a;
+    unsigned int nr_vnodes = 0, nr_vmemranges = 0, nr_vcpus = 0;
+    libxl__domain_suspend_state *dsps = &dss->dsps;
+
+    if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE && !r_info) {
+        LOGD(ERROR, domid, "Migration stream is checkpointed, but there's no "
+                           "checkpoint info!");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    dss->rc = 0;
+    libxl__logdirty_init(&dss->logdirty);
+    dss->logdirty.ao = ao;
+
+    dsps->ao = ao;
+    dsps->domid = domid;
+    dsps->live = !!live;
+    rc = libxl__domain_suspend_init(egc, dsps, type);
+    if (rc) goto out;
+
+    dss->xcflags = (live ? XCFLAGS_LIVE : 0)
+          | (debug ? XCFLAGS_DEBUG : 0);
+
+    /* Disallow saving a guest with vNUMA configured because migration
+     * stream does not preserve node information.
+     *
+     * Reject any domain which has vnuma enabled, even if the
+     * configuration is empty. Only domains which have no vnuma
+     * configuration at all are supported.
+     */
+    ret = xc_domain_getvnuma(CTX->xch, domid, &nr_vnodes, &nr_vmemranges,
+                             &nr_vcpus, NULL, NULL, NULL);
+    if (ret != -1 || errno != EOPNOTSUPP) {
+        LOGD(ERROR, domid, "Cannot save a guest with vNUMA configured");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (dss->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_NONE)
+        callbacks->suspend = libxl__domain_suspend_callback;
+
+    callbacks->switch_qemu_logdirty = libxl__domain_suspend_common_switch_qemu_logdirty;
+
+    dss->sws.ao  = dss->ao;
+    dss->sws.dss = dss;
+    dss->sws.fd  = dss->fd;
+    dss->sws.back_channel = false;
+    dss->sws.completion_callback = stream_done;
+
+    libxl__stream_write_start(egc, &dss->sws);
+    return;
+
+ out:
+    domain_save_done(egc, dss, rc);
+}
+
+static void stream_done(libxl__egc *egc,
+                        libxl__stream_write_state *sws, int rc)
+{
+    domain_save_done(egc, sws->dss, rc);
+}
+
+static void domain_save_done(libxl__egc *egc,
+                             libxl__domain_save_state *dss, int rc)
+{
+    STATE_AO_GC(dss->ao);
+
+    /* Convenience aliases */
+    const uint32_t domid = dss->domid;
+    libxl__domain_suspend_state *dsps = &dss->dsps;
+
+    libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
+
+    if (dsps->guest_evtchn.port > 0)
+        xc_suspend_evtchn_release(CTX->xch, CTX->xce, domid,
+                        dsps->guest_evtchn.port, &dsps->guest_evtchn_lockfd);
+
+    if (dss->remus) {
+        /*
+         * With Remus/COLO, if we reach this point, it means either
+         * backup died or some network error occurred preventing us
+         * from sending checkpoints. Teardown the network buffers and
+         * release netlink resources.  This is an async op.
+         */
+        if (libxl_defbool_val(dss->remus->colo))
+            libxl__colo_save_teardown(egc, &dss->css, rc);
+        else
+            libxl__remus_teardown(egc, &dss->rs, rc);
+        return;
+    }
+
+    dss->callback(egc, dss, rc);
+}
+
+/*========================= Domain restore ============================*/
+
+/*
+ * Inspect the buffer between start and end, and return a pointer to the
+ * character following the NUL terminator of start, or NULL if start is not
+ * terminated before end.
+ */
+static const char *next_string(const char *start, const char *end)
+{
+    if (start >= end) return NULL;
+
+    size_t total_len = end - start;
+    size_t len = strnlen(start, total_len);
+
+    if (len == total_len)
+        return NULL;
+    else
+        return start + len + 1;
+}
+
+int libxl__restore_emulator_xenstore_data(libxl__domain_create_state *dcs,
+                                          const char *ptr, uint32_t size)
+{
+    STATE_AO_GC(dcs->ao);
+    const char *next = ptr, *end = ptr + size, *key, *val;
+    int rc;
+
+    const uint32_t domid = dcs->guest_domid;
+    const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+    const char *xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
+
+    while (next < end) {
+        key = next;
+        next = next_string(next, end);
+
+        /* Sanitise 'key'. */
+        if (!next) {
+            rc = ERROR_FAIL;
+            LOGD(ERROR, domid, "Key in xenstore data not NUL terminated");
+            goto out;
+        }
+        if (key[0] == '\0') {
+            rc = ERROR_FAIL;
+            LOGD(ERROR, domid, "empty key found in xenstore data");
+            goto out;
+        }
+        if (key[0] == '/') {
+            rc = ERROR_FAIL;
+            LOGD(ERROR, domid, "Key in xenstore data not relative");
+            goto out;
+        }
+
+        val = next;
+        next = next_string(next, end);
+
+        /* Sanitise 'val'. */
+        if (!next) {
+            rc = ERROR_FAIL;
+            LOGD(ERROR, domid, "Val in xenstore data not NUL terminated");
+            goto out;
+        }
+
+        libxl__xs_printf(gc, XBT_NULL,
+                         GCSPRINTF("%s/%s", xs_root, key),
+                         "%s", val);
+    }
+
+    rc = 0;
+
+ out:
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_dom_suspend.c b/tools/libs/light/libxl_dom_suspend.c
new file mode 100644 (file)
index 0000000..25d1571
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*====================== Domain suspend =======================*/
+
+int libxl__domain_suspend_init(libxl__egc *egc,
+                               libxl__domain_suspend_state *dsps,
+                               libxl_domain_type type)
+{
+    STATE_AO_GC(dsps->ao);
+    int rc = ERROR_FAIL;
+    int port;
+
+    /* Convenience aliases */
+    const uint32_t domid = dsps->domid;
+
+    libxl__xswait_init(&dsps->pvcontrol);
+    libxl__ev_evtchn_init(&dsps->guest_evtchn);
+    libxl__ev_xswatch_init(&dsps->guest_watch);
+    libxl__ev_time_init(&dsps->guest_timeout);
+    libxl__ev_qmp_init(&dsps->qmp);
+
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) goto out;
+    dsps->type = type;
+
+    dsps->guest_evtchn.port = -1;
+    dsps->guest_evtchn_lockfd = -1;
+    dsps->guest_responded = 0;
+    dsps->dm_savefile = libxl__device_model_savefile(gc, domid);
+
+    port = xs_suspend_evtchn_port(domid);
+
+    if (port >= 0) {
+        rc = libxl__ctx_evtchn_init(gc);
+        if (rc) goto out;
+
+        dsps->guest_evtchn.port =
+            xc_suspend_evtchn_init_exclusive(CTX->xch, CTX->xce,
+                                    domid, port, &dsps->guest_evtchn_lockfd);
+
+        if (dsps->guest_evtchn.port < 0) {
+            LOGD(WARN, domid, "Suspend event channel initialization failed");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/*----- callbacks, called by xc_domain_save -----*/
+
+void libxl__domain_suspend_device_model(libxl__egc *egc,
+                                       libxl__domain_suspend_state *dsps)
+{
+    STATE_AO_GC(dsps->ao);
+    int rc = 0;
+    uint32_t const domid = dsps->domid;
+    const char *const filename = dsps->dm_savefile;
+
+    switch (libxl__device_model_version_running(gc, domid)) {
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
+        LOGD(DEBUG, domid, "Saving device model state to %s", filename);
+        libxl__qemu_traditional_cmd(gc, domid, "save");
+        libxl__wait_for_device_model_deprecated(gc, domid, "paused", NULL, NULL, NULL);
+        break;
+    }
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+        /* calls dsps->callback_device_model_done when done */
+        libxl__qmp_suspend_save(egc, dsps); /* must be last */
+        return;
+    default:
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+out:
+    if (rc)
+        LOGD(ERROR, dsps->domid,
+             "failed to suspend device model, rc=%d", rc);
+    dsps->callback_device_model_done(egc, dsps, rc); /* must be last */
+}
+
+static void domain_suspend_common_wait_guest(libxl__egc *egc,
+                                             libxl__domain_suspend_state *dsps);
+static void domain_suspend_common_guest_suspended(libxl__egc *egc,
+                                         libxl__domain_suspend_state *dsps);
+
+static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
+      libxl__xswait_state *xswa, int rc, const char *state);
+static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
+        libxl__ev_evtchn *evev);
+static void suspend_common_wait_guest_watch(libxl__egc *egc,
+      libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path);
+static void suspend_common_wait_guest_check(libxl__egc *egc,
+        libxl__domain_suspend_state *dsps);
+static void suspend_common_wait_guest_timeout(libxl__egc *egc,
+      libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+
+static void domain_suspend_common_done(libxl__egc *egc,
+                                       libxl__domain_suspend_state *dsps,
+                                       int rc);
+
+static void domain_suspend_callback_common(libxl__egc *egc,
+                                           libxl__domain_suspend_state *dsps);
+static void domain_suspend_callback_common_done(libxl__egc *egc,
+                                libxl__domain_suspend_state *dsps, int rc);
+
+/* calls dsps->callback_common_done when done */
+void libxl__domain_suspend(libxl__egc *egc,
+                           libxl__domain_suspend_state *dsps)
+{
+    domain_suspend_callback_common(egc, dsps);
+}
+
+static bool domain_suspend_pvcontrol_acked(const char *state) {
+    /* any value other than "suspend", including ENOENT (i.e. !state), is OK */
+    if (!state) return 1;
+    return strcmp(state,"suspend");
+}
+
+/* calls dsps->callback_common_done when done */
+static void domain_suspend_callback_common(libxl__egc *egc,
+                                           libxl__domain_suspend_state *dsps)
+{
+    STATE_AO_GC(dsps->ao);
+    uint64_t hvm_s_state = 0, hvm_pvdrv = 0;
+    int ret, rc;
+
+    /* Convenience aliases */
+    const uint32_t domid = dsps->domid;
+
+    if (dsps->type != LIBXL_DOMAIN_TYPE_PV) {
+        xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_CALLBACK_IRQ, &hvm_pvdrv);
+        xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, &hvm_s_state);
+    }
+
+    if ((hvm_s_state == 0) && (dsps->guest_evtchn.port >= 0)) {
+        LOGD(DEBUG, domid, "issuing %s suspend request via event channel",
+            dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
+        ret = xenevtchn_notify(CTX->xce, dsps->guest_evtchn.port);
+        if (ret < 0) {
+            LOGD(ERROR, domid, "xenevtchn_notify failed ret=%d", ret);
+            rc = ERROR_FAIL;
+            goto err;
+        }
+
+        dsps->guest_evtchn.callback = domain_suspend_common_wait_guest_evtchn;
+        rc = libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
+        if (rc) goto err;
+
+        rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
+                                         suspend_common_wait_guest_timeout,
+                                         60*1000);
+        if (rc) goto err;
+
+        return;
+    }
+
+    if (dsps->type == LIBXL_DOMAIN_TYPE_HVM && (!hvm_pvdrv || hvm_s_state)) {
+        LOGD(DEBUG, domid, "Calling xc_domain_shutdown on HVM domain");
+        ret = xc_domain_shutdown(CTX->xch, domid, SHUTDOWN_suspend);
+        if (ret < 0) {
+            LOGED(ERROR, domid, "xc_domain_shutdown failed");
+            rc = ERROR_FAIL;
+            goto err;
+        }
+        /* The guest does not (need to) respond to this sort of request. */
+        dsps->guest_responded = 1;
+        domain_suspend_common_wait_guest(egc, dsps);
+        return;
+    }
+
+    LOGD(DEBUG, domid, "issuing %s suspend request via XenBus control node",
+        dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
+
+    dsps->pvcontrol.ao = ao;
+    dsps->pvcontrol.callback = domain_suspend_common_pvcontrol_suspending;
+    rc = libxl__domain_pvcontrol(egc, &dsps->pvcontrol, domid, "suspend");
+    if (rc) goto err;
+
+    return;
+
+ err:
+    domain_suspend_common_done(egc, dsps, rc);
+}
+
+static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
+        libxl__ev_evtchn *evev)
+{
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(evev, *dsps, guest_evtchn);
+    STATE_AO_GC(dsps->ao);
+    /* If we should be done waiting, suspend_common_wait_guest_check
+     * will end up calling domain_suspend_common_guest_suspended or
+     * domain_suspend_common_done, both of which cancel the evtchn
+     * wait as needed.  So re-enable it now. */
+    libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
+    suspend_common_wait_guest_check(egc, dsps);
+}
+
+static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
+      libxl__xswait_state *xswa, int rc, const char *state)
+{
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(xswa, *dsps, pvcontrol);
+    STATE_AO_GC(dsps->ao);
+    xs_transaction_t t = 0;
+
+    if (!rc && !domain_suspend_pvcontrol_acked(state))
+        /* keep waiting */
+        return;
+
+    libxl__xswait_stop(gc, &dsps->pvcontrol);
+
+    if (rc == ERROR_TIMEDOUT) {
+        /*
+         * Guest appears to not be responding. Cancel the suspend
+         * request.
+         *
+         * We re-read the suspend node and clear it within a
+         * transaction in order to handle the case where we race
+         * against the guest catching up and acknowledging the request
+         * at the last minute.
+         */
+        for (;;) {
+            rc = libxl__xs_transaction_start(gc, &t);
+            if (rc) goto err;
+
+            rc = libxl__xs_read_checked(gc, t, xswa->path, &state);
+            if (rc) goto err;
+
+            if (domain_suspend_pvcontrol_acked(state))
+                /* last minute ack */
+                break;
+
+            rc = libxl__xs_write_checked(gc, t, xswa->path, "");
+            if (rc) goto err;
+
+            rc = libxl__xs_transaction_commit(gc, &t);
+            if (!rc) {
+                LOGD(ERROR, dsps->domid,
+                     "guest didn't acknowledge suspend, cancelling request");
+                goto err;
+            }
+            if (rc<0) goto err;
+        }
+    } else if (rc) {
+        /* some error in xswait's read of xenstore, already logged */
+        goto err;
+    }
+
+    assert(domain_suspend_pvcontrol_acked(state));
+    LOGD(DEBUG, dsps->domid, "guest acknowledged suspend request");
+
+    libxl__xs_transaction_abort(gc, &t);
+    dsps->guest_responded = 1;
+    domain_suspend_common_wait_guest(egc,dsps);
+    return;
+
+ err:
+    libxl__xs_transaction_abort(gc, &t);
+    domain_suspend_common_done(egc, dsps, rc);
+    return;
+}
+
+static void domain_suspend_common_wait_guest(libxl__egc *egc,
+                                             libxl__domain_suspend_state *dsps)
+{
+    STATE_AO_GC(dsps->ao);
+    int rc;
+
+    LOGD(DEBUG, dsps->domid, "wait for the guest to suspend");
+
+    rc = libxl__ev_xswatch_register(gc, &dsps->guest_watch,
+                                    suspend_common_wait_guest_watch,
+                                    "@releaseDomain");
+    if (rc) goto err;
+
+    rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
+                                     suspend_common_wait_guest_timeout,
+                                     60*1000);
+    if (rc) goto err;
+    return;
+
+ err:
+    domain_suspend_common_done(egc, dsps, rc);
+}
+
+static void suspend_common_wait_guest_watch(libxl__egc *egc,
+      libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path)
+{
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(xsw, *dsps, guest_watch);
+    suspend_common_wait_guest_check(egc, dsps);
+}
+
+static void suspend_common_wait_guest_check(libxl__egc *egc,
+        libxl__domain_suspend_state *dsps)
+{
+    STATE_AO_GC(dsps->ao);
+    xc_domaininfo_t info;
+    int ret;
+    int shutdown_reason;
+
+    /* Convenience aliases */
+    const uint32_t domid = dsps->domid;
+
+    ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
+    if (ret < 0) {
+        LOGED(ERROR, domid, "unable to check for status of guest");
+        goto err;
+    }
+
+    if (!(ret == 1 && info.domain == domid)) {
+        LOGED(ERROR, domid, "guest we were suspending has been destroyed");
+        goto err;
+    }
+
+    if (!(info.flags & XEN_DOMINF_shutdown))
+        /* keep waiting */
+        return;
+
+    shutdown_reason = (info.flags >> XEN_DOMINF_shutdownshift)
+        & XEN_DOMINF_shutdownmask;
+    if (shutdown_reason != SHUTDOWN_suspend) {
+        LOGD(DEBUG, domid, "guest we were suspending has shut down"
+             " with unexpected reason code %d", shutdown_reason);
+        goto err;
+    }
+
+    LOGD(DEBUG, domid, "guest has suspended");
+    domain_suspend_common_guest_suspended(egc, dsps);
+    return;
+
+ err:
+    domain_suspend_common_done(egc, dsps, ERROR_FAIL);
+}
+
+static void suspend_common_wait_guest_timeout(libxl__egc *egc,
+      libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, guest_timeout);
+    STATE_AO_GC(dsps->ao);
+    if (rc == ERROR_TIMEDOUT) {
+        LOGD(ERROR, dsps->domid, "guest did not suspend, timed out");
+        rc = ERROR_GUEST_TIMEDOUT;
+    }
+    domain_suspend_common_done(egc, dsps, rc);
+}
+
+static void domain_suspend_common_guest_suspended(libxl__egc *egc,
+                                         libxl__domain_suspend_state *dsps)
+{
+    STATE_AO_GC(dsps->ao);
+
+    libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
+    libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
+    libxl__ev_time_deregister(gc, &dsps->guest_timeout);
+
+    if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) {
+        dsps->callback_device_model_done = domain_suspend_common_done;
+        libxl__domain_suspend_device_model(egc, dsps); /* must be last */
+        return;
+    }
+    domain_suspend_common_done(egc, dsps, 0);
+}
+
+static void domain_suspend_common_done(libxl__egc *egc,
+                                       libxl__domain_suspend_state *dsps,
+                                       int rc)
+{
+    EGC_GC;
+    assert(!libxl__xswait_inuse(&dsps->pvcontrol));
+    libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
+    libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
+    libxl__ev_time_deregister(gc, &dsps->guest_timeout);
+    libxl__ev_qmp_dispose(gc, &dsps->qmp);
+    dsps->callback_common_done(egc, dsps, rc);
+}
+
+void libxl__domain_suspend_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__egc *egc = shs->egc;
+    libxl__domain_save_state *dss = shs->caller_state;
+    libxl__domain_suspend_state *dsps = &dss->dsps;
+
+    dsps->callback_common_done = domain_suspend_callback_common_done;
+    domain_suspend_callback_common(egc, dsps);
+}
+
+static void domain_suspend_callback_common_done(libxl__egc *egc,
+                                libxl__domain_suspend_state *dsps, int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
+    dss->rc = rc;
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+/*======================= Domain resume ========================*/
+
+int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid)
+{
+    const char *path, *state;
+
+    switch (libxl__device_model_version_running(gc, domid)) {
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
+        uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+        path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+        state = libxl__xs_read(gc, XBT_NULL, path);
+        if (state != NULL && !strcmp(state, "paused")) {
+            libxl__qemu_traditional_cmd(gc, domid, "continue");
+            libxl__wait_for_device_model_deprecated(gc, domid, "running",
+                                                    NULL, NULL, NULL);
+        }
+        break;
+    }
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+        if (libxl__qmp_resume(gc, domid))
+            return ERROR_FAIL;
+        break;
+    default:
+        return ERROR_INVAL;
+    }
+
+    return 0;
+}
+
+int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid, int suspend_cancel)
+{
+    int rc = 0;
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (type == LIBXL_DOMAIN_TYPE_HVM) {
+        rc = libxl__domain_resume_device_model_deprecated(gc, domid);
+        if (rc) {
+            LOGD(ERROR, domid, "failed to resume device model:%d", rc);
+            goto out;
+        }
+    }
+
+    if (xc_domain_resume(CTX->xch, domid, suspend_cancel)) {
+        LOGED(ERROR, domid, "xc_domain_resume failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (!xs_resume_domain(CTX->xsh, domid)) {
+        LOGED(ERROR, domid, "xs_resume_domain failed");
+        rc = ERROR_FAIL;
+    }
+out:
+    return rc;
+}
+
+static void dm_resume_init(libxl__dm_resume_state *dmrs)
+{
+    libxl__ev_qmp_init(&dmrs->qmp);
+    libxl__ev_time_init(&dmrs->time);
+    libxl__ev_xswatch_init(&dmrs->watch);
+}
+
+static void dm_resume_dispose(libxl__gc *gc,
+                              libxl__dm_resume_state *dmrs)
+{
+    libxl__ev_qmp_dispose(gc, &dmrs->qmp);
+    libxl__ev_time_deregister(gc, &dmrs->time);
+    libxl__ev_xswatch_deregister(gc, &dmrs->watch);
+}
+
+static void dm_resume_xswatch_cb(libxl__egc *egc,
+    libxl__ev_xswatch *, const char *watch_path, const char *);
+static void dm_resume_qmp_done(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *, int rc);
+static void dm_resume_timeout(libxl__egc *egc,
+    libxl__ev_time *, const struct timeval *, int rc);
+static void dm_resume_done(libxl__egc *egc,
+    libxl__dm_resume_state *dmrs, int rc);
+
+void libxl__dm_resume(libxl__egc *egc,
+                      libxl__dm_resume_state *dmrs)
+{
+    STATE_AO_GC(dmrs->ao);
+    int rc = 0;
+
+    /* Convenience aliases */
+    libxl_domid domid = dmrs->domid;
+    libxl__ev_qmp *qmp = &dmrs->qmp;
+
+    dm_resume_init(dmrs);
+
+    rc = libxl__ev_time_register_rel(dmrs->ao,
+                                     &dmrs->time,
+                                     dm_resume_timeout,
+                                     LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
+    if (rc) goto out;
+
+    switch (libxl__device_model_version_running(gc, domid)) {
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
+        uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
+        const char *path, *state;
+
+        path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &state);
+        if (rc) goto out;
+        if (!state || strcmp(state, "paused")) {
+            /* already running */
+            rc = 0;
+            goto out;
+        }
+
+        rc = libxl__qemu_traditional_cmd(gc, domid, "continue");
+        if (rc) goto out;
+        rc = libxl__ev_xswatch_register(gc, &dmrs->watch,
+                                        dm_resume_xswatch_cb,
+                                        path);
+        if (rc) goto out;
+        break;
+    }
+    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+        qmp->ao = dmrs->ao;
+        qmp->domid = domid;
+        qmp->callback = dm_resume_qmp_done;
+        qmp->payload_fd = -1;
+        rc = libxl__ev_qmp_send(egc, qmp, "cont", NULL);
+        if (rc) goto out;
+        break;
+    default:
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    return;
+
+out:
+    dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_xswatch_cb(libxl__egc *egc,
+                                 libxl__ev_xswatch *xsw,
+                                 const char *watch_path,
+                                 const char *event_path)
+{
+    EGC_GC;
+    libxl__dm_resume_state *dmrs = CONTAINER_OF(xsw, *dmrs, watch);
+    int rc;
+    const char *value;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL, watch_path, &value);
+    if (rc) goto out;
+
+    if (!value || strcmp(value, "running"))
+        return;
+
+    rc = 0;
+out:
+    dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_qmp_done(libxl__egc *egc,
+                               libxl__ev_qmp *qmp,
+                               const libxl__json_object *response,
+                               int rc)
+{
+    libxl__dm_resume_state *dmrs = CONTAINER_OF(qmp, *dmrs, qmp);
+    dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_timeout(libxl__egc *egc,
+                              libxl__ev_time *ev,
+                              const struct timeval *requested_abs,
+                              int rc)
+{
+    libxl__dm_resume_state *dmrs = CONTAINER_OF(ev, *dmrs, time);
+    dm_resume_done(egc, dmrs, rc);
+}
+
+static void dm_resume_done(libxl__egc *egc,
+                           libxl__dm_resume_state *dmrs,
+                           int rc)
+{
+    EGC_GC;
+
+    if (rc) {
+        LOGD(ERROR, dmrs->domid,
+             "Failed to resume device model: rc=%d", rc);
+    }
+
+    dm_resume_dispose(gc, dmrs);
+    dmrs->dm_resumed_callback(egc, dmrs, rc);
+}
+
+
+static void domain_resume_done(libxl__egc *egc,
+                               libxl__dm_resume_state *dmrs, int rc);
+
+void libxl__domain_resume(libxl__egc *egc,
+                          libxl__dm_resume_state *dmrs,
+                          bool suspend_cancel)
+{
+    STATE_AO_GC(dmrs->ao);
+    int rc = 0;
+    libxl_domain_type type = libxl__domain_type(gc, dmrs->domid);
+
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (type != LIBXL_DOMAIN_TYPE_HVM) {
+        rc = 0;
+        goto out;
+    }
+
+    dmrs->suspend_cancel = suspend_cancel;
+    dmrs->dm_resumed_callback = domain_resume_done;
+    libxl__dm_resume(egc, dmrs); /* must be last */
+    return;
+
+out:
+    domain_resume_done(egc, dmrs, rc);
+}
+
+static void domain_resume_done(libxl__egc *egc,
+                               libxl__dm_resume_state *dmrs, int rc)
+{
+    EGC_GC;
+
+    /* Convenience aliases */
+    libxl_domid domid = dmrs->domid;
+
+    if (rc) goto out;
+
+    if (xc_domain_resume(CTX->xch, domid, dmrs->suspend_cancel)) {
+        LOGED(ERROR, domid, "xc_domain_resume failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (!xs_resume_domain(CTX->xsh, domid)) {
+        LOGED(ERROR, domid, "xs_resume_domain failed");
+        rc = ERROR_FAIL;
+    }
+out:
+    dmrs->callback(egc, dmrs, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_domain.c b/tools/libs/light/libxl_domain.c
new file mode 100644 (file)
index 0000000..5d4ec90
--- /dev/null
@@ -0,0 +1,2462 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+#define PAGE_TO_MEMKB(pages) ((pages) * 4)
+
+int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
+                         const char *old_name, const char *new_name,
+                         xs_transaction_t trans)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *dom_path = 0;
+    const char *name_path;
+    char *got_old_name;
+    unsigned int got_old_len;
+    xs_transaction_t our_trans = 0;
+    uint32_t stub_dm_domid;
+    const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL;
+    int rc;
+    libxl_dominfo info;
+    char *uuid;
+    const char *vm_name_path;
+
+    libxl_dominfo_init(&info);
+
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path) goto x_nomem;
+
+    name_path= GCSPRINTF("%s/name", dom_path);
+    if (!name_path) goto x_nomem;
+
+    stub_dm_domid = libxl_get_stubdom_id(CTX, domid);
+    if (stub_dm_domid) {
+        stub_dm_old_name = libxl__stub_dm_name(gc, old_name);
+        stub_dm_new_name = libxl__stub_dm_name(gc, new_name);
+    }
+
+ retry_transaction:
+    if (!trans) {
+        trans = our_trans = xs_transaction_start(ctx->xsh);
+        if (!our_trans) {
+            LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name");
+            goto x_fail;
+        }
+    }
+
+    if (!new_name) {
+        LOGD(ERROR, domid, "New domain name not specified");
+        rc = ERROR_INVAL;
+        goto x_rc;
+    }
+
+    if (new_name[0]) {
+        /* nonempty names must be unique */
+        uint32_t domid_e;
+        rc = libxl_name_to_domid(ctx, new_name, &domid_e);
+        if (rc == ERROR_INVAL) {
+            /* no such domain, good */
+        } else if (rc != 0) {
+            LOGD(ERROR, domid, "Unexpected error checking for existing domain");
+            goto x_rc;
+        } else if (domid_e == domid) {
+            /* domain already has this name, ok (but we do still
+             * need the rest of the code as we may need to check
+             * old_name, for example). */
+        } else {
+            LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name);
+            rc = ERROR_INVAL;
+            goto x_rc;
+        }
+    }
+
+    if (old_name) {
+        got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len);
+        if (!got_old_name) {
+            LOGEVD(ERROR, errno, domid,
+                   "Check old name for domain allegedly named `%s'",
+                   old_name);
+            goto x_fail;
+        }
+        if (strcmp(old_name, got_old_name)) {
+            LOGD(ERROR, domid,
+                 "Allegedly named `%s' is actually named `%s' - racing ?",
+                 old_name,
+                 got_old_name);
+            free(got_old_name);
+            goto x_fail;
+        }
+        free(got_old_name);
+    }
+    if (!xs_write(ctx->xsh, trans, name_path,
+                  new_name, strlen(new_name))) {
+        LOGD(ERROR, domid,
+             "Failed to write new name `%s'"
+             " for domain previously named `%s'",
+             new_name,
+             old_name);
+        goto x_fail;
+    }
+
+    /* update /vm/<uuid>/name */
+    rc = libxl_domain_info(ctx, &info, domid);
+    if (rc)
+        goto x_rc;
+
+    uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
+    vm_name_path = GCSPRINTF("/vm/%s/name", uuid);
+    if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name))
+        goto x_fail;
+
+    if (stub_dm_domid) {
+        rc = libxl__domain_rename(gc, stub_dm_domid,
+                                  stub_dm_old_name,
+                                  stub_dm_new_name,
+                                  trans);
+        if (rc) {
+            LOGED(ERROR, domid, "Unable to rename stub-domain");
+            goto x_rc;
+        }
+    }
+
+    if (our_trans) {
+        if (!xs_transaction_end(ctx->xsh, our_trans, 0)) {
+            trans = our_trans = 0;
+            if (errno != EAGAIN) {
+                LOGD(ERROR, domid,
+                     "Failed to commit new name `%s'"
+                     " for domain previously named `%s'",
+                     new_name,
+                     old_name);
+                goto x_fail;
+            }
+            LOGD(DEBUG, domid,
+                 "Need to retry rename transaction"
+                 " for domain (name_path=\"%s\", new_name=\"%s\")",
+                 name_path,
+                 new_name);
+            goto retry_transaction;
+        }
+        our_trans = 0;
+    }
+
+    rc = 0;
+ x_rc:
+    if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1);
+    libxl_dominfo_dispose(&info);
+    return rc;
+
+ x_fail:  rc = ERROR_FAIL;  goto x_rc;
+ x_nomem: rc = ERROR_NOMEM; goto x_rc;
+}
+
+int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
+                        const char *old_name, const char *new_name)
+{
+    GC_INIT(ctx);
+    int rc;
+    rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL);
+    GC_FREE;
+    return rc;
+}
+
+static void domain_resume_done(libxl__egc *egc,
+                               libxl__dm_resume_state *,
+                               int rc);
+
+int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
+                        const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__dm_resume_state *dmrs;
+
+    GCNEW(dmrs);
+    dmrs->ao = ao;
+    dmrs->domid = domid;
+    dmrs->callback = domain_resume_done;
+    libxl__domain_resume(egc, dmrs, suspend_cancel);
+    return AO_INPROGRESS;
+}
+
+static void domain_resume_done(libxl__egc *egc,
+                               libxl__dm_resume_state *dmrs,
+                               int rc)
+{
+    STATE_AO_GC(dmrs->ao);
+    libxl__ao_complete(egc, ao, rc);
+}
+
+/*
+ * Preserves a domain but rewrites xenstore etc to make it unique so
+ * that the domain can be restarted.
+ *
+ * Does not modify info so that it may be reused.
+ */
+int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
+                          libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
+{
+    GC_INIT(ctx);
+    struct xs_permissions roperm[2];
+    xs_transaction_t t;
+    char *preserved_name;
+    char *uuid_string;
+    char *vm_path;
+    char *dom_path;
+
+    int rc;
+
+    preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
+    if (!preserved_name) {
+        GC_FREE;
+        return ERROR_NOMEM;
+    }
+
+    uuid_string = libxl__uuid2string(gc, new_uuid);
+    if (!uuid_string) {
+        GC_FREE;
+        return ERROR_NOMEM;
+    }
+
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path) {
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+
+    vm_path = GCSPRINTF("/vm/%s", uuid_string);
+    if (!vm_path) {
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+
+    roperm[0].id = 0;
+    roperm[0].perms = XS_PERM_NONE;
+    roperm[1].id = domid;
+    roperm[1].perms = XS_PERM_READ;
+
+ retry_transaction:
+    t = xs_transaction_start(ctx->xsh);
+
+    xs_rm(ctx->xsh, t, vm_path);
+    xs_mkdir(ctx->xsh, t, vm_path);
+    xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
+
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
+    rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
+    if (rc) {
+        GC_FREE;
+        return rc;
+    }
+
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
+
+    if (!xs_transaction_end(ctx->xsh, t, 0))
+        if (errno == EAGAIN)
+            goto retry_transaction;
+
+    GC_FREE;
+    return 0;
+}
+
+void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
+                          const xc_domaininfo_t *xcinfo,
+                          libxl_dominfo *xlinfo)
+{
+    size_t size;
+
+    memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
+    xlinfo->domid = xcinfo->domain;
+    xlinfo->ssidref = xcinfo->ssidref;
+    if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
+                                   &xlinfo->ssid_label, &size) < 0)
+        xlinfo->ssid_label = NULL;
+
+    xlinfo->dying      = !!(xcinfo->flags&XEN_DOMINF_dying);
+    xlinfo->shutdown   = !!(xcinfo->flags&XEN_DOMINF_shutdown);
+    xlinfo->paused     = !!(xcinfo->flags&XEN_DOMINF_paused);
+    xlinfo->blocked    = !!(xcinfo->flags&XEN_DOMINF_blocked);
+    xlinfo->running    = !!(xcinfo->flags&XEN_DOMINF_running);
+    xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain);
+
+    if (xlinfo->shutdown)
+        xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
+    else
+        xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
+
+    xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
+    xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
+    xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
+    xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
+    xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
+    xlinfo->cpu_time = xcinfo->cpu_time;
+    xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
+    xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
+    xlinfo->cpupool = xcinfo->cpupool;
+    xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
+        LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
+}
+
+libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
+{
+    libxl_dominfo *ptr = NULL;
+    int i, ret;
+    xc_domaininfo_t *info;
+    int size = 0;
+    uint32_t domid = 0;
+    GC_INIT(ctx);
+
+    GCNEW_ARRAY(info, 1024);
+
+    while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) {
+        ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo));
+        for (i = 0; i < ret; i++) {
+            libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]);
+        }
+        domid = info[ret - 1].domain + 1;
+        size += ret;
+    }
+
+    if (ret < 0) {
+        LOGE(ERROR, "getting domain info list");
+        free(ptr);
+        GC_FREE;
+        return NULL;
+    }
+
+    *nb_domain_out = size;
+    GC_FREE;
+    return ptr;
+}
+
+int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
+                      uint32_t domid) {
+    xc_domaininfo_t xcinfo;
+    int ret;
+    GC_INIT(ctx);
+
+    ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo);
+    if (ret<0) {
+        LOGED(ERROR, domid, "Getting domain info list");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    if (ret==0 || xcinfo.domain != domid) {
+        GC_FREE;
+        return ERROR_DOMAIN_NOTFOUND;
+    }
+
+    if (info_r)
+        libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r);
+    GC_FREE;
+    return 0;
+}
+
+/* this API call only list VM running on this host. A VM can
+ * be an aggregate of multiple domains. */
+libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
+{
+    GC_INIT(ctx);
+    libxl_dominfo *info;
+    libxl_vminfo *ptr = NULL;
+    int idx, i, n_doms;
+
+    info = libxl_list_domain(ctx, &n_doms);
+    if (!info)
+        goto out;
+
+    /*
+     * Always make sure to allocate at least one element; if we don't and we
+     * request zero, libxl__calloc (might) think its internal call to calloc
+     * has failed (if it returns null), if so it would kill our process.
+     */
+    ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo));
+
+    for (idx = i = 0; i < n_doms; i++) {
+        if (libxl_is_stubdom(ctx, info[i].domid, NULL))
+            continue;
+        ptr[idx].uuid = info[i].uuid;
+        ptr[idx].domid = info[i].domid;
+
+        idx++;
+    }
+    *nb_vm_out = idx;
+    libxl_dominfo_list_free(info, n_doms);
+
+out:
+    GC_FREE;
+    return ptr;
+}
+
+static void remus_failover_cb(libxl__egc *egc,
+                              libxl__domain_save_state *dss, int rc);
+
+int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
+                             uint32_t domid, int send_fd, int recv_fd,
+                             const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__domain_save_state *dss;
+    int rc;
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* The caller must set this defbool */
+    if (libxl_defbool_is_default(info->colo)) {
+        LOGD(ERROR, domid, "Colo mode must be enabled/disabled");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl_defbool_setdefault(&info->allow_unsafe, false);
+    libxl_defbool_setdefault(&info->blackhole, false);
+    libxl_defbool_setdefault(&info->compression,
+                             !libxl_defbool_val(info->colo));
+    libxl_defbool_setdefault(&info->netbuf, true);
+    libxl_defbool_setdefault(&info->diskbuf, true);
+
+    if (libxl_defbool_val(info->colo) &&
+        libxl_defbool_val(info->compression)) {
+            LOGD(ERROR, domid, "Cannot use memory checkpoint "
+                        "compression in COLO mode");
+            rc = ERROR_FAIL;
+            goto out;
+    }
+
+    if (!libxl_defbool_val(info->allow_unsafe) &&
+        (libxl_defbool_val(info->blackhole) ||
+         !libxl_defbool_val(info->netbuf) ||
+         !libxl_defbool_val(info->diskbuf))) {
+        LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null,"
+                    "disable network buffering and disk replication");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+
+    GCNEW(dss);
+    dss->ao = ao;
+    dss->callback = remus_failover_cb;
+    dss->domid = domid;
+    dss->fd = send_fd;
+    dss->recv_fd = recv_fd;
+    dss->type = type;
+    dss->live = 1;
+    dss->debug = 0;
+    dss->remus = info;
+    if (libxl_defbool_val(info->colo))
+        dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO;
+    else
+        dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS;
+
+    assert(info);
+
+    /* Point of no return */
+    if (libxl_defbool_val(info->colo))
+        libxl__colo_save_setup(egc, &dss->css);
+    else
+        libxl__remus_setup(egc, &dss->rs);
+    return AO_INPROGRESS;
+
+ out:
+    return AO_CREATE_FAIL(rc);
+}
+
+static void remus_failover_cb(libxl__egc *egc,
+                              libxl__domain_save_state *dss, int rc)
+{
+    STATE_AO_GC(dss->ao);
+    /*
+     * With Remus, if we reach this point, it means either
+     * backup died or some network error occurred preventing us
+     * from sending checkpoints.
+     */
+    libxl__ao_complete(egc, ao, rc);
+}
+
+static void domain_suspend_cb(libxl__egc *egc,
+                              libxl__domain_save_state *dss, int rc)
+{
+    STATE_AO_GC(dss->ao);
+    int flrc;
+
+    flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
+    /* If suspend has failed already then report that error not this one. */
+    if (flrc && !rc) rc = flrc;
+
+    libxl__ao_complete(egc,ao,rc);
+
+}
+
+int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out_err;
+    }
+
+    libxl__domain_save_state *dss;
+    GCNEW(dss);
+
+    dss->ao = ao;
+    dss->callback = domain_suspend_cb;
+
+    dss->domid = domid;
+    dss->fd = fd;
+    dss->type = type;
+    dss->live = flags & LIBXL_SUSPEND_LIVE;
+    dss->debug = flags & LIBXL_SUSPEND_DEBUG;
+    dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
+
+    rc = libxl__fd_flags_modify_save(gc, dss->fd,
+                                     ~(O_NONBLOCK|O_NDELAY), 0,
+                                     &dss->fdfl);
+    if (rc < 0) goto out_err;
+
+    libxl__domain_save(egc, dss);
+    return AO_INPROGRESS;
+
+ out_err:
+    return AO_CREATE_FAIL(rc);
+}
+
+static void domain_suspend_empty_cb(libxl__egc *egc,
+                              libxl__domain_suspend_state *dss, int rc)
+{
+    STATE_AO_GC(dss->ao);
+    libxl__ao_complete(egc,ao,rc);
+}
+
+int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
+                              const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__domain_suspend_state *dsps;
+    int rc;
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out_err;
+    }
+
+    GCNEW(dsps);
+    dsps->ao = ao;
+    dsps->domid = domid;
+    dsps->type = type;
+    rc = libxl__domain_suspend_init(egc, dsps, type);
+    if (rc < 0) goto out_err;
+    dsps->callback_common_done = domain_suspend_empty_cb;
+    libxl__domain_suspend(egc, dsps);
+    return AO_INPROGRESS;
+
+ out_err:
+    return AO_CREATE_FAIL(rc);
+}
+
+int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
+                       const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int r;
+    r = xc_domain_pause(ctx->xch, domid);
+    if (r < 0) {
+        LOGED(ERROR, domid, "Pausing domain");
+        return AO_CREATE_FAIL(ERROR_FAIL);
+    }
+    libxl__ao_complete(egc, ao, 0);
+    return AO_INPROGRESS;
+}
+
+int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
+                           const char *filename,
+                           const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int ret, rc;
+
+    ret = xc_domain_dumpcore(ctx->xch, domid, filename);
+    if (ret<0) {
+        LOGED(ERROR, domid, "Core dumping domain to %s", filename);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+out:
+
+    libxl__ao_complete(egc, ao, rc);
+
+    return AO_INPROGRESS;
+}
+
+int libxl__domain_unpause_deprecated(libxl__gc *gc, libxl_domid domid)
+{
+    int r, rc;
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (type == LIBXL_DOMAIN_TYPE_HVM) {
+        rc = libxl__domain_resume_device_model_deprecated(gc, domid);
+        if (rc < 0) {
+            LOGD(ERROR, domid,
+                 "Failed to unpause device model for domain: %d", rc);
+            goto out;
+        }
+    }
+    r = xc_domain_unpause(CTX->xch, domid);
+    if (r < 0) {
+        LOGED(ERROR, domid, "Unpausing domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = 0;
+out:
+    return rc;
+}
+
+static void domain_unpause_done(libxl__egc *egc,
+                                libxl__dm_resume_state *,
+                                int rc);
+
+void libxl__domain_unpause(libxl__egc *egc,
+                           libxl__dm_resume_state *dmrs)
+{
+    STATE_AO_GC(dmrs->ao);
+    int rc = 0;
+
+    /* Convenience aliases */
+    libxl_domid domid = dmrs->domid;
+
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (type == LIBXL_DOMAIN_TYPE_HVM) {
+        dmrs->dm_resumed_callback = domain_unpause_done;
+        libxl__dm_resume(egc, dmrs); /* must be last */
+        return;
+    }
+    rc = 0;
+out:
+    domain_unpause_done(egc, dmrs, rc);
+}
+
+static void domain_unpause_done(libxl__egc *egc,
+                                libxl__dm_resume_state *dmrs,
+                                int rc)
+{
+    EGC_GC;
+    int r;
+
+    /* Convenience aliases */
+    libxl_domid domid = dmrs->domid;
+
+    if (rc) goto out;
+
+    r = xc_domain_unpause(CTX->xch, domid);
+    if (r < 0) {
+        LOGED(ERROR, domid, "Unpausing domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = 0;
+out:
+    dmrs->callback(egc, dmrs, rc);
+}
+
+static void domain_unpause_ao_done(libxl__egc *egc,
+                                   libxl__dm_resume_state *,
+                                   int rc);
+
+int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__dm_resume_state *dmrs;
+
+    GCNEW(dmrs);
+    dmrs->ao = ao;
+    dmrs->domid = domid;
+    dmrs->callback = domain_unpause_ao_done;
+    libxl__domain_unpause(egc, dmrs); /* must be last */
+    return AO_INPROGRESS;
+}
+
+static void domain_unpause_ao_done(libxl__egc *egc,
+                                   libxl__dm_resume_state *dmrs,
+                                   int rc)
+{
+    STATE_AO_GC(dmrs->ao);
+
+    libxl__ao_complete(egc, ao, rc);
+}
+
+int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+
+    uint64_t pvdriver = 0;
+    int ret;
+
+    libxl_domain_type domtype = libxl__domain_type(gc, domid);
+    if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
+        return ERROR_FAIL;
+
+    if (domtype != LIBXL_DOMAIN_TYPE_HVM)
+        return 1;
+
+    ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
+    if (ret<0) {
+        LOGED(ERROR, domid, "Getting HVM callback IRQ");
+        return ERROR_FAIL;
+    }
+    return !!pvdriver;
+}
+
+const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
+{
+    const char *dom_path;
+
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path)
+        return NULL;
+
+    return GCSPRINTF("%s/control/shutdown", dom_path);
+}
+
+char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
+                                    uint32_t domid)
+{
+    const char *shutdown_path;
+
+    shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
+    if (!shutdown_path)
+        return NULL;
+
+    return libxl__xs_read(gc, t, shutdown_path);
+}
+
+int libxl__domain_pvcontrol(libxl__egc *egc, libxl__xswait_state *pvcontrol,
+                            domid_t domid, const char *cmd)
+{
+    STATE_AO_GC(pvcontrol->ao);
+    const char *shutdown_path;
+    int rc;
+
+    rc = libxl__domain_pvcontrol_available(gc, domid);
+    if (rc < 0)
+        return rc;
+
+    if (!rc)
+        return ERROR_NOPARAVIRT;
+
+    shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
+    if (!shutdown_path)
+        return ERROR_FAIL;
+
+    rc = libxl__xs_printf(gc, XBT_NULL, shutdown_path, "%s", cmd);
+    if (rc)
+        return rc;
+
+    pvcontrol->path = shutdown_path;
+    pvcontrol->what = GCSPRINTF("guest acknowledgement of %s request", cmd);
+    pvcontrol->timeout_ms = 60 * 1000;
+    rc = libxl__xswait_start(gc, pvcontrol);
+    if (rc)
+        return rc;
+
+    return 0;
+}
+
+static bool pvcontrol_acked(const char *state)
+{
+    if (!state || !strcmp(state,""))
+        return true;
+
+    return false;
+}
+
+/* Xenstore watch callback prototype for the reboot/poweroff operations. */
+static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
+                         const char *state);
+
+int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
+                          const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__xswait_state *pvcontrol;
+    int rc;
+
+    GCNEW(pvcontrol);
+    pvcontrol->ao = ao;
+    pvcontrol->callback = pvcontrol_cb;
+    rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "poweroff");
+
+    return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
+}
+
+int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
+                        const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__xswait_state *pvcontrol;
+    int rc;
+
+    GCNEW(pvcontrol);
+    pvcontrol->ao = ao;
+    pvcontrol->callback = pvcontrol_cb;
+    rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "reboot");
+
+    return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
+}
+
+static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
+                         const char *state)
+{
+    STATE_AO_GC(xswa->ao);
+
+    if (!rc && !pvcontrol_acked(state))
+        return;
+
+    libxl__xswait_stop(gc, xswa);
+
+    if (rc)
+        LOG(ERROR, "guest didn't acknowledge control request: %d", rc);
+
+    libxl__ao_complete(egc, ao, rc);
+}
+
+static void domain_death_occurred(libxl__egc *egc,
+                                  libxl_evgen_domain_death **evg_upd,
+                                  const char *why) {
+    /* Removes **evg_upd from death_list and puts it on death_reported
+     * and advances *evg_upd to the next entry.
+     * Call sites in domain_death_xswatch_callback must use "continue". */
+    EGC_GC;
+    libxl_evgen_domain_death *const evg = *evg_upd;
+
+    LOGD(DEBUG, evg->domid, "%s", why);
+
+    libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry);
+    *evg_upd = evg_next;
+
+    libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
+
+    libxl__event_occurred(egc, ev);
+
+    evg->death_reported = 1;
+    LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
+    LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
+}
+
+static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
+                                        const char *wpath, const char *epath) {
+    EGC_GC;
+    libxl_evgen_domain_death *evg;
+    int rc;
+
+    CTX_LOCK;
+
+    evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
+
+    for (;;) {
+        if (!evg) goto out;
+
+        int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
+        xc_domaininfo_t domaininfos[nentries];
+        const xc_domaininfo_t *got = domaininfos, *gotend;
+
+        rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
+        if (rc == -1) {
+            LIBXL__EVENT_DISASTER(gc, "xc_domain_getinfolist failed while"
+                                  " processing @releaseDomain watch event",
+                                  errno, 0);
+            goto out;
+        }
+        gotend = &domaininfos[rc];
+
+        LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld",
+             evg, nentries, rc,
+             rc>0 ? (long)domaininfos[0].domain : 0,
+             rc>0 ? (long)domaininfos[rc-1].domain : 0);
+
+        for (;;) {
+            if (!evg) {
+                LOG(DEBUG, "[evg=0] all reported");
+                goto all_reported;
+            }
+
+            LOGD(DEBUG, evg->domid, "[evg=%p]"
+                 "   got=domaininfos[%d] got->domain=%ld",
+                 evg, (int)(got - domaininfos),
+                 got < gotend ? (long)got->domain : -1L);
+
+            if (!rc) {
+                domain_death_occurred(egc, &evg, "empty list");
+                continue;
+            }
+
+            if (got == gotend) {
+                LOG(DEBUG, " got==gotend");
+                break;
+            }
+
+            if (got->domain > evg->domid) {
+                /* ie, the list doesn't contain evg->domid any more so
+                 * the domain has been destroyed */
+                domain_death_occurred(egc, &evg, "missing from list");
+                continue;
+            }
+
+            if (got->domain < evg->domid) {
+                got++;
+                continue;
+            }
+
+            assert(evg->domid == got->domain);
+            LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x",
+                 evg->shutdown_reported, got->flags);
+
+            if (got->flags & XEN_DOMINF_dying) {
+                domain_death_occurred(egc, &evg, "dying");
+                continue;
+            }
+
+            if (!evg->shutdown_reported &&
+                (got->flags & XEN_DOMINF_shutdown)) {
+                libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
+                                            got->domain, evg->user);
+
+                LOG(DEBUG, " shutdown reporting");
+
+                ev->u.domain_shutdown.shutdown_reason =
+                    (got->flags >> XEN_DOMINF_shutdownshift) &
+                    XEN_DOMINF_shutdownmask;
+                libxl__event_occurred(egc, ev);
+
+                evg->shutdown_reported = 1;
+            }
+            evg = LIBXL_TAILQ_NEXT(evg, entry);
+        }
+
+        assert(rc); /* rc==0 results in us eating all evgs and quitting */
+    }
+ all_reported:
+ out:
+
+    LOG(DEBUG, "domain death search done");
+
+    CTX_UNLOCK;
+}
+
+int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
+                libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
+    GC_INIT(ctx);
+    libxl_evgen_domain_death *evg, *evg_search;
+    int rc;
+
+    CTX_LOCK;
+
+    evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
+    memset(evg, 0, sizeof(*evg));
+    evg->domid = domid;
+    evg->user = user;
+
+    LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
+                              evg->domid > evg_search->domid);
+
+    if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
+        rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
+                        domain_death_xswatch_callback, "@releaseDomain");
+        if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
+    }
+
+    *evgen_out = evg;
+    rc = 0;
+
+ out:
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+};
+
+void libxl__evdisable_domain_death(libxl__gc *gc,
+                                   libxl_evgen_domain_death *evg) {
+    CTX_LOCK;
+
+    if (!evg->death_reported)
+        LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
+    else
+        LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
+
+    free(evg);
+
+    if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
+        libxl__ev_xswatch_isregistered(&CTX->death_watch))
+        libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
+
+    CTX_UNLOCK;
+}
+
+void libxl_evdisable_domain_death(libxl_ctx *ctx,
+                                  libxl_evgen_domain_death *evg) {
+    GC_INIT(ctx);
+    libxl__evdisable_domain_death(gc, evg);
+    GC_FREE;
+}
+
+/* Callbacks for libxl_domain_destroy */
+
+static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
+                              int rc);
+
+int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__domain_destroy_state *dds;
+
+    GCNEW(dds);
+    dds->ao = ao;
+    dds->domid = domid;
+    dds->callback = domain_destroy_cb;
+    libxl__domain_destroy(egc, dds);
+
+    return AO_INPROGRESS;
+}
+
+static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
+                              int rc)
+{
+    STATE_AO_GC(dds->ao);
+
+    if (rc)
+        LOGD(ERROR, dds->domid, "Destruction of domain failed");
+
+    libxl__ao_complete(egc, ao, rc);
+}
+
+/* Callbacks for libxl__domain_destroy */
+
+static void stubdom_destroy_callback(libxl__egc *egc,
+                                     libxl__destroy_domid_state *dis,
+                                     int rc);
+
+static void domain_destroy_callback(libxl__egc *egc,
+                                    libxl__destroy_domid_state *dis,
+                                    int rc);
+
+static void destroy_finish_check(libxl__egc *egc,
+                                 libxl__domain_destroy_state *dds);
+
+void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
+{
+    STATE_AO_GC(dds->ao);
+    uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
+
+    if (stubdomid) {
+        dds->stubdom.ao = ao;
+        dds->stubdom.domid = stubdomid;
+        dds->stubdom.callback = stubdom_destroy_callback;
+        dds->stubdom.soft_reset = false;
+        libxl__destroy_domid(egc, &dds->stubdom);
+    } else {
+        dds->stubdom_finished = 1;
+    }
+
+    dds->domain.ao = ao;
+    dds->domain.domid = dds->domid;
+    dds->domain.callback = domain_destroy_callback;
+    dds->domain.soft_reset = dds->soft_reset;
+    libxl__destroy_domid(egc, &dds->domain);
+}
+
+static void stubdom_destroy_callback(libxl__egc *egc,
+                                     libxl__destroy_domid_state *dis,
+                                     int rc)
+{
+    STATE_AO_GC(dis->ao);
+    libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
+    const char *savefile;
+
+    if (rc) {
+        LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u",
+             dis->domid);
+        dds->rc = rc;
+    }
+
+    dds->stubdom_finished = 1;
+    savefile = libxl__device_model_savefile(gc, dis->domid);
+    rc = libxl__remove_file(gc, savefile);
+    if (rc) {
+        LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s",
+             savefile);
+    }
+
+    destroy_finish_check(egc, dds);
+}
+
+static void domain_destroy_callback(libxl__egc *egc,
+                                    libxl__destroy_domid_state *dis,
+                                    int rc)
+{
+    STATE_AO_GC(dis->ao);
+    libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
+
+    if (rc) {
+        LOGD(ERROR, dis->domid, "Unable to destroy guest");
+        dds->rc = rc;
+    }
+
+    dds->domain_finished = 1;
+    destroy_finish_check(egc, dds);
+}
+
+static void destroy_finish_check(libxl__egc *egc,
+                                 libxl__domain_destroy_state *dds)
+{
+    if (!(dds->domain_finished && dds->stubdom_finished))
+        return;
+
+    dds->callback(egc, dds, dds->rc);
+}
+
+/* Callbacks for libxl__destroy_domid */
+static void destroy_domid_pci_done(libxl__egc *egc,
+                                   libxl__multidev *multidev,
+                                   int rc);
+static void dm_destroy_cb(libxl__egc *egc,
+                          libxl__destroy_devicemodel_state *ddms,
+                          int rc);
+
+static void devices_destroy_cb(libxl__egc *egc,
+                               libxl__devices_remove_state *drs,
+                               int rc);
+
+static void domain_destroy_domid_cb(libxl__egc *egc,
+                                    libxl__ev_child *destroyer,
+                                    pid_t pid, int status);
+
+void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
+{
+    STATE_AO_GC(dis->ao);
+    uint32_t domid = dis->domid;
+    int rc;
+
+    libxl__ev_child_init(&dis->destroyer);
+
+    rc = libxl_domain_info(CTX, NULL, domid);
+    switch(rc) {
+    case 0:
+        break;
+    case ERROR_DOMAIN_NOTFOUND:
+        LOGD(ERROR, domid, "Non-existant domain");
+    default:
+        goto out;
+    }
+
+    libxl__multidev_begin(ao, &dis->multidev);
+    dis->multidev.callback = destroy_domid_pci_done;
+    libxl__device_pci_destroy_all(egc, domid, &dis->multidev);
+    libxl__multidev_prepared(egc, &dis->multidev, 0);
+    return;
+
+out:
+    assert(rc);
+    dis->callback(egc, dis, rc);
+}
+
+static void destroy_domid_pci_done(libxl__egc *egc,
+                                   libxl__multidev *multidev,
+                                   int rc)
+{
+    STATE_AO_GC(multidev->ao);
+    libxl__destroy_domid_state *dis =
+        CONTAINER_OF(multidev, *dis, multidev);
+    int dm_present;
+    int r;
+
+    /* Convenience aliases */
+    libxl_domid domid = dis->domid;
+
+    if (rc) {
+        LOGD(ERROR, domid, "Pci shutdown failed");
+        goto out;
+    }
+
+    r = xc_domain_pause(CTX->xch, domid);
+    if (r < 0) {
+        LOGEVD(ERROR, r, domid, "xc_domain_pause failed");
+        rc = ERROR_FAIL;
+    }
+
+    switch (libxl__domain_type(gc, domid)) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        if (libxl_get_stubdom_id(CTX, domid)) {
+            dm_present = 0;
+            break;
+        }
+        /* fall through */
+    case LIBXL_DOMAIN_TYPE_PVH:
+    case LIBXL_DOMAIN_TYPE_PV:
+        dm_present = libxl__dm_active(gc, domid);
+        break;
+    case LIBXL_DOMAIN_TYPE_INVALID:
+        rc = ERROR_FAIL;
+        goto out;
+    default:
+        abort();
+    }
+
+    if (dm_present) {
+        dis->ddms.ao = ao;
+        dis->ddms.domid = domid;
+        dis->ddms.callback = dm_destroy_cb;
+
+        libxl__destroy_device_model(egc, &dis->ddms);
+        return;
+    } else {
+        dm_destroy_cb(egc, &dis->ddms, 0);
+        return;
+    }
+
+out:
+    assert(rc);
+    dis->callback(egc, dis, rc);
+    return;
+}
+
+static void dm_destroy_cb(libxl__egc *egc,
+                          libxl__destroy_devicemodel_state *ddms,
+                          int rc)
+{
+    libxl__destroy_domid_state *dis = CONTAINER_OF(ddms, *dis, ddms);
+    STATE_AO_GC(dis->ao);
+    uint32_t domid = dis->domid;
+    uint32_t target_domid;
+
+    if (rc < 0)
+        LOGD(ERROR, domid, "libxl__destroy_device_model failed");
+
+    if (libxl_is_stubdom(CTX, domid, &target_domid) &&
+        libxl__stubdomain_is_linux_running(gc, target_domid)) {
+        char *path = GCSPRINTF("/local/domain/%d/image/qmp-proxy-pid", domid);
+
+        libxl__kill_xs_path(gc, path, "QMP Proxy");
+        /* qmp-proxy for stubdom registers target_domid's QMP sockets. */
+        libxl__qmp_cleanup(gc, target_domid);
+    }
+
+    dis->drs.ao = ao;
+    dis->drs.domid = domid;
+    dis->drs.callback = devices_destroy_cb;
+    dis->drs.force.flag = LIBXL__FORCE_ON;
+    libxl__devices_destroy(egc, &dis->drs);
+}
+
+static unsigned int libxl__get_domid_reuse_timeout(void)
+{
+    const char *env_timeout = getenv("LIBXL_DOMID_REUSE_TIMEOUT");
+
+    return env_timeout ? strtol(env_timeout, NULL, 0) :
+        LIBXL_DOMID_REUSE_TIMEOUT;
+}
+
+char *libxl__domid_history_path(libxl__gc *gc, const char *suffix)
+{
+    return GCSPRINTF("%s/domid-history%s", libxl__run_dir_path(),
+                     suffix ?: "");
+}
+
+int libxl_clear_domid_history(libxl_ctx *ctx)
+{
+    GC_INIT(ctx);
+    char *path;
+    int rc = ERROR_FAIL;
+
+    path = libxl__domid_history_path(gc, NULL);
+    if (!path)
+        goto out;
+
+    if (unlink(path) < 0 && errno != ENOENT) {
+        LOGE(ERROR, "failed to remove '%s'\n", path);
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+struct libxl__domid_history {
+    long timeout;
+    char *path;
+    FILE *f;
+    struct timespec ts;
+};
+
+static void libxl__domid_history_dispose(
+    struct libxl__domid_history *ctxt)
+{
+    if (ctxt->f) {
+        fclose(ctxt->f);
+        ctxt->f = NULL;
+    }
+}
+
+static int libxl__open_domid_history(libxl__gc *gc,
+                                     struct libxl__domid_history *ctxt)
+{
+    ctxt->timeout = libxl__get_domid_reuse_timeout();
+    ctxt->path = libxl__domid_history_path(gc, NULL);
+
+    ctxt->f = fopen(ctxt->path, "r");
+    if (!ctxt->f && errno != ENOENT) {
+        LOGE(ERROR, "failed to open '%s'", ctxt->path);
+        return ERROR_FAIL;
+    }
+
+    if (clock_gettime(CLOCK_MONOTONIC, &ctxt->ts)) {
+        LOGE(ERROR, "failed to get time");
+        libxl__domid_history_dispose(ctxt);
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+static int libxl__close_domid_history(libxl__gc *gc,
+                                      struct libxl__domid_history *ctxt)
+{
+    int r;
+
+    if (!ctxt->f) return 0;
+
+    r = fclose(ctxt->f);
+    ctxt->f = NULL;
+    if (r == EOF) {
+        LOGE(ERROR, "failed to close '%s'", ctxt->path);
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+static int libxl__read_recent(libxl__gc *gc,
+                              struct libxl__domid_history *ctxt,
+                              unsigned long *sec, unsigned int *domid)
+{
+    if (!ctxt->f) {
+        *domid = INVALID_DOMID;
+        return 0;
+    }
+
+    for (;;) {
+        int r = fscanf(ctxt->f, "%lu %u", sec, domid);
+
+        if (r == EOF) {
+            if (ferror(ctxt->f)) {
+                LOGE(ERROR, "failed to read from '%s'", ctxt->path);
+                return ERROR_FAIL;
+            }
+
+            *domid = INVALID_DOMID;
+            break;
+        } else if (r == 2 && libxl_domid_valid_guest(*domid) &&
+                   ctxt->ts.tv_sec - *sec <= ctxt->timeout) {
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static int libxl__mark_domid_recent(libxl__gc *gc, uint32_t domid)
+{
+    libxl__flock *lock;
+    struct libxl__domid_history ctxt = {};
+    char *new;
+    FILE *nf = NULL;
+    int r, rc;
+
+    lock = libxl__lock_domid_history(gc);
+    if (!lock) {
+        LOGED(ERROR, domid, "failed to acquire lock");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__open_domid_history(gc, &ctxt);
+    if (rc) goto out;
+
+    new = libxl__domid_history_path(gc, ".new");
+    nf = fopen(new, "a");
+    if (!nf) {
+        LOGED(ERROR, domid, "failed to open '%s'", new);
+        goto out;
+    }
+
+    for (;;) {
+        unsigned long sec;
+        unsigned int val;
+
+        rc = libxl__read_recent(gc, &ctxt, &sec, &val);
+        if (rc) goto out;
+
+        if (val == INVALID_DOMID) /* EOF */
+            break;
+
+        r = fprintf(nf, "%lu %u\n", sec, val);
+        if (r < 0) {
+            LOGED(ERROR, domid, "failed to write to '%s'", new);
+            goto out;
+        }
+    }
+
+    r = fprintf(nf, "%lu %u\n", ctxt.ts.tv_sec, domid);
+    if (r < 0) {
+        LOGED(ERROR, domid, "failed to write to '%s'", new);
+        goto out;
+    }
+
+    r = fclose(nf);
+    nf = NULL;
+    if (r == EOF) {
+        LOGED(ERROR, domid, "failed to close '%s'", new);
+        goto out;
+    }
+
+    rc = libxl__close_domid_history(gc, &ctxt);
+    if (rc) goto out;
+
+    r = rename(new, ctxt.path);
+    if (r) {
+        LOGE(ERROR, "failed to rename '%s' -> '%s'", new, ctxt.path);
+        return ERROR_FAIL;
+    }
+
+out:
+    if (nf) fclose(nf);
+    libxl__domid_history_dispose(&ctxt);
+    if (lock) libxl__unlock_file(lock);
+
+    return rc;
+}
+
+int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent)
+{
+    struct libxl__domid_history ctxt = {};
+    int rc;
+
+    rc = libxl__open_domid_history(gc, &ctxt);
+    if (rc) goto out;
+
+    *recent = false;
+    for (;;) {
+        unsigned long sec;
+        unsigned int val;
+
+        rc = libxl__read_recent(gc, &ctxt, &sec, &val);
+        if (rc) goto out;
+
+        if (val == INVALID_DOMID) /* EOF */
+            break;
+
+        if (val == domid && ctxt.ts.tv_sec - sec <= ctxt.timeout) {
+            *recent = true;
+            break;
+        }
+    }
+
+    rc = libxl__close_domid_history(gc, &ctxt);
+
+out:
+    libxl__domid_history_dispose(&ctxt);
+
+    return rc;
+}
+
+static void devices_destroy_cb(libxl__egc *egc,
+                               libxl__devices_remove_state *drs,
+                               int rc)
+{
+    STATE_AO_GC(drs->ao);
+    libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
+    libxl_ctx *ctx = CTX;
+    uint32_t domid = dis->domid;
+    char *dom_path;
+    char *vm_path;
+    libxl__flock *lock;
+
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (rc < 0)
+        LOGD(ERROR, domid, "libxl__devices_destroy failed");
+
+    vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
+    if (vm_path)
+        if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
+            LOGED(ERROR, domid, "xs_rm failed for %s", vm_path);
+
+    if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
+        LOGED(ERROR, domid, "xs_rm failed for %s", dom_path);
+
+    xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
+    xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
+
+    /* This is async operation, we already hold CTX lock */
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+    libxl__userdata_destroyall(gc, domid);
+
+    libxl__unlock_file(lock);
+
+    /* Clean up qemu-save and qemu-resume files. They are
+     * intermediate files created by libxc. Unfortunately they
+     * don't fit in existing userdata scheme very well. In soft reset
+     * case we need to keep the file.
+     */
+    if (!dis->soft_reset) {
+        rc = libxl__remove_file(gc,
+                                libxl__device_model_savefile(gc, domid));
+        if (rc < 0) goto out;
+    }
+    rc = libxl__remove_file(gc,
+             GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
+    if (rc < 0) goto out;
+
+    rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
+    if (rc < 0) goto out;
+    if (!rc) { /* child */
+        ctx->xch = xc_interface_open(ctx->lg,0,0);
+        if (!ctx->xch) goto badchild;
+
+        if (!dis->soft_reset) {
+            rc = libxl__mark_domid_recent(gc, domid);
+            if (rc) goto badchild;
+            rc = xc_domain_destroy(ctx->xch, domid);
+        } else {
+            rc = xc_domain_pause(ctx->xch, domid);
+            if (rc < 0) goto badchild;
+            rc = xc_domain_soft_reset(ctx->xch, domid);
+            if (rc < 0) goto badchild;
+            rc = xc_domain_unpause(ctx->xch, domid);
+        }
+        if (rc < 0) goto badchild;
+        _exit(0);
+
+    badchild:
+        if (errno > 0  && errno < 126) {
+            _exit(errno);
+        } else {
+            LOGED(ERROR, domid,
+ "xc_domain_destroy failed (with difficult errno value %d)",
+                  errno);
+            _exit(-1);
+        }
+    }
+    LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc);
+
+    return;
+
+out:
+    dis->callback(egc, dis, rc);
+    return;
+}
+
+static void domain_destroy_domid_cb(libxl__egc *egc,
+                                    libxl__ev_child *destroyer,
+                                    pid_t pid, int status)
+{
+    libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
+    STATE_AO_GC(dis->ao);
+    int rc;
+
+    if (status) {
+        if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
+            LOGEVD(ERROR, WEXITSTATUS(status), dis->domid,
+                   "xc_domain_destroy failed");
+        } else {
+            libxl_report_child_exitstatus(CTX, XTL_ERROR,
+                                          "async domain destroy", pid, status);
+        }
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = 0;
+
+ out:
+    dis->callback(egc, dis, rc);
+}
+
+int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
+{
+    int rc;
+    const char *xs_domid;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
+    if (rc) goto out;
+    if (!xs_domid) {
+        LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    *domid = atoi(xs_domid);
+
+out:
+    return rc;
+}
+
+int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
+{
+    if (!name)
+        return 0;
+    return libxl_domain_qualifier_to_domid(CTX, name, domid);
+}
+
+libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
+                                       int *nr_vcpus_out, int *nr_cpus_out)
+{
+    GC_INIT(ctx);
+    libxl_vcpuinfo *ptr, *ret;
+    xc_domaininfo_t domaininfo;
+    xc_vcpuinfo_t vcpuinfo;
+
+    if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) {
+        LOGED(ERROR, domid, "Getting infolist");
+        GC_FREE;
+        return NULL;
+    }
+
+    if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
+        GC_FREE;
+        return NULL;
+    }
+
+    *nr_cpus_out = libxl_get_max_cpus(ctx);
+    ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
+                              sizeof(libxl_vcpuinfo));
+
+    for (*nr_vcpus_out = 0;
+         *nr_vcpus_out <= domaininfo.max_vcpu_id;
+         ++*nr_vcpus_out, ++ptr) {
+        libxl_bitmap_init(&ptr->cpumap);
+        if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
+            goto err;
+        libxl_bitmap_init(&ptr->cpumap_soft);
+        if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
+            goto err;
+        if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) {
+            LOGED(ERROR, domid, "Getting vcpu info");
+            goto err;
+        }
+
+        if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out,
+                                ptr->cpumap.map, ptr->cpumap_soft.map,
+                                XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
+            LOGED(ERROR, domid, "Getting vcpu affinity");
+            goto err;
+        }
+        ptr->vcpuid = *nr_vcpus_out;
+        ptr->cpu = vcpuinfo.cpu;
+        ptr->online = !!vcpuinfo.online;
+        ptr->blocked = !!vcpuinfo.blocked;
+        ptr->running = !!vcpuinfo.running;
+        ptr->vcpu_time = vcpuinfo.cpu_time;
+    }
+    GC_FREE;
+    return ret;
+
+err:
+    libxl_bitmap_dispose(&ptr->cpumap);
+    libxl_bitmap_dispose(&ptr->cpumap_soft);
+    free(ret);
+    GC_FREE;
+    return NULL;
+}
+
+static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
+                                          const libxl_bitmap *cpumap,
+                                          const libxl_dominfo *info)
+{
+    char *dompath;
+    xs_transaction_t t;
+    int i, rc = ERROR_FAIL;
+
+    if (!(dompath = libxl__xs_get_dompath(gc, domid)))
+        goto out;
+
+retry_transaction:
+    t = xs_transaction_start(CTX->xsh);
+    for (i = 0; i <= info->vcpu_max_id; i++)
+        libxl__xs_printf(gc, t,
+                         GCSPRINTF("%s/cpu/%u/availability", dompath, i),
+                         "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
+    if (!xs_transaction_end(CTX->xsh, t, 0)) {
+        if (errno == EAGAIN)
+            goto retry_transaction;
+    } else
+        rc = 0;
+out:
+    return rc;
+}
+
+static int qmp_parse_query_cpus(libxl__gc *gc,
+                                libxl_domid domid,
+                                const libxl__json_object *response,
+                                libxl_bitmap *const map)
+{
+    int i;
+    const libxl__json_object *cpu;
+
+    libxl_bitmap_set_none(map);
+    /* Parse response to QMP command "query-cpus":
+     * [ { 'CPU': 'int',...} ]
+     */
+    for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
+        unsigned int cpu_index;
+        const libxl__json_object *o;
+
+        o = libxl__json_map_get("CPU", cpu, JSON_INTEGER);
+        if (!o) {
+            LOGD(ERROR, domid, "Failed to retrieve CPU index.");
+            return ERROR_QEMU_API;
+        }
+
+        cpu_index = libxl__json_object_get_integer(o);
+        libxl_bitmap_set(map, cpu_index);
+    }
+
+    return 0;
+}
+
+typedef struct set_vcpuonline_state {
+    libxl__ev_qmp qmp;
+    libxl__ev_time timeout;
+    const libxl_bitmap *cpumap;
+    libxl_dominfo info;
+    libxl_bitmap final_map;
+    int index; /* for loop on final_map */
+} set_vcpuonline_state;
+
+static void set_vcpuonline_qmp_cpus_queried(libxl__egc *,
+    libxl__ev_qmp *, const libxl__json_object *, int rc);
+static void set_vcpuonline_qmp_add_cpu(libxl__egc *,
+    libxl__ev_qmp *, const libxl__json_object *response, int rc);
+static void set_vcpuonline_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void set_vcpuonline_done(libxl__egc *egc,
+    set_vcpuonline_state *svos, int rc);
+
+int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
+                         libxl_bitmap *cpumap,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc, maxcpus;
+    set_vcpuonline_state *svos;
+
+    GCNEW(svos);
+    libxl__ev_qmp_init(&svos->qmp);
+    svos->qmp.ao = ao;
+    svos->qmp.domid = domid;
+    svos->qmp.payload_fd = -1;
+    libxl__ev_time_init(&svos->timeout);
+    svos->cpumap = cpumap;
+    libxl_dominfo_init(&svos->info);
+    libxl_bitmap_init(&svos->final_map);
+
+    /* Convenience aliases */
+    libxl_dominfo *info = &svos->info;
+    libxl__ev_qmp *qmp = &svos->qmp;
+
+    rc = libxl_domain_info(CTX, info, domid);
+    if (rc < 0) {
+        LOGED(ERROR, domid, "Getting domain info list");
+        goto out;
+    }
+
+    maxcpus = libxl_bitmap_count_set(cpumap);
+    if (maxcpus == 0)
+    {
+        LOGED(ERROR, domid, "Requested 0 VCPUs!");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (maxcpus > info->vcpu_max_id + 1)
+    {
+        LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!",
+              maxcpus, info->vcpu_max_id + 1);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    switch (libxl__domain_type(gc, domid)) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        switch (libxl__device_model_version_running(gc, domid)) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            break;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            rc = libxl__ev_time_register_rel(ao, &svos->timeout,
+                                             set_vcpuonline_timeout,
+                                             LIBXL_QMP_CMD_TIMEOUT * 1000);
+            if (rc) goto out;
+            qmp->callback = set_vcpuonline_qmp_cpus_queried;
+            rc = libxl__ev_qmp_send(egc, qmp, "query-cpus", NULL);
+            if (rc) goto out;
+            return AO_INPROGRESS;
+        default:
+            rc = ERROR_INVAL;
+        }
+        break;
+    case LIBXL_DOMAIN_TYPE_PVH:
+    case LIBXL_DOMAIN_TYPE_PV:
+        break;
+    default:
+        rc = ERROR_INVAL;
+    }
+
+out:
+    set_vcpuonline_done(egc, svos, rc); /* must be last */
+    return AO_INPROGRESS;
+}
+
+static void set_vcpuonline_qmp_cpus_queried(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
+    int i;
+    libxl_bitmap current_map;
+
+    /* Convenience aliases */
+    libxl_bitmap *final_map = &svos->final_map;
+
+    libxl_bitmap_init(&current_map);
+
+    if (rc) goto out;
+
+    libxl_bitmap_alloc(CTX, &current_map, svos->info.vcpu_max_id + 1);
+    rc = qmp_parse_query_cpus(gc, qmp->domid, response, &current_map);
+    if (rc) goto out;
+
+    libxl_bitmap_copy_alloc(CTX, final_map, svos->cpumap);
+
+    libxl_for_each_set_bit(i, current_map) {
+        libxl_bitmap_reset(final_map, i);
+    }
+
+out:
+    libxl_bitmap_dispose(&current_map);
+    svos->index = -1;
+    set_vcpuonline_qmp_add_cpu(egc, qmp, NULL, rc); /* must be last */
+}
+
+static void set_vcpuonline_qmp_add_cpu(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
+{
+    STATE_AO_GC(qmp->ao);
+    set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
+    libxl__json_object *args = NULL;
+
+    /* Convenience aliases */
+    libxl_bitmap *map = &svos->final_map;
+
+    if (rc) goto out;
+
+    while (libxl_bitmap_cpu_valid(map, ++svos->index)) {
+        if (libxl_bitmap_test(map, svos->index)) {
+            qmp->callback = set_vcpuonline_qmp_add_cpu;
+            libxl__qmp_param_add_integer(gc, &args, "id", svos->index);
+            rc = libxl__ev_qmp_send(egc, qmp, "cpu-add", args);
+            if (rc) goto out;
+            return;
+        }
+    }
+
+out:
+    set_vcpuonline_done(egc, svos, rc);
+}
+
+static void set_vcpuonline_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                                   const struct timeval *requested_abs,
+                                   int rc)
+{
+    EGC_GC;
+    set_vcpuonline_state *svos = CONTAINER_OF(ev, *svos, timeout);
+
+    if (rc == ERROR_TIMEDOUT)
+        LOGD(ERROR, svos->qmp.domid,
+             "Setting CPU online in QEMU timed out");
+
+    set_vcpuonline_done(egc, svos, rc);
+}
+
+static void set_vcpuonline_done(libxl__egc *egc,
+                                set_vcpuonline_state *svos,
+                                int rc)
+{
+    STATE_AO_GC(svos->qmp.ao);
+
+    /* Convenience aliases */
+    libxl_domid domid = svos->qmp.domid;
+
+    if (!rc)
+        rc = libxl__set_vcpuonline_xenstore(gc, domid, svos->cpumap,
+                                            &svos->info);
+
+    libxl_bitmap_dispose(&svos->final_map);
+    libxl_dominfo_dispose(&svos->info);
+    libxl__ev_time_deregister(gc, &svos->timeout);
+    libxl__ev_qmp_dispose(gc, &svos->qmp);
+    libxl__ao_complete(egc, ao, rc);
+}
+
+static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
+                                  const libxl__json_object *response,
+                                  int rc);
+
+static void domain_s3_resume(libxl__ao *ao, libxl__egc *egc, int domid)
+{
+    AO_GC;
+    libxl__ev_qmp *qmp;
+    int rc = 0;
+    int r;
+
+    GCNEW(qmp);
+    libxl__ev_qmp_init(qmp);
+    qmp->ao = ao;
+    qmp->domid = domid;
+    qmp->payload_fd = -1;
+    qmp->callback = domain_s3_resume_done;
+
+    switch (libxl__domain_type(gc, domid)) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        switch (libxl__device_model_version_running(gc, domid)) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            r = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
+            if (r) {
+                LOGED(ERROR, domid, "Send trigger '%s' failed",
+                      libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME));
+                rc = ERROR_FAIL;
+            }
+            break;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            rc = libxl__ev_qmp_send(egc, qmp, "system_wakeup", NULL);
+            if (rc) goto out;
+            return;
+        default:
+            rc = ERROR_INVAL;
+            break;
+        }
+        break;
+    default:
+        rc = ERROR_INVAL;
+        break;
+    }
+
+out:
+    domain_s3_resume_done(egc, qmp, NULL, rc);
+}
+
+static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
+                                  const libxl__json_object *response,
+                                  int rc)
+{
+    EGC_GC;
+
+    if (rc)
+        LOGD(ERROR, qmp->domid, "Send trigger '%s' failed, rc=%d",
+              libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME), rc);
+
+    libxl__ev_qmp_dispose(gc, qmp);
+    libxl__ao_complete(egc, qmp->ao, rc);
+}
+
+int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
+                       libxl_trigger trigger, uint32_t vcpuid,
+                       const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    switch (trigger) {
+    case LIBXL_TRIGGER_POWER:
+        rc = xc_domain_send_trigger(ctx->xch, domid,
+                                    XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
+        break;
+    case LIBXL_TRIGGER_SLEEP:
+        rc = xc_domain_send_trigger(ctx->xch, domid,
+                                    XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
+        break;
+    case LIBXL_TRIGGER_NMI:
+        rc = xc_domain_send_trigger(ctx->xch, domid,
+                                    XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
+        break;
+    case LIBXL_TRIGGER_INIT:
+        rc = xc_domain_send_trigger(ctx->xch, domid,
+                                    XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
+        break;
+    case LIBXL_TRIGGER_RESET:
+        rc = xc_domain_send_trigger(ctx->xch, domid,
+                                    XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
+        break;
+    case LIBXL_TRIGGER_S3RESUME:
+        domain_s3_resume(ao, egc, domid); /* must be last */
+        return AO_INPROGRESS;
+    default:
+        rc = -1;
+        errno = EINVAL;
+        break;
+    }
+
+    if (rc != 0) {
+        LOGED(ERROR, domid, "Send trigger '%s' failed",
+              libxl_trigger_to_string(trigger));
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+out:
+    return AO_CREATE_FAIL(rc);
+}
+
+uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
+{
+    GC_INIT(ctx);
+    char *dompath = libxl__xs_get_dompath(gc, domid);
+    char *vm_path, *start_time;
+    uint32_t ret;
+
+    vm_path = libxl__xs_read(
+        gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
+    start_time = libxl__xs_read(
+        gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
+    if (start_time == NULL) {
+        LOGEVD(ERROR, -1, domid, "Can't get start time of domain");
+        ret = -1;
+    }else{
+        ret = strtoul(start_time, NULL, 10);
+    }
+    GC_FREE;
+    return ret;
+}
+
+static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid,
+                                              unsigned int max_vcpus,
+                                              libxl_bitmap *map)
+{
+    int rc;
+    unsigned int i;
+    const char *dompath;
+
+    dompath = libxl__xs_get_dompath(gc, domid);
+    if (!dompath) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    for (i = 0; i < max_vcpus; i++) {
+        const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i);
+        const char *content;
+        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content);
+        if (rc) goto out;
+        if (content && !strcmp(content, "online"))
+            libxl_bitmap_set(map, i);
+    }
+
+    rc = 0;
+out:
+    return rc;
+}
+
+typedef struct {
+    libxl__ev_qmp qmp;
+    libxl__ev_time timeout;
+    libxl_domain_config *d_config; /* user pointer */
+    libxl__ev_slowlock devlock;
+    libxl_bitmap qemuu_cpus;
+} retrieve_domain_configuration_state;
+
+static void retrieve_domain_configuration_lock_acquired(
+    libxl__egc *egc, libxl__ev_slowlock *, int rc);
+static void retrieve_domain_configuration_cpu_queried(
+    libxl__egc *egc, libxl__ev_qmp *qmp,
+    const libxl__json_object *response, int rc);
+static void retrieve_domain_configuration_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void retrieve_domain_configuration_end(libxl__egc *egc,
+    retrieve_domain_configuration_state *rdcs, int rc);
+
+int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
+                                        libxl_domain_config *d_config,
+                                        const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    retrieve_domain_configuration_state *rdcs;
+
+    GCNEW(rdcs);
+    libxl__ev_qmp_init(&rdcs->qmp);
+    rdcs->qmp.ao = ao;
+    rdcs->qmp.domid = domid;
+    rdcs->qmp.payload_fd = -1;
+    libxl__ev_time_init(&rdcs->timeout);
+    rdcs->d_config = d_config;
+    libxl_bitmap_init(&rdcs->qemuu_cpus);
+    libxl__ev_devlock_init(&rdcs->devlock);
+    rdcs->devlock.ao = ao;
+    rdcs->devlock.domid = domid;
+    rdcs->devlock.callback = retrieve_domain_configuration_lock_acquired;
+    libxl__ev_slowlock_lock(egc, &rdcs->devlock);
+    return AO_INPROGRESS;
+}
+
+static void retrieve_domain_configuration_lock_acquired(
+    libxl__egc *egc, libxl__ev_slowlock *devlock, int rc)
+{
+    retrieve_domain_configuration_state *rdcs =
+        CONTAINER_OF(devlock, *rdcs, devlock);
+    STATE_AO_GC(rdcs->qmp.ao);
+    libxl__flock *lock = NULL;
+    bool has_callback = false;
+
+    /* Convenience aliases */
+    libxl_domid domid = rdcs->qmp.domid;
+    libxl_domain_config *const d_config = rdcs->d_config;
+
+    if (rc) goto out;
+
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    rc = libxl__get_domain_configuration(gc, domid, d_config);
+    if (rc) {
+        LOGD(ERROR, domid, "Fail to get domain configuration");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl__unlock_file(lock);
+    lock = NULL;
+
+    /* We start by querying QEMU, if it is running, for its cpumap as this
+     * is a long operation. */
+    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM &&
+        libxl__device_model_version_running(gc, domid) ==
+            LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+        /* For QEMU upstream we always need to provide the number
+         * of cpus present to QEMU whether they are online or not;
+         * otherwise QEMU won't accept the saved state.
+         */
+        rc = libxl__ev_time_register_rel(ao, &rdcs->timeout,
+            retrieve_domain_configuration_timeout,
+            LIBXL_QMP_CMD_TIMEOUT * 1000);
+        if (rc) goto out;
+        libxl_bitmap_alloc(CTX, &rdcs->qemuu_cpus,
+                           d_config->b_info.max_vcpus);
+        rdcs->qmp.callback = retrieve_domain_configuration_cpu_queried;
+        rc = libxl__ev_qmp_send(egc, &rdcs->qmp, "query-cpus", NULL);
+        if (rc) goto out;
+        has_callback = true;
+    }
+
+out:
+    if (lock) libxl__unlock_file(lock);
+    if (!has_callback)
+        retrieve_domain_configuration_end(egc, rdcs, rc);
+}
+
+static void retrieve_domain_configuration_cpu_queried(
+    libxl__egc *egc, libxl__ev_qmp *qmp,
+    const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    retrieve_domain_configuration_state *rdcs =
+        CONTAINER_OF(qmp, *rdcs, qmp);
+
+    if (rc) goto out;
+
+    rc = qmp_parse_query_cpus(gc, qmp->domid, response, &rdcs->qemuu_cpus);
+
+out:
+    retrieve_domain_configuration_end(egc, rdcs, rc);
+}
+
+static void retrieve_domain_configuration_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+    retrieve_domain_configuration_state *rdcs =
+        CONTAINER_OF(ev, *rdcs, timeout);
+
+    retrieve_domain_configuration_end(egc, rdcs, rc);
+}
+
+static void retrieve_domain_configuration_end(libxl__egc *egc,
+    retrieve_domain_configuration_state *rdcs, int rc)
+{
+    STATE_AO_GC(rdcs->qmp.ao);
+    libxl__flock *lock = NULL;
+
+    /* Convenience aliases */
+    libxl_domain_config *const d_config = rdcs->d_config;
+    libxl_domid domid = rdcs->qmp.domid;
+
+    if (rc) goto out;
+
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    /* Domain name */
+    {
+        char *domname;
+        domname = libxl_domid_to_name(CTX, domid);
+        if (!domname) {
+            LOGD(ERROR, domid, "Fail to get domain name");
+            goto out;
+        }
+        free(d_config->c_info.name);
+        d_config->c_info.name = domname; /* steals allocation */
+    }
+
+    /* Domain UUID */
+    {
+        libxl_dominfo info;
+        libxl_dominfo_init(&info);
+        rc = libxl_domain_info(CTX, &info, domid);
+        if (rc) {
+            LOGD(ERROR, domid, "Fail to get domain info");
+            libxl_dominfo_dispose(&info);
+            goto out;
+        }
+        libxl_uuid_copy(CTX, &d_config->c_info.uuid, &info.uuid);
+        libxl_dominfo_dispose(&info);
+    }
+
+    /* VCPUs */
+    {
+        libxl_bitmap *map = &d_config->b_info.avail_vcpus;
+        unsigned int max_vcpus = d_config->b_info.max_vcpus;
+        libxl_device_model_version version;
+
+        libxl_bitmap_dispose(map);
+        libxl_bitmap_init(map);
+        libxl_bitmap_alloc(CTX, map, max_vcpus);
+        libxl_bitmap_set_none(map);
+
+        switch (d_config->b_info.type) {
+        case LIBXL_DOMAIN_TYPE_HVM:
+            version = libxl__device_model_version_running(gc, domid);
+            assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN);
+            switch (version) {
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+                libxl_bitmap_copy(CTX, map, &rdcs->qemuu_cpus);
+                break;
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+                rc = libxl__update_avail_vcpus_xenstore(gc, domid,
+                                                        max_vcpus, map);
+                break;
+            default:
+                abort();
+            }
+            break;
+        case LIBXL_DOMAIN_TYPE_PVH:
+        case LIBXL_DOMAIN_TYPE_PV:
+            rc = libxl__update_avail_vcpus_xenstore(gc, domid,
+                                                    max_vcpus, map);
+            break;
+        default:
+            abort();
+        }
+
+        if (rc) {
+            LOGD(ERROR, domid, "Fail to update available cpu map");
+            goto out;
+        }
+    }
+
+
+    /* Memory limits:
+     *
+     * Currently there are three memory limits:
+     *  1. "target" in xenstore (originally memory= in config file)
+     *  2. "static-max" in xenstore (originally maxmem= in config file)
+     *  3. "max_memkb" in hypervisor
+     *
+     * The third one is not visible and currently managed by
+     * toolstack. In order to rebuild a domain we only need to have
+     * "target" and "static-max".
+     */
+    {
+        uint64_t target_memkb = 0, max_memkb = 0;
+
+        /* "target" */
+        rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
+        if (rc) {
+            LOGD(ERROR, domid, "Fail to get memory target");
+            goto out;
+        }
+
+        /* libxl__get_targetmem_fudge() calculates the difference from
+         * what is in xenstore to what we have in the domain build info.
+         */
+        d_config->b_info.target_memkb = target_memkb +
+            libxl__get_targetmem_fudge(gc, &d_config->b_info);
+
+        d_config->b_info.max_memkb = max_memkb;
+    }
+
+    /* Scheduler params */
+    {
+        libxl_domain_sched_params_dispose(&d_config->b_info.sched_params);
+        rc = libxl_domain_sched_params_get(CTX, domid,
+                                           &d_config->b_info.sched_params);
+        if (rc) {
+            LOGD(ERROR, domid, "Fail to get scheduler parameters");
+            goto out;
+        }
+    }
+
+    /* Devices: disk, nic, vtpm, pcidev etc. */
+
+    /* The MERGE macro implements following logic:
+     * 0. retrieve JSON (done by now)
+     * 1. retrieve list of device from xenstore
+     * 2. use xenstore entries as primary reference and compare JSON
+     *    entries with them.
+     *    a. if a device is present in xenstore and in JSON, merge the
+     *       two views.
+     *    b. if a device is not present in xenstore but in JSON, delete
+     *       it from the result.
+     *    c. it's impossible to have an entry present in xenstore but
+     *       not in JSON, because we maintain an invariant that every
+     *       entry in xenstore must have a corresponding entry in JSON.
+     * 3. "merge" operates on "src" and "dst". "src" points to the
+     *    entry retrieved from xenstore while "dst" points to the entry
+     *    retrieve from JSON.
+     */
+    {
+        const libxl__device_type *dt;
+        int idx;
+
+        for (idx = 0;; idx++) {
+            void *p = NULL;
+            void **devs;
+            int i, j, num;
+            int *num_dev;
+
+            dt = device_type_tbl[idx];
+            if (!dt)
+                break;
+
+            if (!dt->compare)
+                continue;
+
+            num_dev = libxl__device_type_get_num(dt, d_config);
+            p = libxl__device_list(gc, dt, domid, &num);
+            if (p == NULL) {
+                LOGD(DEBUG, domid, "No %s from xenstore",
+                     libxl__device_kind_to_string(dt->type));
+            }
+            devs = libxl__device_type_get_ptr(dt, d_config);
+
+            for (i = 0; i < *num_dev; i++) {
+                void *q;
+
+                q = libxl__device_type_get_elem(dt, d_config, i);
+                for (j = 0; j < num; j++) {
+                    if (dt->compare(p + dt->dev_elem_size * j, q))
+                        break;
+                }
+
+                if (j < num) {         /* found in xenstore */
+                    if (dt->merge)
+                        dt->merge(CTX, p + dt->dev_elem_size * j, q);
+                } else {                /* not found in xenstore */
+                    LOGD(WARN, domid,
+                         "Device present in JSON but not in xenstore, ignored");
+
+                    dt->dispose(q);
+
+                    for (j = i; j < *num_dev - 1; j++)
+                        memcpy(libxl__device_type_get_elem(dt, d_config, j),
+                               libxl__device_type_get_elem(dt, d_config, j+1),
+                               dt->dev_elem_size);
+
+                    /* rewind counters */
+                    (*num_dev)--;
+                    i--;
+
+                    *devs = libxl__realloc(NOGC, *devs,
+                                           dt->dev_elem_size * *num_dev);
+                }
+            }
+
+            for (i = 0; i < num; i++)
+                dt->dispose(p + dt->dev_elem_size * i);
+            free(p);
+        }
+    }
+
+out:
+    libxl__ev_slowlock_unlock(gc, &rdcs->devlock);
+    if (lock) libxl__unlock_file(lock);
+    libxl_bitmap_dispose(&rdcs->qemuu_cpus);
+    libxl__ev_qmp_dispose(gc, &rdcs->qmp);
+    libxl__ev_time_deregister(gc, &rdcs->timeout);
+    libxl__ao_complete(egc, ao, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_event.c b/tools/libs/light/libxl_event.c
new file mode 100644 (file)
index 0000000..7c5387e
--- /dev/null
@@ -0,0 +1,2467 @@
+/*
+ * Copyright (C) 2011      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+/*
+ * Internal event machinery for use by other parts of libxl
+ */
+
+#include <poll.h>
+
+#include "libxl_internal.h"
+
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+# define LIBXL__DBG_LOG(ctx, args, ...) \
+    LIBXL__LOG((ctx), XTL_DEBUG, args, __VA_ARGS__)
+#else
+# define LIBXL__DBG_LOG(ctx, args, ...) ((void)0)
+#endif
+#define DBG(args, ...) LIBXL__DBG_LOG(CTX, args, __VA_ARGS__)
+
+
+static libxl__ao *ao_nested_root(libxl__ao *ao);
+
+static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao);
+
+
+/*
+ * osevent update baton handling
+ *
+ * We need the following property (the "unstale liveness property"):
+ *
+ * Whenever any thread is blocking as a result of being given an fd
+ * set or timeout by libxl, at least one thread must be using an up to
+ * date osevent set.  It is OK for all but one threads to have stale
+ * event sets, because so long as one waiting thread has the right
+ * event set, any actually interesting event will, if nothing else,
+ * wake that "right" thread up.  It will then make some progress
+ * and/or, if it exits, ensure that some other thread becomes the
+ * "right" thread.
+ *
+ * For threads blocking outside libxl and which are receiving libxl's
+ * fd and timeout information via the libxl_osevent_hooks callbacks,
+ * libxl calls this function as soon as it becomes interested.  It is
+ * the responsiblity of a provider of these functions in a
+ * multithreaded environment to make arrangements to wake up event
+ * waiting thread(s) with stale event sets.
+ *
+ * Waiters outside libxl using _beforepoll are dealt with below.
+ *
+ * For the libxl event loop, the argument is as follows:
+ *
+ * The issue we are concerned about is libxl sleeping on an out of
+ * date fd set, or too long a timeout, so that it doesn't make
+ * progress.  If the property above is satisfied, then if any thread
+ * is waiting in libxl at least one such thread will be waiting on a
+ * sufficient osevent set, so any relevant osevent will wake up a
+ * libxl thread which will either handle the event, or arrange that at
+ * least one other libxl thread has the right set.
+ *
+ * There are two calls to poll in libxl: one is the fd recheck, which
+ * is not blocking.  There is only the one blocking call, in
+ * eventloop_iteration.  poll runs with the ctx unlocked, so osevents
+ * might be added after it unlocks the ctx - that is what we are
+ * worried about.
+ *
+ * To demonstrate that the unstale liveness property is satisfied:
+ *
+ * We define a baton holder as follows: a libxl thread is a baton
+ * holder if
+ *   (a) it has an egc or an ao and holds the ctx lock, or
+ *   (b) it has an active non-app poller and no osevents have been
+ *       added since it released the lock, or
+ *   (c) it has an active non-app poller which has been woken
+ *       (by writing to its pipe), so it will not sleep
+ * We will maintain the invariant (the "baton invariant") that
+ * whenever there is any active poller, there is at least
+ * one baton holder.  ("non-app" means simply "not poller_app".)
+ *
+ * No thread outside libxl can have an active non-app poller: pollers
+ * are put on the active list by poller_get which is called in three
+ * places: libxl_event_wait, which puts it before returning;
+ * libxl__ao_create but only in the synchronous case, in which case
+ * the poller is put before returning; and the poller_app, during
+ * initialisation.
+ *
+ * So any time when all libxl threads are blocking (and therefore do
+ * not have the ctx lock), the non-app active pollers belong to those
+ * threads.  If at least one is a baton holder (the invariant), that
+ * thread has a good enough event set.
+ *
+ * Now we will demonstrate that the "baton invariant" is maintained:
+ *
+ * The rule is that any thread which might be the baton holder is
+ * responsible for checking that there continues to be a baton holder
+ * as needed.
+ *
+ * Firstly, consider the case when the baton holders (b) cease to be
+ * baton holders because osevents are added.
+ *
+ * There are only two kinds of osevents: timeouts and fds.  Every
+ * other internal event source reduces to one of these eventually.
+ * Both of these cases are handled (in the case of fd events, add and
+ * modify, separately), calling pollers_note_osevent_added.
+ *
+ * This walks the poller_active list, marking the active pollers
+ * osevents_added=1.  Such a poller cannot be the baton holder.  But
+ * pollers_note_osevent_added is called only from ev_* functions,
+ * which are only called from event-chain libxl code: ie, code with an
+ * ao or an egc.  So at this point we are a baton holder, and there is
+ * still a baton holder.
+ *
+ * Secondly, consider the case where baton holders (a) cease to be
+ * batton holders because they dispose of their egc or ao.  We call
+ * libxl__egc_ao_cleanup_1_baton on every exit path.  We arrange that
+ * everything that disposes of an egc or an ao checks that there is a
+ * new baton holder by calling libxl__egc_ao_cleanup_1_baton.
+ *
+ * This function handles the invariant explicitly: if we have any
+ * non-app active pollers it looks for one which is up to date (baton
+ * holder category (b)), and failing that it picks a victim to turn
+ * into the baton holder category (c) by waking it up.  (Correctness
+ * depends on this function not spotting its own thread as the
+ * baton-holder, since it is on its way to not being the baton-holder,
+ * so it must be called after the poller has been put back.)
+ *
+ * Thirdly, we must consider the case (c).  A thread in category (c)
+ * will reenter libxl when it gains the lock and necessarily then
+ * becomes a baton holder in category (a).
+ *
+ * So the "baton invariant" is maintained.
+ * QED (for waiters in libxl).
+ *
+ *
+ * For waiters outside libxl which used libxl_osevent_beforepoll
+ * to get the fd set:
+ *
+ * As above, adding an osevent involves having an egc or an ao.
+ * It sets poller->osevents_added on all active pollers.  Notably
+ * it sets it on poller_app, which is always active.
+ *
+ * The thread which does this will dispose of its egc or ao before
+ * exiting libxl so it will always wake up the poller_app if the last
+ * call to _beforepoll was before the osevents were added.  So the
+ * application's fd set contains at least a wakeup in the form of the
+ * poller_app fd.  The application cannot sleep on the libxl fd set
+ * until it has called _afterpoll which empties the pipe, and it
+ * is expected to then call _beforepoll again before sleeping.
+ *
+ * So all the application's event waiting thread(s) will always have
+ * an up to date osevent set, and will be woken up if necessary to
+ * achieve this.  (This is in contrast libxl's own event loop where
+ * only one thread need be up to date, as discussed above.)
+ */
+static void pollers_note_osevent_added(libxl_ctx *ctx) {
+    libxl__poller *poller;
+    LIBXL_LIST_FOREACH(poller, &ctx->pollers_active, active_entry)
+        poller->osevents_added = 1;
+}
+
+static void baton_wake(libxl__gc *gc, libxl__poller *wake)
+{
+    libxl__poller_wakeup(gc, wake);
+
+    wake->osevents_added = 0;
+    /* This serves to make _1_baton idempotent.  It is OK even though
+     * that poller may currently be sleeping on only old osevents,
+     * because it is going to wake up because we've just prodded it,
+     * and it pick up new osevents on its next iteration (or pass
+     * on the baton). */
+}
+
+void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc)
+    /* Any poller we had must have been `put' already. */
+{
+    libxl__poller *search, *wake=0;
+
+    if (CTX->poller_app->osevents_added)
+        baton_wake(gc, CTX->poller_app);
+
+    LIBXL_LIST_FOREACH(search, &CTX->pollers_active, active_entry) {
+        if (search == CTX->poller_app)
+            /* This one is special.  We can't give it the baton. */
+            continue;
+        if (!search->osevents_added)
+            /* This poller is up to date and will wake up as needed. */
+            return;
+        if (!wake)
+            wake = search;
+    }
+
+    if (!wake)
+        /* no-one in libxl waiting for any events */
+        return;
+
+    baton_wake(gc, wake);
+}
+
+/*
+ * The counter osevent_in_hook is used to ensure that the application
+ * honours the reentrancy restriction documented in libxl_event.h.
+ *
+ * The application's registration hooks should be called ONLY via
+ * these macros, with the ctx locked.  Likewise all the "occurred"
+ * entrypoints from the application should assert(!in_hook);
+ *
+ * During the hook call - including while the arguments are being
+ * evaluated - ev->nexus is guaranteed to be valid and refer to the
+ * nexus which is being used for this event registration.  The
+ * arguments should specify ev->nexus for the for_libxl argument and
+ * ev->nexus->for_app_reg (or a pointer to it) for for_app_reg.
+ */
+#define OSEVENT_HOOK_INTERN(retval, failedp, evkind, hookop, nexusop, ...) do { \
+    if (CTX->osevent_hooks) {                                           \
+        CTX->osevent_in_hook++;                                         \
+        libxl__osevent_hook_nexi *nexi = &CTX->hook_##evkind##_nexi_idle; \
+        osevent_hook_pre_##nexusop(gc, ev, nexi, &ev->nexus);            \
+        retval CTX->osevent_hooks->evkind##_##hookop                    \
+            (CTX->osevent_user, __VA_ARGS__);                           \
+        if ((failedp))                                                  \
+            osevent_hook_failed_##nexusop(gc, ev, nexi, &ev->nexus);     \
+        CTX->osevent_in_hook--;                                         \
+    }                                                                   \
+} while (0)
+
+#define OSEVENT_HOOK(evkind, hookop, nexusop, ...) ({                   \
+    int osevent_hook_rc = 0;                                    \
+    OSEVENT_HOOK_INTERN(osevent_hook_rc =, !!osevent_hook_rc,   \
+                        evkind, hookop, nexusop, __VA_ARGS__);          \
+    osevent_hook_rc;                                            \
+})
+
+#define OSEVENT_HOOK_VOID(evkind, hookop, nexusop, ...)                         \
+    OSEVENT_HOOK_INTERN(/* void */, 0, evkind, hookop, nexusop, __VA_ARGS__)
+
+/*
+ * The application's calls to libxl_osevent_occurred_... may be
+ * indefinitely delayed with respect to the rest of the program (since
+ * they are not necessarily called with any lock held).  So the
+ * for_libxl value we receive may be (almost) arbitrarily old.  All we
+ * know is that it came from this ctx.
+ *
+ * Therefore we may not free the object referred to by any for_libxl
+ * value until we free the whole libxl_ctx.  And if we reuse it we
+ * must be able to tell when an old use turns up, and discard the
+ * stale event.
+ *
+ * Thus we cannot use the ev directly as the for_libxl value - we need
+ * a layer of indirection.
+ *
+ * We do this by keeping a pool of libxl__osevent_hook_nexus structs,
+ * and use pointers to them as for_libxl values.  In fact, there are
+ * two pools: one for fds and one for timeouts.  This ensures that we
+ * don't risk a type error when we upcast nexus->ev.  In each nexus
+ * the ev is either null or points to a valid libxl__ev_time or
+ * libxl__ev_fd, as applicable.
+ *
+ * We /do/ allow ourselves to reassociate an old nexus with a new ev
+ * as otherwise we would have to leak nexi.  (This reassociation
+ * might, of course, be an old ev being reused for a new purpose so
+ * simply comparing the ev pointer is not sufficient.)  Thus the
+ * libxl_osevent_occurred functions need to check that the condition
+ * allegedly signalled by this event actually exists.
+ *
+ * The nexi and the lists are all protected by the ctx lock.
+ */
+
+struct libxl__osevent_hook_nexus {
+    void *ev;
+    void *for_app_reg;
+    LIBXL_SLIST_ENTRY(libxl__osevent_hook_nexus) next;
+};
+
+static void *osevent_ev_from_hook_nexus(libxl_ctx *ctx,
+           libxl__osevent_hook_nexus *nexus /* pass  void *for_libxl */)
+{
+    return nexus->ev;
+}
+
+static void osevent_release_nexus(libxl__gc *gc,
+                                  libxl__osevent_hook_nexi *nexi_idle,
+                                  libxl__osevent_hook_nexus *nexus)
+{
+    nexus->ev = 0;
+    LIBXL_SLIST_INSERT_HEAD(nexi_idle, nexus, next);
+}
+
+/*----- OSEVENT* hook functions for nexusop "alloc" -----*/
+static void osevent_hook_pre_alloc(libxl__gc *gc, void *ev,
+                                   libxl__osevent_hook_nexi *nexi_idle,
+                                   libxl__osevent_hook_nexus **nexus_r)
+{
+    libxl__osevent_hook_nexus *nexus = LIBXL_SLIST_FIRST(nexi_idle);
+    if (nexus) {
+        LIBXL_SLIST_REMOVE_HEAD(nexi_idle, next);
+    } else {
+        nexus = libxl__zalloc(NOGC, sizeof(*nexus));
+    }
+    nexus->ev = ev;
+    *nexus_r = nexus;
+}
+static void osevent_hook_failed_alloc(libxl__gc *gc, void *ev,
+                                      libxl__osevent_hook_nexi *nexi_idle,
+                                      libxl__osevent_hook_nexus **nexus)
+{
+    osevent_release_nexus(gc, nexi_idle, *nexus);
+}
+
+/*----- OSEVENT* hook functions for nexusop "release" -----*/
+static void osevent_hook_pre_release(libxl__gc *gc, void *ev,
+                                     libxl__osevent_hook_nexi *nexi_idle,
+                                     libxl__osevent_hook_nexus **nexus)
+{
+    osevent_release_nexus(gc, nexi_idle, *nexus);
+}
+static void osevent_hook_failed_release(libxl__gc *gc, void *ev,
+                                        libxl__osevent_hook_nexi *nexi_idle,
+                                        libxl__osevent_hook_nexus **nexus)
+{
+    abort();
+}
+
+/*----- OSEVENT* hook functions for nexusop "noop" -----*/
+static void osevent_hook_pre_noop(libxl__gc *gc, void *ev,
+                                  libxl__osevent_hook_nexi *nexi_idle,
+                                  libxl__osevent_hook_nexus **nexus) { }
+static void osevent_hook_failed_noop(libxl__gc *gc, void *ev,
+                                     libxl__osevent_hook_nexi *nexi_idle,
+                                     libxl__osevent_hook_nexus **nexus) { }
+
+
+/*
+ * fd events
+ */
+
+int libxl__ev_fd_register(libxl__gc *gc, libxl__ev_fd *ev,
+                          libxl__ev_fd_callback *func,
+                          int fd, short events)
+{
+    int rc;
+
+    assert(fd >= 0);
+
+    CTX_LOCK;
+
+    DBG("ev_fd=%p register fd=%d events=%x", ev, fd, events);
+
+    rc = OSEVENT_HOOK(fd,register, alloc, fd, &ev->nexus->for_app_reg,
+                      events, ev->nexus);
+    if (rc) goto out;
+
+    ev->fd = fd;
+    ev->events = events;
+    ev->func = func;
+
+    LIBXL_LIST_INSERT_HEAD(&CTX->efds, ev, entry);
+    pollers_note_osevent_added(CTX);
+
+    rc = 0;
+
+ out:
+    CTX_UNLOCK;
+    return rc;
+}
+
+int libxl__ev_fd_modify(libxl__gc *gc, libxl__ev_fd *ev, short events)
+{
+    int rc;
+
+    CTX_LOCK;
+    assert(libxl__ev_fd_isregistered(ev));
+
+    DBG("ev_fd=%p modify fd=%d events=%x", ev, ev->fd, events);
+
+    rc = OSEVENT_HOOK(fd,modify, noop, ev->fd, &ev->nexus->for_app_reg, events);
+    if (rc) goto out;
+
+    if ((events & ~ev->events))
+        pollers_note_osevent_added(CTX);
+    ev->events = events;
+
+    rc = 0;
+ out:
+    CTX_UNLOCK;
+    return rc;
+}
+
+void libxl__ev_fd_deregister(libxl__gc *gc, libxl__ev_fd *ev)
+{
+    CTX_LOCK;
+    libxl__poller *poller;
+
+    if (!libxl__ev_fd_isregistered(ev)) {
+        DBG("ev_fd=%p deregister unregistered",ev);
+        goto out;
+    }
+
+    DBG("ev_fd=%p deregister fd=%d", ev, ev->fd);
+
+    OSEVENT_HOOK_VOID(fd,deregister, release, ev->fd, ev->nexus->for_app_reg);
+    LIBXL_LIST_REMOVE(ev, entry);
+    ev->fd = -1;
+
+    LIBXL_LIST_FOREACH(poller, &CTX->pollers_active, active_entry)
+        poller->fds_deregistered = 1;
+
+ out:
+    CTX_UNLOCK;
+}
+
+short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events) {
+    struct pollfd check;
+    int r;
+    EGC_GC;
+
+    for (;;) {
+        check.fd = fd;
+        check.events = events;
+        r = poll(&check, 1, 0);
+        DBG("poll recheck fd=%d r=%d revents=%#x", fd, r, check.revents);
+        if (!r)
+            break;
+        if (r==1)
+            break;
+        assert(r<0);
+        if (errno != EINTR) {
+            LIBXL__EVENT_DISASTER(gc, "failed poll to check for fd", errno, 0);
+            return 0;
+        }
+    }
+    assert(!!r == !!check.revents);
+    return check.revents;
+}
+
+/*
+ * timeouts
+ */
+
+
+int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r)
+{
+    int rc = gettimeofday(now_r, 0);
+    if (rc) {
+        LOGE(ERROR, "gettimeofday failed");
+        return ERROR_FAIL;
+    }
+    return 0;
+}
+
+static int time_rel_to_abs(libxl__gc *gc, int ms, struct timeval *abs_out)
+{
+    int rc;
+    struct timeval additional = {
+        .tv_sec = ms / 1000,
+        .tv_usec = (ms % 1000) * 1000
+    };
+    struct timeval now;
+
+    rc = libxl__gettimeofday(gc, &now);
+    if (rc) return rc;
+
+    timeradd(&now, &additional, abs_out);
+    return 0;
+}
+
+static int time_register_finite(libxl__gc *gc, libxl__ev_time *ev,
+                                struct timeval absolute)
+{
+    int rc;
+    libxl__ev_time *evsearch;
+
+    rc = OSEVENT_HOOK(timeout,register, alloc, &ev->nexus->for_app_reg,
+                      absolute, ev->nexus);
+    if (rc) return rc;
+
+    ev->infinite = 0;
+    ev->abs = absolute;
+    LIBXL_TAILQ_INSERT_SORTED(&CTX->etimes, entry, ev, evsearch, /*empty*/,
+                              timercmp(&ev->abs, &evsearch->abs, >));
+
+    pollers_note_osevent_added(CTX);
+    return 0;
+}
+
+static void time_deregister(libxl__gc *gc, libxl__ev_time *ev)
+{
+    libxl__ao_abortable_deregister(&ev->abrt);
+
+    if (!ev->infinite) {
+        struct timeval right_away = { 0, 0 };
+        if (ev->nexus) /* only set if app provided hooks */
+            ev->nexus->ev = 0;
+        OSEVENT_HOOK_VOID(timeout,modify,
+                          noop /* release nexus in _occurred_ */,
+                          &ev->nexus->for_app_reg, right_away);
+        LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
+    }
+}
+
+static void time_done_debug(libxl__gc *gc, const char *func,
+                            libxl__ev_time *ev, int rc)
+{
+#ifdef DEBUG
+    libxl__log(CTX, XTL_DEBUG, -1, __FILE__, 0, func, INVALID_DOMID,
+               "ev_time=%p done rc=%d .func=%p infinite=%d abs=%lu.%06lu",
+               ev, rc, ev->func, ev->infinite,
+               (unsigned long)ev->abs.tv_sec, (unsigned long)ev->abs.tv_usec);
+#endif
+}
+
+static void time_aborted(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
+{
+    libxl__ev_time *ev = CONTAINER_OF(abrt, *ev, abrt);
+    EGC_GC;
+
+    time_deregister(gc, ev);
+    DBG("ev_time=%p aborted", ev);
+    ev->func(egc, ev, &ev->abs, rc);
+}
+
+static int time_register_abortable(libxl__ao *ao, libxl__ev_time *ev)
+{
+    ev->abrt.ao = ao;
+    ev->abrt.callback = time_aborted;
+    return libxl__ao_abortable_register(&ev->abrt);
+}
+
+int libxl__ev_time_register_abs(libxl__ao *ao, libxl__ev_time *ev,
+                                libxl__ev_time_callback *func,
+                                struct timeval absolute)
+{
+    AO_GC;
+    int rc;
+
+    CTX_LOCK;
+
+    DBG("ev_time=%p register abs=%lu.%06lu",
+        ev, (unsigned long)absolute.tv_sec, (unsigned long)absolute.tv_usec);
+
+    rc = time_register_abortable(ao, ev);
+    if (rc) goto out;
+
+    rc = time_register_finite(gc, ev, absolute);
+    if (rc) goto out;
+
+    ev->func = func;
+
+    rc = 0;
+ out:
+    libxl__ao_abortable_deregister(&ev->abrt);
+    time_done_debug(gc,__func__,ev,rc);
+    CTX_UNLOCK;
+    return rc;
+}
+
+
+int libxl__ev_time_register_rel(libxl__ao *ao, libxl__ev_time *ev,
+                                libxl__ev_time_callback *func,
+                                int milliseconds /* as for poll(2) */)
+{
+    AO_GC;
+    struct timeval absolute;
+    int rc;
+
+    CTX_LOCK;
+
+    DBG("ev_time=%p register ms=%d", ev, milliseconds);
+
+    rc = time_register_abortable(ao, ev);
+    if (rc) goto out;
+
+    if (milliseconds < 0) {
+        ev->infinite = 1;
+    } else {
+        rc = time_rel_to_abs(gc, milliseconds, &absolute);
+        if (rc) goto out;
+
+        rc = time_register_finite(gc, ev, absolute);
+        if (rc) goto out;
+    }
+
+    ev->func = func;
+    rc = 0;
+
+ out:
+    if (!libxl__ev_time_isregistered(ev))
+        libxl__ao_abortable_deregister(&ev->abrt);
+    time_done_debug(gc,__func__,ev,rc);
+    CTX_UNLOCK;
+    return rc;
+}
+
+void libxl__ev_time_deregister(libxl__gc *gc, libxl__ev_time *ev)
+{
+    CTX_LOCK;
+
+    DBG("ev_time=%p deregister", ev);
+
+    if (!libxl__ev_time_isregistered(ev))
+        goto out;
+
+    time_deregister(gc, ev);
+    ev->func = 0;
+
+ out:
+    time_done_debug(gc,__func__,ev,0);
+    CTX_UNLOCK;
+    return;
+}
+
+static void time_occurs(libxl__egc *egc, libxl__ev_time *etime, int rc)
+{
+    EGC_GC;
+
+    DBG("ev_time=%p occurs abs=%lu.%06lu",
+        etime, (unsigned long)etime->abs.tv_sec,
+        (unsigned long)etime->abs.tv_usec);
+
+    libxl__ev_time_callback *func = etime->func;
+    etime->func = 0;
+    func(egc, etime, &etime->abs, rc);
+}
+
+
+/*
+ * xenstore watches
+ */
+
+libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum)
+{
+    libxl__ev_watch_slot *slot = &CTX->watch_slots[slotnum];
+    libxl__ev_watch_slot *slotcontents = LIBXL_SLIST_NEXT(slot, empty);
+
+    if (slotcontents == NULL ||
+        ((uintptr_t)slotcontents >= (uintptr_t)CTX->watch_slots &&
+         (uintptr_t)slotcontents < (uintptr_t)(CTX->watch_slots +
+                                               CTX->watch_nslots)))
+        /* An empty slot has either a NULL pointer (end of the
+         * free list), or a pointer to another entry in the array.
+         * So we can do a bounds check to distinguish empty from
+         * full slots.
+         */
+        /* We need to do the comparisons as uintptr_t because
+         * comparing pointers which are not in the same object is
+         * undefined behaviour; if the compiler managed to figure
+         * out that watch_slots[0..watch_nslots-1] is all of the
+         * whole array object it could prove that the above bounds
+         * check was always true if it was legal, and remove it!
+         *
+         * uintptr_t because even on a machine with signed
+         * pointers, objects do not cross zero; whereas on
+         * machines with unsigned pointers, they may cross
+         * 0x8bazillion.
+         */
+        return NULL;
+
+        /* see comment near libxl__ev_watch_slot definition */
+    return (void*)slotcontents;
+}
+
+static void libxl__set_watch_slot_contents(libxl__ev_watch_slot *slot,
+                                           libxl__ev_xswatch *w)
+{
+    /* we look a bit behind the curtain of LIBXL_SLIST, to explicitly
+     * assign to the pointer that's the next link.  See the comment
+     * by the definition of libxl__ev_watch_slot */
+    slot->empty.sle_next = (void*)w;
+}
+
+static void watchfd_callback(libxl__egc *egc, libxl__ev_fd *ev,
+                             int fd, short events, short revents)
+{
+    EGC_GC;
+
+    if (revents & (POLLERR|POLLHUP))
+        LIBXL__EVENT_DISASTER(gc, "unexpected poll event on watch fd", 0, 0);
+
+    for (;;) {
+        char **event = xs_check_watch(CTX->xsh);
+        if (!event) {
+            if (errno == EAGAIN) break;
+            if (errno == EINTR) continue;
+            LIBXL__EVENT_DISASTER(gc, "cannot check/read watches", errno, 0);
+            return;
+        }
+
+        const char *epath = event[0];
+        const char *token = event[1];
+        int slotnum;
+        uint32_t counterval;
+        int rc = sscanf(token, "%d/%"SCNx32, &slotnum, &counterval);
+        if (rc != 2) {
+            LOG(ERROR, "watch epath=%s token=%s: failed to parse token",
+                epath, token);
+            /* oh well */
+            goto ignore;
+        }
+        if (slotnum < 0 || slotnum >= CTX->watch_nslots) {
+            /* perhaps in the future we will make the watchslots array shrink */
+            LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, "watch epath=%s token=%s:"
+                       " slotnum %d out of range [0,%d>",
+                       epath, token, slotnum, CTX->watch_nslots);
+            goto ignore;
+        }
+
+        libxl__ev_xswatch *w = libxl__watch_slot_contents(gc, slotnum);
+
+        if (!w) {
+            LOG(DEBUG, "watch epath=%s token=%s: empty slot", epath, token);
+            goto ignore;
+        }
+
+        if (w->counterval != counterval) {
+            LOG(DEBUG, "watch w=%p epath=%s token=%s: counter != %"PRIx32,
+                w, epath, token, w->counterval);
+            goto ignore;
+        }
+
+        /* Now it's possible, though unlikely, that this was an event
+         * from a previous use of the same slot with the same counterval.
+         *
+         * In that case either:
+         *  - the event path is a child of the watch path, in
+         *    which case this watch would really have generated this
+         *    event if it had been registered soon enough and we are
+         *    OK to give this possibly-spurious event to the caller; or
+         * - it is not, in which case we must suppress it as the
+         *   caller should not see events for unrelated paths.
+         *
+         * See also docs/misc/xenstore.txt.
+         */
+        if (!xs_path_is_subpath(w->path, epath)) {
+            LOG(DEBUG, "watch w=%p wpath=%s token=%s: unexpected epath=%s",
+                w, w->path, token, epath);
+            goto ignore;
+        }
+
+        /* At last, we have checked everything! */
+        LOG(DEBUG, "watch w=%p wpath=%s token=%s: event epath=%s",
+            w, w->path, token, epath);
+        w->callback(egc, w, w->path, epath);
+
+    ignore:
+        free(event);
+    }
+}
+
+static char *watch_token(libxl__gc *gc, int slotnum, uint32_t counterval)
+{
+    return GCSPRINTF("%d/%"PRIx32, slotnum, counterval);
+}
+
+static void watches_check_fd_deregister(libxl__gc *gc)
+{
+    assert(CTX->nwatches>=0);
+    if (!CTX->nwatches)
+        libxl__ev_fd_deregister(gc, &CTX->watch_efd);
+}
+
+int libxl__ev_xswatch_register(libxl__gc *gc, libxl__ev_xswatch *w,
+                               libxl__ev_xswatch_callback *func,
+                               const char *path /* copied */)
+{
+    libxl__ev_watch_slot *use = NULL;
+    char *path_copy = NULL;
+    int rc;
+
+    CTX_LOCK;
+
+    if (!libxl__ev_fd_isregistered(&CTX->watch_efd)) {
+        rc = libxl__ev_fd_register(gc, &CTX->watch_efd, watchfd_callback,
+                                   xs_fileno(CTX->xsh), POLLIN);
+        if (rc) goto out_rc;
+    }
+
+    if (LIBXL_SLIST_EMPTY(&CTX->watch_freeslots)) {
+        /* Free list is empty so there is not in fact a linked
+         * free list in the array and we can safely realloc it */
+        int newarraysize = (CTX->watch_nslots + 1) << 2;
+        int i;
+        libxl__ev_watch_slot *newarray =
+            libxl__realloc(NOGC,
+                           CTX->watch_slots, sizeof(*newarray) * newarraysize);
+        if (!newarray) goto out_nomem;
+        for (i = CTX->watch_nslots; i < newarraysize; i++)
+            LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots,
+                                    &newarray[i], empty);
+        CTX->watch_slots = newarray;
+        CTX->watch_nslots = newarraysize;
+    }
+    use = LIBXL_SLIST_FIRST(&CTX->watch_freeslots);
+    assert(use);
+    LIBXL_SLIST_REMOVE_HEAD(&CTX->watch_freeslots, empty);
+
+    path_copy = strdup(path);
+    if (!path_copy) goto out_nomem;
+
+    int slotnum = use - CTX->watch_slots;
+    w->counterval = CTX->watch_counter++;
+
+    const char *token = watch_token(gc, slotnum, w->counterval);
+    LOG(DEBUG, "watch w=%p wpath=%s token=%s: register slotnum=%d",
+        w, path, token, slotnum);
+
+    if (!xs_watch(CTX->xsh, path, token)) {
+        LOGEV(ERROR, errno, "create watch for path %s", path);
+        rc = ERROR_FAIL;
+        goto out_rc;
+    }
+
+    w->slotnum = slotnum;
+    w->path = path_copy;
+    w->callback = func;
+    CTX->nwatches++;
+    libxl__set_watch_slot_contents(use, w);
+
+    CTX_UNLOCK;
+    return 0;
+
+ out_nomem:
+    rc = ERROR_NOMEM;
+ out_rc:
+    if (use)
+        LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, use, empty);
+    free(path_copy);
+    watches_check_fd_deregister(gc);
+    CTX_UNLOCK;
+    return rc;
+}
+
+void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch *w)
+{
+    /* it is legal to deregister from within _callback */
+    CTX_LOCK;
+
+    if (w->slotnum >= 0) {
+        const char *token = watch_token(gc, w->slotnum, w->counterval);
+
+        LOG(DEBUG, "watch w=%p wpath=%s token=%s: deregister slotnum=%d",
+            w, w->path, token, w->slotnum);
+
+        if (!xs_unwatch(CTX->xsh, w->path, token))
+            /* Oh well, we will just get watch events forever more
+             * and ignore them.  But we should complain to the log. */
+            LOGEV(ERROR, errno, "remove watch for path %s", w->path);
+
+        libxl__ev_watch_slot *slot = &CTX->watch_slots[w->slotnum];
+        LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, slot, empty);
+        w->slotnum = -1;
+        CTX->nwatches--;
+        watches_check_fd_deregister(gc);
+    } else {
+        LOG(DEBUG, "watch w=%p: deregister unregistered", w);
+    }
+
+    free(w->path);
+    w->path = NULL;
+
+    CTX_UNLOCK;
+}
+
+/*
+ * evtchn
+ */
+
+static int evtchn_revents_check(libxl__egc *egc, int revents)
+{
+    EGC_GC;
+
+    if (revents & ~POLLIN) {
+        LOG(ERROR, "unexpected poll event on event channel fd: %x", revents);
+        LIBXL__EVENT_DISASTER(gc,
+                   "unexpected poll event on event channel fd", 0, 0);
+        libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
+        return ERROR_FAIL;
+    }
+
+    assert(revents & POLLIN);
+
+    return 0;
+}
+
+static void evtchn_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
+                               int fd, short events, short revents)
+{
+    EGC_GC;
+    libxl__ev_evtchn *evev;
+    int rc;
+    xenevtchn_port_or_error_t port;
+
+    rc = evtchn_revents_check(egc, revents);
+    if (rc) return;
+
+    for (;;) {
+        /* Check the fd again.  The incoming revent may no longer be
+         * true, because the libxl ctx lock has not necessarily been
+         * held continuously since someone noticed the fd.  Normally
+         * this wouldn't be a problem but evtchn devices don't always
+         * honour O_NONBLOCK (see xenctrl.h). */
+        revents = libxl__fd_poll_recheck(egc,fd,POLLIN);
+        if (!revents)
+            break;
+        rc = evtchn_revents_check(egc, revents);
+        if (rc) return;
+
+        /* OK, that's that workaround done.  We can actually check for
+         * work for us to do: */
+
+        port = xenevtchn_pending(CTX->xce);
+        if (port < 0) {
+            if (errno == EAGAIN)
+                break;
+            LIBXL__EVENT_DISASTER(gc,
+     "unexpected failure fetching occurring event port number from evtchn",
+                                  errno, 0);
+            return;
+        }
+
+        LIBXL_LIST_FOREACH(evev, &CTX->evtchns_waiting, entry)
+            if (port == evev->port)
+                goto found;
+        /* not found */
+        DBG("ev_evtchn port=%d no-one cared", port);
+        continue;
+
+    found:
+        DBG("ev_evtchn=%p port=%d signaled", evev, port);
+        evev->waiting = 0;
+        LIBXL_LIST_REMOVE(evev, entry);
+        evev->callback(egc, evev);
+    }
+}
+
+int libxl__ctx_evtchn_init(libxl__gc *gc) {
+    xenevtchn_handle *xce;
+    int rc, fd;
+
+    if (CTX->xce)
+        return 0;
+
+    xce = xenevtchn_open(CTX->lg, 0);
+    if (!xce) {
+        LOGE(ERROR,"cannot open libxc evtchn handle");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    fd = xenevtchn_fd(xce);
+    assert(fd >= 0);
+
+    rc = libxl_fd_set_nonblock(CTX, fd, 1);
+    if (rc) goto out;
+
+    CTX->xce = xce;
+    return 0;
+
+ out:
+    xenevtchn_close(xce);
+    return rc;
+}
+
+static void evtchn_check_fd_deregister(libxl__gc *gc)
+{
+    if (CTX->xce && LIBXL_LIST_EMPTY(&CTX->evtchns_waiting))
+        libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
+}
+
+int libxl__ev_evtchn_wait(libxl__gc *gc, libxl__ev_evtchn *evev)
+{
+    int r, rc;
+
+    DBG("ev_evtchn=%p port=%d wait (was waiting=%d)",
+        evev, evev->port, evev->waiting);
+
+    rc = libxl__ctx_evtchn_init(gc);
+    if (rc) goto out;
+
+    if (!libxl__ev_fd_isregistered(&CTX->evtchn_efd)) {
+        rc = libxl__ev_fd_register(gc, &CTX->evtchn_efd, evtchn_fd_callback,
+                                   xenevtchn_fd(CTX->xce), POLLIN);
+        if (rc) goto out;
+    }
+
+    if (evev->waiting)
+        return 0;
+
+    r = xenevtchn_unmask(CTX->xce, evev->port);
+    if (r) {
+        LOGE(ERROR,"cannot unmask event channel %d",evev->port);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    evev->waiting = 1;
+    LIBXL_LIST_INSERT_HEAD(&CTX->evtchns_waiting, evev, entry);
+    return 0;
+
+ out:
+    evtchn_check_fd_deregister(gc);
+    return rc;
+}
+
+void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev)
+{
+    DBG("ev_evtchn=%p port=%d cancel (was waiting=%d)",
+        evev, evev->port, evev->waiting);
+
+    if (!evev->waiting)
+        return;
+
+    evev->waiting = 0;
+    LIBXL_LIST_REMOVE(evev, entry);
+    evtchn_check_fd_deregister(gc);
+}
+
+/*
+ * waiting for device state
+ */
+
+static void devstate_callback(libxl__egc *egc, libxl__xswait_state *xsw,
+                              int rc, const char *sstate)
+{
+    EGC_GC;
+    libxl__ev_devstate *ds = CONTAINER_OF(xsw, *ds, w);
+
+    if (rc) {
+        if (rc == ERROR_TIMEDOUT)
+            LOG(DEBUG, "backend %s wanted state %d "" timed out", ds->w.path,
+                ds->wanted);
+        goto out;
+    }
+    if (!sstate) {
+        LOG(DEBUG, "backend %s wanted state %d"" but it was removed",
+            ds->w.path, ds->wanted);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    int got = atoi(sstate);
+    if (got == ds->wanted) {
+        LOG(DEBUG, "backend %s wanted state %d ok", ds->w.path, ds->wanted);
+        rc = 0;
+    } else {
+        LOG(DEBUG, "backend %s wanted state %d"" still waiting state %d",
+            ds->w.path, ds->wanted, got);
+        return;
+    }
+
+ out:
+    libxl__ev_devstate_cancel(gc, ds);
+    ds->callback(egc, ds, rc);
+}
+
+int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
+                            libxl__ev_devstate_callback cb,
+                            const char *state_path, int state, int milliseconds)
+{
+    AO_GC;
+    int rc;
+
+    libxl__xswait_init(&ds->w);
+    ds->wanted = state;
+    ds->callback = cb;
+
+    ds->w.ao = ao;
+    ds->w.what = GCSPRINTF("backend %s (hoping for state change to %d)",
+                           state_path, state);
+    ds->w.path = state_path;
+    ds->w.timeout_ms = milliseconds;
+    ds->w.callback = devstate_callback;
+    rc = libxl__xswait_start(gc, &ds->w);
+    if (rc) goto out;
+
+    return 0;
+
+ out:
+    libxl__ev_devstate_cancel(gc, ds);
+    return rc;
+}
+
+/*
+ * immediate non-reentrant callback
+ */
+
+void libxl__ev_immediate_register(libxl__egc *egc, libxl__ev_immediate *ei)
+{
+    LIBXL_STAILQ_INSERT_TAIL(&egc->ev_immediates, ei, entry);
+}
+
+/*
+ * domain death/destruction
+ */
+
+/*
+ * We use a xenstore watch on the domain's path, rather than using an
+ * @releaseDomain watch and asking the hypervisor.  This is simpler
+ * because turning @releaseDomain into domain-specific information is
+ * complicated.
+ *
+ * It is also sufficient for our callers, which are generally trying
+ * to do cleanup of their own execution state on domain death, for the
+ * following reason: if the domain is destroyed then either (a) the
+ * entries in xenstore have already been deleted, in which case the
+ * test here works or (b) they have not in which case something has
+ * gone very badly wrong and we are going to leak those xenstore
+ * entries, in which case trying to avoid leaking other stuff is
+ * futile.
+ */
+
+void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc)
+{
+    libxl__ao_abortable_init(&dc->abrt);
+    libxl__ev_xswatch_init(&dc->watch);
+}
+
+void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc)
+{
+    libxl__ao_abortable_deregister(&dc->abrt);
+    libxl__ev_xswatch_deregister(gc,&dc->watch);
+}
+
+static void domaindeathcheck_callback(libxl__egc *egc, libxl__ev_xswatch *w,
+                            const char *watch_path, const char *event_path)
+{
+    libxl__domaindeathcheck *dc = CONTAINER_OF(w, *dc, watch);
+    EGC_GC;
+    const char *p = libxl__xs_read(gc, XBT_NULL, watch_path);
+    if (p) return;
+
+    libxl__domaindeathcheck_stop(gc,dc);
+
+    if (errno!=ENOENT) {
+        LIBXL__EVENT_DISASTER(gc,"failed to read xenstore"
+                              " for domain detach check", errno, 0);
+        return;
+    }
+
+    LOG(ERROR,"%s: domain %"PRIu32" removed (%s no longer in xenstore)",
+        dc->what, dc->domid, watch_path);
+    dc->callback(egc, dc, ERROR_DOMAIN_DESTROYED);
+}
+
+static void domaindeathcheck_abort(libxl__egc *egc,
+                                   libxl__ao_abortable *abrt,
+                                   int rc)
+{
+    libxl__domaindeathcheck *dc = CONTAINER_OF(abrt, *dc, abrt);
+    EGC_GC;
+
+    libxl__domaindeathcheck_stop(gc,dc);
+    dc->callback(egc, dc, rc);
+}
+
+int libxl__domaindeathcheck_start(libxl__ao *ao,
+                                  libxl__domaindeathcheck *dc)
+{
+    AO_GC;
+    int rc;
+    const char *path = GCSPRINTF("/local/domain/%"PRIu32, dc->domid);
+
+    libxl__domaindeathcheck_init(dc);
+
+    dc->abrt.ao = ao;
+    dc->abrt.callback = domaindeathcheck_abort;
+    rc = libxl__ao_abortable_register(&dc->abrt);
+    if (rc) goto out;
+
+    rc = libxl__ev_xswatch_register(gc, &dc->watch,
+                                    domaindeathcheck_callback, path);
+    if (rc) goto out;
+
+    return 0;
+
+ out:
+    libxl__domaindeathcheck_stop(gc,dc);
+    return rc;
+}
+
+/*
+ * osevent poll
+ */
+
+static int beforepoll_internal(libxl__gc *gc, libxl__poller *poller,
+                               int *nfds_io, struct pollfd *fds,
+                               int *timeout_upd, struct timeval now)
+{
+    libxl__ev_fd *efd;
+    int rc;
+
+    /*
+     * We need to look at the fds we want twice: firstly, to count
+     * them so we can make the rindex array big enough, and secondly
+     * to actually fill the arrays in.
+     *
+     * To ensure correctness and avoid repeating the logic for
+     * deciding which fds are relevant, we define a macro
+     *    REQUIRE_FDS( BODY )
+     * which calls
+     *    do{
+     *        int req_fd;
+     *        int req_events;
+     *        BODY;
+     *    }while(0)
+     * for each fd with a nonzero events.  This is invoked twice.
+     *
+     * The definition of REQUIRE_FDS is simplified with the helper
+     * macro
+     *    void REQUIRE_FD(int req_fd, int req_events, BODY);
+     */
+
+#define REQUIRE_FDS(BODY) do{                                          \
+                                                                       \
+        LIBXL_LIST_FOREACH(efd, &CTX->efds, entry)                     \
+            REQUIRE_FD(efd->fd, efd->events, BODY);                    \
+                                                                       \
+        REQUIRE_FD(poller->wakeup_pipe[0], POLLIN, BODY);              \
+                                                                       \
+    }while(0)
+
+#define REQUIRE_FD(req_fd_, req_events_, BODY) do{      \
+        int req_events = (req_events_);                 \
+        int req_fd = (req_fd_);                         \
+        if (req_events) {                               \
+            BODY;                                       \
+        }                                               \
+    }while(0)
+
+
+    /*
+     * In order to be able to efficiently find the libxl__ev_fd for a
+     * struct poll during _afterpoll, we maintain a shadow data
+     * structure in CTX->fd_rindices: each fd corresponds to a slot in
+     * fd_rindices, and each element in the rindices is three indices
+     * into the fd array (for POLLIN, POLLPRI and POLLOUT).
+     */
+
+    if (*nfds_io) {
+        /*
+         * As an optimisation, we don't touch fd_rindex
+         * if *nfds_io is zero on entry, since in that case the
+         * caller just wanted to know how big an array to give us.
+         *
+         * If !*nfds_io, the unconditional parts below are guaranteed
+         * not to mess with fd_rindex.
+         */
+
+        int maxfd = 0;
+
+        REQUIRE_FDS({
+            if (req_fd >= maxfd)
+                maxfd = req_fd + 1;
+        });
+
+        /* make sure our array is as big as *nfds_io */
+        if (poller->fd_rindices_allocd < maxfd) {
+            assert(ARRAY_SIZE_OK(poller->fd_rindices, maxfd));
+            poller->fd_rindices =
+                libxl__realloc(NOGC, poller->fd_rindices,
+                               maxfd * sizeof(*poller->fd_rindices));
+            memset(poller->fd_rindices + poller->fd_rindices_allocd,
+                   0,
+                   (maxfd - poller->fd_rindices_allocd)
+                     * sizeof(*poller->fd_rindices));
+            poller->fd_rindices_allocd = maxfd;
+        }
+    }
+
+    int used = 0;
+
+    REQUIRE_FDS({
+        if (used < *nfds_io) {
+            fds[used].fd = req_fd;
+            fds[used].events = req_events;
+            fds[used].revents = 0;
+            assert(req_fd < poller->fd_rindices_allocd);
+            if (req_events & POLLIN)  poller->fd_rindices[req_fd][0] = used;
+            if (req_events & POLLPRI) poller->fd_rindices[req_fd][1] = used;
+            if (req_events & POLLOUT) poller->fd_rindices[req_fd][2] = used;
+        }
+        used++;
+    });
+
+    rc = used <= *nfds_io ? 0 : ERROR_BUFFERFULL;
+
+    *nfds_io = used;
+
+    poller->fds_deregistered = 0;
+    poller->osevents_added = 0;
+
+    libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
+    if (etime) {
+        int our_timeout;
+        struct timeval rel;
+        static struct timeval zero;
+
+        timersub(&etime->abs, &now, &rel);
+
+        if (timercmp(&rel, &zero, <)) {
+            our_timeout = 0;
+        } else if (rel.tv_sec >= 2000000) {
+            our_timeout = 2000000000;
+        } else {
+            our_timeout = rel.tv_sec * 1000 + (rel.tv_usec + 999) / 1000;
+        }
+        if (*timeout_upd < 0 || our_timeout < *timeout_upd)
+            *timeout_upd = our_timeout;
+    }
+
+    return rc;
+}
+
+int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
+                             struct pollfd *fds, int *timeout_upd,
+                             struct timeval now)
+{
+    EGC_INIT(ctx);
+    CTX_LOCK;
+    int rc = beforepoll_internal(gc, ctx->poller_app,
+                                 nfds_io, fds, timeout_upd, now);
+    CTX_UNLOCK_EGC_FREE;
+    return rc;
+}
+
+static int afterpoll_check_fd(libxl__poller *poller,
+                              const struct pollfd *fds, int nfds,
+                              int fd, int events)
+    /* Returns mask of events which were requested and occurred.  Will
+     * return nonzero only once for each (poller,fd,events)
+     * combination, until the next beforepoll.  If events from
+     * different combinations overlap, between one such combination
+     * and all distinct combinations will produce nonzero returns. */
+{
+    if (fd >= poller->fd_rindices_allocd)
+        /* added after we went into poll, have to try again */
+        return 0;
+
+    events |= POLLERR | POLLHUP;
+
+    int i, revents = 0;
+    for (i=0; i<3; i++) {
+        int *slotp = &poller->fd_rindices[fd][i];
+        int slot = *slotp;
+
+        if (slot >= nfds)
+            /* stale slot entry (again, added afterwards), */
+            /* or slot for which we have already returned nonzero */
+            continue;
+
+        if (fds[slot].fd != fd)
+            /* again, stale slot entry */
+            continue;
+
+        assert(poller->fds_deregistered || !(fds[slot].revents & POLLNVAL));
+
+        /* we mask in case requested events have changed */
+        int slot_revents = fds[slot].revents & events;
+        if (!slot_revents)
+            /* this slot is for a different set of events */
+            continue;
+
+        revents |= slot_revents;
+        *slotp = INT_MAX; /* so that next time we'll see slot >= nfds */
+    }
+
+    return revents;
+}
+
+static void fd_occurs(libxl__egc *egc, libxl__ev_fd *efd, short revents_ign)
+{
+    short revents_current = libxl__fd_poll_recheck(egc, efd->fd, efd->events);
+    EGC_GC;
+
+    DBG("ev_fd=%p occurs fd=%d events=%x revents_ign=%x revents_current=%x",
+        efd, efd->fd, efd->events, revents_ign, revents_current);
+
+    if (revents_current)
+        efd->func(egc, efd, efd->fd, efd->events, revents_current);
+}
+
+static void afterpoll_internal(libxl__egc *egc, libxl__poller *poller,
+                               int nfds, const struct pollfd *fds,
+                               struct timeval now)
+{
+    /* May make callbacks into the application for child processes.
+     * ctx must be locked exactly once */
+    EGC_GC;
+    libxl__ev_fd *efd;
+
+    /*
+     * Warning! Reentrancy hazards!
+     *
+     * Many parts of this function eventually call arbitrary callback
+     * functions which may modify the event handling data structures.
+     *
+     * Of the data structures used here:
+     *
+     *   egc, poller, now
+     *                are allocated by our caller and relate to the
+     *                current thread and its call stack down into the
+     *                event machinery; it is not freed until we return.
+     *                So it is safe.
+     *
+     *   fds          is either what application passed into
+     *                libxl_osevent_afterpoll (which, although this
+     *                isn't explicitly stated, clearly must remain
+     *                valid until libxl_osevent_afterpoll returns) or
+     *                it's poller->fd_polls which is modified only by
+     *                our (non-recursive) caller eventloop_iteration.
+     *
+     *   CTX          comes from our caller, and applications are
+     *                forbidden from destroying it while we are running.
+     *                So the ctx pointer itself is safe to use; now
+     *                for its contents:
+     *
+     *   CTX->etimes  is used in a simple reentrancy-safe manner.
+     *
+     *   CTX->efds    is more complicated; see below.
+     */
+
+    for (;;) {
+        /* We restart our scan of fd events whenever we call a
+         * callback function.  This is necessary because such
+         * a callback might make arbitrary changes to CTX->efds.
+         * We invalidate the fd_rindices[] entries which were used
+         * so that we don't call the same function again. */
+        int revents;
+
+        LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) {
+
+            if (!efd->events)
+                continue;
+
+            revents = afterpoll_check_fd(poller,fds,nfds,
+                                         efd->fd,efd->events);
+            if (revents)
+                goto found_fd_event;
+        }
+        /* no ordinary fd events, then */
+        break;
+
+    found_fd_event:
+        fd_occurs(egc, efd, revents);
+    }
+
+    for (;;) {
+        libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
+        if (!etime)
+            break;
+
+        assert(!etime->infinite);
+
+        if (timercmp(&etime->abs, &now, >))
+            break;
+
+        time_deregister(gc, etime);
+
+        time_occurs(egc, etime, ERROR_TIMEDOUT);
+    }
+
+    if (afterpoll_check_fd(poller,fds,nfds, poller->wakeup_pipe[0],POLLIN)) {
+        poller->pipe_nonempty = 0;
+        int e = libxl__self_pipe_eatall(poller->wakeup_pipe[0]);
+        if (e) LIBXL__EVENT_DISASTER(gc, "read wakeup", e, 0);
+    }
+}
+
+void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
+                             struct timeval now)
+{
+    EGC_INIT(ctx);
+    CTX_LOCK;
+    afterpoll_internal(egc, ctx->poller_app, nfds, fds, now);
+    CTX_UNLOCK_EGC_FREE;
+}
+
+/*
+ * osevent hook and callback machinery
+ */
+
+void libxl_osevent_register_hooks(libxl_ctx *ctx,
+                                  const libxl_osevent_hooks *hooks,
+                                  void *user)
+{
+    GC_INIT(ctx);
+    CTX_LOCK;
+    assert(LIBXL_LIST_EMPTY(&ctx->efds));
+    assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
+    ctx->osevent_hooks = hooks;
+    ctx->osevent_user = user;
+    CTX_UNLOCK;
+    GC_FREE;
+}
+
+
+void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
+                               int fd, short events_ign, short revents_ign)
+{
+    EGC_INIT(ctx);
+    CTX_LOCK;
+    assert(!CTX->osevent_in_hook);
+
+    libxl__ev_fd *ev = osevent_ev_from_hook_nexus(ctx, for_libxl);
+    if (!ev) goto out;
+    if (ev->fd != fd) goto out;
+
+    fd_occurs(egc, ev, revents_ign);
+
+ out:
+    CTX_UNLOCK_EGC_FREE;
+}
+
+void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
+{
+    EGC_INIT(ctx);
+    CTX_LOCK;
+    assert(!CTX->osevent_in_hook);
+
+    libxl__osevent_hook_nexus *nexus = for_libxl;
+    libxl__ev_time *ev = osevent_ev_from_hook_nexus(ctx, nexus);
+
+    osevent_release_nexus(gc, &CTX->hook_timeout_nexi_idle, nexus);
+
+    if (!ev) goto out;
+    assert(!ev->infinite);
+
+    LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
+
+    time_occurs(egc, ev, ERROR_TIMEDOUT);
+
+ out:
+    CTX_UNLOCK_EGC_FREE;
+}
+
+void libxl__event_disaster(libxl__gc *gc, const char *msg, int errnoval,
+                           libxl_event_type type /* may be 0 */,
+                           const char *file, int line, const char *func)
+{
+    libxl__log(CTX, XTL_CRITICAL, errnoval, file, line, func, INVALID_DOMID,
+               "DISASTER in event loop: %s%s%s%s",
+               msg,
+               type ? " (relates to event type " : "",
+               type ? libxl_event_type_to_string(type) : "",
+               type ? ")" : "");
+
+    if (CTX->event_hooks && CTX->event_hooks->disaster) {
+        CTX->event_hooks->disaster(CTX->event_hooks_user, type, msg, errnoval);
+        return;
+    }
+
+    const char verybad[] =
+        "DISASTER in event loop not handled by libxl application";
+    LIBXL__LOG(CTX, XTL_CRITICAL, verybad);
+    fprintf(stderr, "libxl: fatal error, exiting program: %s\n", verybad);
+    exit(-1);
+}
+
+static void egc_run_callbacks(libxl__egc *egc)
+{
+    /*
+     * The callbacks must happen with the ctx unlocked.  See the
+     * comment near #define EGC_GC in libxl_internal.h and those in
+     * the definitions of libxl__egc, libxl__ao and libxl__aop.
+     */
+    EGC_GC;
+    libxl_event *ev, *ev_tmp;
+    libxl__aop_occurred *aop, *aop_tmp;
+    libxl__ev_immediate *ei;
+
+    while (!LIBXL_STAILQ_EMPTY(&egc->ev_immediates)) {
+        ei = LIBXL_STAILQ_FIRST(&egc->ev_immediates);
+        LIBXL_STAILQ_REMOVE_HEAD(&egc->ev_immediates, entry);
+        CTX_LOCK;
+        /* This callback is internal to libxl and expects CTX to be
+         * locked. */
+        ei->callback(egc, ei);
+        CTX_UNLOCK;
+    }
+
+    LIBXL_TAILQ_FOREACH_SAFE(ev, &egc->occurred_for_callback, link, ev_tmp) {
+        LIBXL_TAILQ_REMOVE(&egc->occurred_for_callback, ev, link);
+        LOG(DEBUG,"event %p callback type=%s",
+            ev, libxl_event_type_to_string(ev->type));
+        CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev);
+    }
+
+    LIBXL_TAILQ_FOREACH_SAFE(aop, &egc->aops_for_callback, entry, aop_tmp) {
+        LIBXL_TAILQ_REMOVE(&egc->aops_for_callback, aop, entry);
+        LOG(DEBUG,"ao %p: progress report: callback aop=%p", aop->ao, aop);
+        aop->how->callback(CTX, aop->ev, aop->how->for_callback);
+
+        CTX_LOCK;
+        assert(aop->ao->magic == LIBXL__AO_MAGIC);
+        aop->ao->progress_reports_outstanding--;
+        libxl__ao_complete_check_progress_reports(egc, aop->ao);
+        CTX_UNLOCK;
+    }
+
+    libxl__ao *ao, *ao_tmp;
+    LIBXL_TAILQ_FOREACH_SAFE(ao, &egc->aos_for_callback,
+                             entry_for_callback, ao_tmp) {
+        LIBXL_TAILQ_REMOVE(&egc->aos_for_callback, ao, entry_for_callback);
+        LOG(DEBUG,"ao %p: completion callback", ao);
+        ao->how.callback(CTX, ao->rc, ao->how.u.for_callback);
+        CTX_LOCK;
+        ao->notified = 1;
+        ao__check_destroy(CTX, ao);
+        CTX_UNLOCK;
+    }
+}
+
+void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc)
+{
+    EGC_GC;
+    egc_run_callbacks(egc);
+
+    libxl__free_all(gc);
+}
+
+/*
+ * Event retrieval etc.
+ */
+
+void libxl_event_register_callbacks(libxl_ctx *ctx,
+                  const libxl_event_hooks *hooks, void *user)
+{
+    ctx->event_hooks = hooks;
+    ctx->event_hooks_user = user;
+}
+
+void libxl__event_occurred(libxl__egc *egc, libxl_event *event)
+{
+    EGC_GC;
+
+    if (CTX->event_hooks &&
+        (CTX->event_hooks->event_occurs_mask & (1UL << event->type))) {
+        /* libxl__egc_cleanup will call the callback, just before exit
+         * from libxl.  This helps avoid reentrancy bugs: parts of
+         * libxl that call libxl__event_occurred do not have to worry
+         * that libxl might be reentered at that point. */
+        LIBXL_TAILQ_INSERT_TAIL(&egc->occurred_for_callback, event, link);
+        return;
+    } else {
+        libxl__poller *poller;
+        LIBXL_TAILQ_INSERT_TAIL(&CTX->occurred, event, link);
+        LIBXL_LIST_FOREACH(poller, &CTX->pollers_event, entry)
+            libxl__poller_wakeup(gc, poller);
+    }
+}
+
+void libxl_event_free(libxl_ctx *ctx, libxl_event *event)
+{
+    /* Exceptionally, this function may be called from libxl, with ctx==0 */
+    libxl_event_dispose(event);
+    free(event);
+}
+
+libxl_event *libxl__event_new(libxl__egc *egc,
+                              libxl_event_type type, uint32_t domid,
+                              libxl_ev_user for_user)
+{
+    EGC_GC;
+    libxl_event *ev;
+
+    ev = libxl__zalloc(NOGC,sizeof(*ev));
+
+    libxl_event_init(ev);
+    libxl_event_init_type(ev, type);
+
+    ev->domid = domid;
+    ev->for_user = for_user;
+
+    return ev;
+}
+
+static int event_check_internal(libxl__egc *egc, libxl_event **event_r,
+                                unsigned long typemask,
+                                libxl_event_predicate *pred, void *pred_user)
+{
+    EGC_GC;
+    libxl_event *ev;
+    int rc;
+
+    LIBXL_TAILQ_FOREACH(ev, &CTX->occurred, link) {
+        if (!(typemask & ((uint64_t)1 << ev->type)))
+            continue;
+
+        if (pred && !pred(ev, pred_user))
+            continue;
+
+        /* got one! */
+        LIBXL_TAILQ_REMOVE(&CTX->occurred, ev, link);
+        *event_r = ev;
+        rc = 0;
+        goto out;
+    }
+    rc = ERROR_NOT_READY;
+
+ out:
+    return rc;
+}
+
+int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
+                      uint64_t typemask,
+                      libxl_event_predicate *pred, void *pred_user)
+{
+    EGC_INIT(ctx);
+    CTX_LOCK;
+    int rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
+    CTX_UNLOCK_EGC_FREE;
+    return rc;
+}
+
+/*
+ * Utilities for pipes (specifically, useful for self-pipes)
+ */
+
+void libxl__pipe_close(int fds[2])
+{
+    if (fds[0] >= 0) close(fds[0]);
+    if (fds[1] >= 0) close(fds[1]);
+    fds[0] = fds[1] = -1;
+}
+
+int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2])
+{
+    int r, rc;
+
+    r = libxl_pipe(ctx, fds);
+    if (r) {
+        fds[0] = fds[1] = -1;
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl_fd_set_nonblock(ctx, fds[0], 1);
+    if (rc) goto out;
+
+    rc = libxl_fd_set_nonblock(ctx, fds[1], 1);
+    if (rc) goto out;
+
+    return 0;
+
+ out:
+    libxl__pipe_close(fds);
+    return rc;
+}
+
+int libxl__self_pipe_wakeup(int fd)
+{
+    /* Called from signal handlers, so needs to be async-signal-safe */
+    static const char buf[1] = "";
+
+    for (;;) {
+        int r = write(fd, buf, 1);
+        if (r==1) return 0;
+        assert(r==-1);
+        if (errno == EINTR) continue;
+        if (errno == EWOULDBLOCK) return 0;
+        if (!errno) abort();
+        return errno;
+    }
+}
+
+int libxl__self_pipe_eatall(int fd)
+{
+    char buf[256];
+    for (;;) {
+        int r = read(fd, buf, sizeof(buf));
+        if (r == sizeof(buf)) continue;
+        if (r >= 0) return 0;
+        assert(r == -1);
+        if (errno == EINTR) continue;
+        if (errno == EWOULDBLOCK) return 0;
+        assert(errno);
+        return errno;
+    }
+}
+
+/*
+ * Manipulation of pollers
+ */
+
+int libxl__poller_init(libxl__gc *gc, libxl__poller *p)
+{
+    int rc;
+    p->fd_polls = 0;
+    p->fd_rindices = 0;
+    p->fds_deregistered = 0;
+
+    rc = libxl__pipe_nonblock(CTX, p->wakeup_pipe);
+    if (rc) goto out;
+
+    return 0;
+
+ out:
+    libxl__poller_dispose(p);
+    return rc;
+}
+
+void libxl__poller_dispose(libxl__poller *p)
+{
+    libxl__pipe_close(p->wakeup_pipe);
+    free(p->fd_polls);
+    free(p->fd_rindices);
+}
+
+libxl__poller *libxl__poller_get(libxl__gc *gc)
+{
+    /* must be called with ctx locked */
+    int rc;
+
+    libxl__poller *p = LIBXL_LIST_FIRST(&CTX->pollers_idle);
+    if (p) {
+        LIBXL_LIST_REMOVE(p, entry);
+    } else {
+        p = libxl__zalloc(NOGC, sizeof(*p));
+
+        rc = libxl__poller_init(gc, p);
+        if (rc) {
+            free(p);
+            return NULL;
+        }
+    }
+
+    LIBXL_LIST_INSERT_HEAD(&CTX->pollers_active, p,
+                           active_entry);
+    return p;
+}
+
+void libxl__poller_put(libxl_ctx *ctx, libxl__poller *p)
+{
+    if (!p) return;
+    LIBXL_LIST_REMOVE(p, active_entry);
+    LIBXL_LIST_INSERT_HEAD(&ctx->pollers_idle, p, entry);
+}
+
+void libxl__poller_wakeup(libxl__gc *gc, libxl__poller *p)
+{
+    if (p->pipe_nonempty) return;
+    p->pipe_nonempty = 1;
+    int e = libxl__self_pipe_wakeup(p->wakeup_pipe[1]);
+    if (e) LIBXL__EVENT_DISASTER(gc, "cannot poke watch pipe", e, 0);
+}
+
+/*
+ * Main event loop iteration
+ */
+
+static int eventloop_iteration(libxl__egc *egc, libxl__poller *poller) {
+    /* The CTX must be locked EXACTLY ONCE so that this function
+     * can unlock it when it polls.
+     */
+    EGC_GC;
+    int rc, nfds;
+    struct timeval now;
+    
+    rc = libxl__gettimeofday(gc, &now);
+    if (rc) goto out;
+
+    int timeout;
+
+    for (;;) {
+        nfds = poller->fd_polls_allocd;
+        timeout = -1;
+        rc = beforepoll_internal(gc, poller, &nfds, poller->fd_polls,
+                                 &timeout, now);
+        if (!rc) break;
+        if (rc != ERROR_BUFFERFULL) goto out;
+
+        struct pollfd *newarray =
+            (nfds > INT_MAX / sizeof(struct pollfd) / 2) ? 0 :
+            libxl__realloc(NOGC, poller->fd_polls, sizeof(*newarray) * nfds);
+
+        if (!newarray) { rc = ERROR_NOMEM; goto out; }
+
+        poller->fd_polls = newarray;
+        poller->fd_polls_allocd = nfds;
+    }
+
+    CTX_UNLOCK;
+    rc = poll(poller->fd_polls, nfds, timeout);
+    CTX_LOCK;
+
+    if (rc < 0) {
+        if (errno == EINTR)
+            return 0; /* will go round again if caller requires */
+
+        LOGEV(ERROR, errno, "poll failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__gettimeofday(gc, &now);
+    if (rc) goto out;
+
+    afterpoll_internal(egc, poller, nfds, poller->fd_polls, now);
+
+    rc = 0;
+ out:
+    return rc;
+}
+
+int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
+                     uint64_t typemask,
+                     libxl_event_predicate *pred, void *pred_user)
+{
+    int rc;
+    libxl__poller *poller = NULL;
+
+    EGC_INIT(ctx);
+    CTX_LOCK;
+
+    poller = libxl__poller_get(gc);
+    if (!poller) { rc = ERROR_FAIL; goto out; }
+
+    for (;;) {
+        rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
+        if (rc != ERROR_NOT_READY) goto out;
+
+        rc = eventloop_iteration(egc, poller);
+        if (rc) goto out;
+
+        /* we unlock and cleanup the egc each time we go through this
+         * loop, so that (a) we don't accumulate garbage and (b) any
+         * events which are to be dispatched by callback are actually
+         * delivered in a timely fashion.  _1_baton will be
+         * called to pass the baton iff we actually leave; otherwise
+         * we are still carrying it.
+         */
+        CTX_UNLOCK;
+        libxl__egc_cleanup_2_ul_cb_gc(egc);
+        CTX_LOCK;
+    }
+
+ out:
+    libxl__poller_put(ctx, poller);
+
+    CTX_UNLOCK_EGC_FREE;
+    return rc;
+}
+
+
+
+/*
+ * The two possible state flow of an ao:
+ *
+ * Completion before initiator return:
+ *
+ *     Initiator thread                       Possible other threads
+ *
+ *   * ao_create allocates memory and
+ *     initialises the struct
+ *
+ *   * the initiator function does its
+ *     work, setting up various internal
+ *     asynchronous operations -----------> * asynchronous operations
+ *                                            start to take place and
+ *                                            might cause ao completion
+ *                                                |
+ *   * initiator calls ao_inprogress              |
+ *     - if synchronous, run event loop           |
+ *       until the ao completes                   |
+ *                              - ao completes on some thread
+ *                              - completing thread releases the lock
+ *                     <--------------'
+ *     - ao_inprogress takes the lock
+ *     - destroy the ao
+ *
+ *
+ * Completion after initiator return (asynch. only):
+ *
+ *
+ *     Initiator thread                       Possible other threads
+ *
+ *   * ao_create allocates memory and
+ *     initialises the struct
+ *
+ *   * the initiator function does its
+ *     work, setting up various internal
+ *     asynchronous operations -----------> * asynchronous operations
+ *                                            start to take place and
+ *                                            might cause ao completion
+ *                                                |
+ *   * initiator calls ao_inprogress              |
+ *     - observes event not yet done,             |
+ *     - returns to caller                        |
+ *                                                |
+ *                              - ao completes on some thread
+ *                              - generate the event or call the callback
+ *                              - destroy the ao
+ */
+
+
+/*
+ * A "manip" is a libxl public function manipulating this ao, which
+ * has a pointer to it.  We have to not destroy it while that's the
+ * case, obviously.  Callers must have the ctx locked, obviously.
+ */
+static void ao__manip_enter(libxl__ao *ao)
+{
+    assert(ao->manip_refcnt < INT_MAX);
+    ao->manip_refcnt++;
+}
+
+static void ao__manip_leave(libxl_ctx *ctx, libxl__ao *ao)
+{
+    assert(ao->manip_refcnt > 0);
+    ao->manip_refcnt--;
+    ao__check_destroy(ctx, ao);
+}
+
+static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao)
+{
+    if (!ao->manip_refcnt && ao->notified) {
+        assert(ao->complete);
+        libxl__ao__destroy(ctx, ao);
+    }
+}
+
+void libxl__ao__destroy(libxl_ctx *ctx, libxl__ao *ao)
+{
+    AO_GC;
+    if (!ao) return;
+    LOG(DEBUG,"ao %p: destroy",ao);
+    libxl__poller_put(ctx, ao->poller);
+    ao->magic = LIBXL__AO_MAGIC_DESTROYED;
+    libxl__free_all(&ao->gc);
+    free(ao);
+}
+
+void libxl__ao_create_fail(libxl__ao *ao)
+{
+    AO_GC;
+    LOG(DEBUG,"ao %p: create fail",ao);
+    assert(ao->magic == LIBXL__AO_MAGIC);
+    assert(ao->in_initiator);
+    assert(!ao->complete);
+    assert(!ao->progress_reports_outstanding);
+    assert(!ao->aborting);
+    LIBXL_LIST_REMOVE(ao, inprogress_entry);
+    libxl__ao__destroy(CTX, ao);
+}
+
+libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao)
+{
+    assert(ao);
+    assert(ao->magic == LIBXL__AO_MAGIC);
+    assert(!ao->complete);
+    return &ao->gc;
+}
+
+void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc)
+{
+    AO_GC;
+    LOG(DEBUG,"ao %p: complete, rc=%d",ao,rc);
+    assert(ao->magic == LIBXL__AO_MAGIC);
+    assert(!ao->complete);
+    assert(!ao->nested_root);
+    assert(!ao->nested_progeny);
+    ao->complete = 1;
+    ao->rc = rc;
+    LIBXL_LIST_REMOVE(ao, inprogress_entry);
+    if (ao->outstanding_killed_child)
+        LOG(DEBUG, "ao %p: .. but waiting for %d fork to exit",
+            ao, ao->outstanding_killed_child);
+    libxl__ao_complete_check_progress_reports(egc, ao);
+}
+
+static bool ao_work_outstanding(libxl__ao *ao)
+{
+    /*
+     * We don't consider an ao complete if it has any outstanding
+     * callbacks.  These callbacks might be outstanding on other
+     * threads, queued up in the other threads' egc's.  Those threads
+     * will, after making the callback, take out the lock again,
+     * decrement progress_reports_outstanding, and call
+     * libxl__ao_complete_check_progress_reports.
+     */
+    return !ao->complete || ao->progress_reports_outstanding
+        || ao->outstanding_killed_child;
+}
+
+void libxl__ao_complete_check_progress_reports(libxl__egc *egc, libxl__ao *ao)
+{
+    EGC_GC;
+    libxl_ctx *ctx = libxl__gc_owner(&egc->gc);
+    assert(ao->progress_reports_outstanding >= 0);
+
+    if (ao_work_outstanding(ao))
+        return;
+
+    if (ao->poller) {
+        assert(ao->in_initiator);
+        if (!ao->constructing)
+            /* don't bother with this if we're not in the event loop */
+            libxl__poller_wakeup(gc, ao->poller);
+    } else if (ao->how.callback) {
+        LOG(DEBUG, "ao %p: complete for callback", ao);
+        LIBXL_TAILQ_INSERT_TAIL(&egc->aos_for_callback, ao, entry_for_callback);
+    } else {
+        libxl_event *ev;
+        ev = NEW_EVENT(egc, OPERATION_COMPLETE, ao->domid, ao->how.u.for_event);
+        if (ev) {
+            ev->u.operation_complete.rc = ao->rc;
+            libxl__event_occurred(egc, ev);
+        }
+        ao->notified = 1;
+    }
+    
+    ao__check_destroy(ctx, ao);
+}
+
+libxl__ao *libxl__ao_create(libxl_ctx *ctx, uint32_t domid,
+                            const libxl_asyncop_how *how,
+                            const char *file, int line, const char *func)
+{
+    libxl__ao *ao;
+
+    ao = calloc(1, sizeof(*ao));
+    if (!ao) goto out;
+
+    ao->magic = LIBXL__AO_MAGIC;
+    ao->constructing = 1;
+    ao->in_initiator = 1;
+    ao__manip_enter(ao);
+    ao->poller = 0;
+    ao->domid = domid;
+    LIBXL_INIT_GC(ao->gc, ctx);
+
+    if (how) {
+        ao->how = *how;
+    } else {
+        ao->poller = libxl__poller_get(&ao->gc);
+        if (!ao->poller) goto out;
+    }
+    libxl__log(ctx,XTL_DEBUG,-1,file,line,func,domid,
+               "ao %p: create: how=%p callback=%p poller=%p",
+               ao, how, ao->how.callback, ao->poller);
+
+    LIBXL_LIST_INSERT_HEAD(&ctx->aos_inprogress, ao, inprogress_entry);
+
+    return ao;
+
+ out:
+    if (ao) libxl__ao__destroy(ctx, ao);
+    return NULL;
+}
+
+
+int libxl__ao_inprogress(libxl__ao *ao,
+                         const char *file, int line, const char *func)
+{
+    AO_GC;
+    int rc;
+    uint32_t domid = ao->domid;
+
+    assert(ao->magic == LIBXL__AO_MAGIC);
+    assert(ao->constructing);
+    assert(ao->in_initiator);
+    ao->constructing = 0;
+
+    if (ao->nested_root)
+        domid = ao->nested_root->domid;
+
+    libxl__log(CTX,XTL_DEBUG,-1,file,line,func,domid,
+               "ao %p: inprogress: poller=%p, flags=%s%s%s%s",
+               ao, ao->poller,
+               ao->constructing ? "o" : "",
+               ao->in_initiator ? "i" : "",
+               ao->complete ? "c" : "",
+               ao->notified ? "n" : "");
+
+    if (ao->poller) {
+        /* Caller wants it done synchronously. */
+        /* We use a fresh gc, so that we can free things
+         * each time round the loop. */
+        libxl__egc egc;
+        LIBXL_INIT_EGC(egc,CTX);
+
+        for (;;) {
+            assert(ao->magic == LIBXL__AO_MAGIC);
+
+            if (!ao_work_outstanding(ao)) {
+                rc = ao->rc;
+                ao->notified = 1;
+                break;
+            }
+
+            DBG("ao %p: not ready, waiting",ao);
+
+            rc = eventloop_iteration(&egc,ao->poller);
+            if (rc) {
+                /* Oh dear, this is quite unfortunate. */
+                LOG(ERROR,
+                    "Error waiting for"" event during long-running operation (rc=%d)",
+                    rc);
+                sleep(1);
+                /* It's either this or return ERROR_I_DONT_KNOW_WHETHER
+                 * _THE_THING_YOU_ASKED_FOR_WILL_BE_DONE_LATER_WHEN
+                 * _YOU_DIDNT_EXPECT_IT, since we don't have a
+                 * synchronous cancellation ability. */
+            }
+
+            /* The call to egc..1_baton is below, only if we are leaving. */
+            CTX_UNLOCK;
+            libxl__egc_cleanup_2_ul_cb_gc(&egc);
+            CTX_LOCK;
+        }
+
+        /* Dispose of this early so libxl__egc_ao_cleanup_1_baton
+         * doesn't mistake us for a baton-holder.  No-one much is
+         * going to look at this ao now so setting this to 0 is fine.
+         * We can't call _baton below _leave because _leave destroys
+         * our gc, which _baton needs. */
+        libxl__poller_put(CTX, ao->poller);
+        ao->poller = 0;
+    } else {
+        rc = 0;
+    }
+
+    libxl__egc_ao_cleanup_1_baton(gc);
+    ao->in_initiator = 0;
+    ao__manip_leave(CTX, ao);
+
+    return rc;
+}
+
+
+/* abort requests */
+
+static int ao__abort(libxl_ctx *ctx, libxl__ao *parent)
+/* Temporarily unlocks ctx, which must be locked exactly once on entry. */
+{
+    libxl__egc egc;
+    LIBXL_INIT_EGC(egc,ctx);
+
+    int rc;
+    ao__manip_enter(parent);
+
+    if (parent->aborting) {
+        rc = ERROR_ABORTED;
+        goto out;
+    }
+
+    parent->aborting = 1;
+
+    if (LIBXL_LIST_EMPTY(&parent->abortables)) {
+        LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
+                   "ao %p: abort requested and noted, but no-one interested",
+                   parent);
+        rc = 0;
+        goto out;
+    }
+
+    /* We keep calling abort hooks until there are none left */
+    while (!LIBXL_LIST_EMPTY(&parent->abortables)) {
+        assert(!parent->complete);
+
+        libxl__ao_abortable *abrt = LIBXL_LIST_FIRST(&parent->abortables);
+        assert(parent == ao_nested_root(abrt->ao));
+
+        LIBXL_LIST_REMOVE(abrt, entry);
+        abrt->registered = 0;
+
+        LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
+                   "ao %p: abrt=%p: aborting", parent, abrt->ao);
+        abrt->callback(&egc, abrt, ERROR_ABORTED);
+
+        /* The call to egc..1_baton is in the out block below. */
+        libxl__ctx_unlock(ctx);
+        libxl__egc_cleanup_2_ul_cb_gc(&egc);
+        libxl__ctx_lock(ctx);
+    }
+
+    rc = 0;
+
+ out:
+    libxl__egc_ao_cleanup_1_baton(&egc.gc);
+    ao__manip_leave(ctx, parent);
+    /* The call to egc..2_ul_cb_gc is above.  This is sufficient
+     * because only code inside the loop adds anything to the egc, and
+     * we ensures that the egc is clean when we leave the loop. */
+    return rc;
+}
+
+int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
+{
+    libxl__ao *search;
+    libxl__ctx_lock(ctx);
+    int rc;
+
+    LIBXL_LIST_FOREACH(search, &ctx->aos_inprogress, inprogress_entry) {
+        if (how) {
+            /* looking for ao to be reported by callback or event */
+            if (search->poller)
+                /* sync */
+                continue;
+            if (how->callback != search->how.callback)
+                continue;
+            if (how->callback
+                ? (how->u.for_callback != search->how.u.for_callback)
+                : (how->u.for_event != search->how.u.for_event))
+                continue;
+        } else {
+            /* looking for synchronous call */
+            if (!search->poller)
+                /* async */
+                continue;
+        }
+        goto found;
+    }
+    rc = ERROR_NOTFOUND;
+    goto out;
+
+ found:
+    rc = ao__abort(ctx, search);
+ out:
+    libxl__ctx_unlock(ctx);
+    return rc;
+}
+
+int libxl__ao_aborting(libxl__ao *ao)
+{
+    libxl__ao *root = ao_nested_root(ao);
+    AO_GC;
+
+    if (root->aborting) {
+        DBG("ao=%p: aborting at explicit check (root=%p)", ao, root);
+        return ERROR_ABORTED;
+    }
+
+    return 0;
+}
+
+int libxl__ao_abortable_register(libxl__ao_abortable *abrt)
+{
+    libxl__ao *ao = abrt->ao;
+    libxl__ao *root = ao_nested_root(ao);
+    AO_GC;
+
+    if (root->aborting) {
+ DBG("ao=%p: preemptively aborting ao_abortable registration %p (root=%p)",
+            ao, abrt, root);
+        return ERROR_ABORTED;
+    }
+
+    DBG("ao=%p, abrt=%p: registering (root=%p)", ao, abrt, root);
+    LIBXL_LIST_INSERT_HEAD(&root->abortables, abrt, entry);
+    abrt->registered = 1;
+
+    return 0;
+}
+
+_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable *abrt)
+{
+    if (!abrt->registered)
+        return;
+
+    libxl__ao *ao = abrt->ao;
+    libxl__ao *root __attribute__((unused)) = ao_nested_root(ao);
+    AO_GC;
+
+    DBG("ao=%p, abrt=%p: deregistering (root=%p)", ao, abrt, root);
+    LIBXL_LIST_REMOVE(abrt, entry);
+    abrt->registered = 0;
+}
+
+
+/* progress reporting */
+
+/* The application indicates a desire to ignore events by passing NULL
+ * for how.  But we want to copy *how.  So we have this dummy function
+ * whose address is stored in callback if the app passed how==NULL. */
+static void dummy_asyncprogress_callback_ignore
+  (libxl_ctx *ctx, libxl_event *ev, void *for_callback) { }
+
+void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
+                               const libxl_asyncprogress_how *from_app) {
+    if (from_app)
+        *in_state = *from_app;
+    else
+        in_state->callback = dummy_asyncprogress_callback_ignore;
+}
+
+void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
+        const libxl_asyncprogress_how *how, libxl_event *ev)
+{
+    AO_GC;
+    assert(!ao->nested_root);
+    if (how->callback == dummy_asyncprogress_callback_ignore) {
+        LOG(DEBUG,"ao %p: progress report: ignored",ao);
+        libxl_event_free(CTX,ev);
+        /* ignore */
+    } else if (how->callback) {
+        libxl__aop_occurred *aop = libxl__zalloc(&egc->gc, sizeof(*aop));
+        ao->progress_reports_outstanding++;
+        aop->ao = ao;
+        aop->ev = ev;
+        aop->how = how;
+        LIBXL_TAILQ_INSERT_TAIL(&egc->aops_for_callback, aop, entry);
+        LOG(DEBUG,"ao %p: progress report: callback queued aop=%p",ao,aop);
+    } else {
+        LOG(DEBUG,"ao %p: progress report: event queued ev=%p type=%s",
+            ao, ev, libxl_event_type_to_string(ev->type));
+        libxl__event_occurred(egc, ev);
+    }
+}
+
+
+/* nested ao */
+
+static libxl__ao *ao_nested_root(libxl__ao *ao) {
+    libxl__ao *root = ao->nested_root ? : ao;
+    assert(!root->nested_root);
+    return root;
+}
+
+_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent)
+{
+    libxl__ao *child = NULL, *root;
+    libxl_ctx *ctx = libxl__gc_owner(&parent->gc);
+
+    assert(parent->magic == LIBXL__AO_MAGIC);
+    root = ao_nested_root(parent);
+
+    child = libxl__zalloc(&ctx->nogc_gc, sizeof(*child));
+    child->magic = LIBXL__AO_MAGIC;
+    child->nested_root = root;
+    assert(root->nested_progeny < INT_MAX);
+    root->nested_progeny++;
+    LIBXL_INIT_GC(child->gc, ctx);
+    libxl__gc *gc = &child->gc;
+
+    LOG(DEBUG,"ao %p: nested ao, parent %p", child, parent);
+    return child;
+}
+
+_hidden void libxl__nested_ao_free(libxl__ao *child)
+{
+    assert(child->magic == LIBXL__AO_MAGIC);
+    libxl__ao *root = child->nested_root;
+    assert(root);
+    assert(root->nested_progeny > 0);
+    root->nested_progeny--;
+    libxl_ctx *ctx = libxl__gc_owner(&child->gc);
+    libxl__ao__destroy(ctx, child);
+}
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_exec.c b/tools/libs/light/libxl_exec.c
new file mode 100644 (file)
index 0000000..47c9c8f
--- /dev/null
@@ -0,0 +1,473 @@
+
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+static void check_open_fds(const char *what)
+{
+    const char *env_debug;
+    int debug;
+    int i, flags, badness = 0;
+
+    env_debug = getenv("_LIBXL_DEBUG_EXEC_FDS");
+    if (!env_debug) return;
+
+    debug = strtol(env_debug, (char **) NULL, 10);
+    if (debug <= 0) return;
+
+    for (i = 4; i < 256; i++) {
+#ifdef __linux__
+        ssize_t len;
+        char path[PATH_MAX];
+        char linkpath[PATH_MAX+1];
+#endif
+        flags = fcntl(i, F_GETFD);
+        if ( flags == -1 ) {
+            if ( errno != EBADF )
+                fprintf(stderr, "libxl: execing %s: fd %d flags returned %s (%d)\n",
+                        what, i, strerror(errno), errno);
+            continue;
+        }
+
+        if ( flags & FD_CLOEXEC )
+            continue;
+
+        badness++;
+
+#ifdef __linux__
+        snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), i);
+        len = readlink(path, linkpath, PATH_MAX);
+        if (len > 0) {
+            linkpath[len] = '\0';
+            fprintf(stderr, "libxl: execing %s: fd %d is open to %s with flags %#x\n",
+                    what, i, linkpath, flags);
+        } else
+#endif
+            fprintf(stderr, "libxl: execing %s: fd %d is open with flags %#x\n",
+                    what, i, flags);
+    }
+    if (debug < 2) return;
+    if (badness) abort();
+}
+
+void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd,
+                 const char *arg0, char *const args[], char *const env[])
+     /* call this in the child */
+{
+    if (stdinfd != -1)
+        dup2(stdinfd, STDIN_FILENO);
+    if (stdoutfd != -1)
+        dup2(stdoutfd, STDOUT_FILENO);
+    if (stderrfd != -1)
+        dup2(stderrfd, STDERR_FILENO);
+
+    if (stdinfd > 2)
+        close(stdinfd);
+    if (stdoutfd > 2 && stdoutfd != stdinfd)
+        close(stdoutfd);
+    if (stderrfd > 2 && stderrfd != stdinfd && stderrfd != stdoutfd)
+        close(stderrfd);
+
+    check_open_fds(arg0);
+
+    signal(SIGPIPE, SIG_DFL);
+    /* in case our caller set it to IGN.  subprocesses are entitled
+     * to assume they got DFL. */
+
+    if (env != NULL) {
+        for (int i = 0; env[i] != NULL && env[i+1] != NULL; i += 2) {
+            if (setenv(env[i], env[i+1], 1) < 0) {
+                LOGEV(ERROR, errno, "setting env vars (%s = %s)",
+                                    env[i], env[i+1]);
+                goto out;
+            }
+        }
+    }
+    execvp(arg0, args);
+
+out:
+    fprintf(stderr, "libxl: cannot execute %s: %s\n", arg0, strerror(errno));
+    _exit(-1);
+}
+
+void libxl_report_child_exitstatus(libxl_ctx *ctx,
+                                   xentoollog_level level,
+                                   const char *what, pid_t pid, int status)
+{
+
+    if (WIFEXITED(status)) {
+        int st = WEXITSTATUS(status);
+        if (st)
+            LIBXL__LOG(ctx, level, "%s [%ld] exited"
+                   " with error status %d", what, (unsigned long)pid, st);
+        else
+            LIBXL__LOG(ctx, level, "%s [%ld] unexpectedly"
+                   " exited status zero", what, (unsigned long)pid);
+    } else if (WIFSIGNALED(status)) {
+        int sig = WTERMSIG(status);
+        const char *str = strsignal(sig);
+        const char *coredump = WCOREDUMP(status) ? " (core dumped)" : "";
+        if (str)
+            LIBXL__LOG(ctx, level, "%s [%ld] died due to"
+                   " fatal signal %s%s", what, (unsigned long)pid,
+                   str, coredump);
+        else
+            LIBXL__LOG(ctx, level, "%s [%ld] died due to unknown"
+                   " fatal signal number %d%s", what, (unsigned long)pid,
+                   sig, coredump);
+    } else {
+        LIBXL__LOG(ctx, level, "%s [%ld] gave unknown"
+               " wait status 0x%x", what, (unsigned long)pid, status);
+    }
+}
+
+int libxl__spawn_record_pid(libxl__gc *gc, libxl__spawn_state *spawn, pid_t pid)
+{
+    int r, rc;
+
+    rc = libxl__ev_child_xenstore_reopen(gc, spawn->what);
+    if (rc) goto out;
+
+    r = libxl__xs_printf(gc, XBT_NULL, spawn->pidpath, "%d", pid);
+    if (r) {
+        LOGE(ERROR,
+             "write %s = %d: xenstore write failed", spawn->pidpath, pid);
+        rc = ERROR_FAIL;  goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc ? SIGTERM : 0;
+}
+
+int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
+                                 uint32_t domid,
+                                 uint32_t timeout, char *what,
+                                 char *path, char *state,
+                                 libxl__spawn_starting *spawning,
+                                 int (*check_callback)(libxl__gc *gc,
+                                                       uint32_t domid,
+                                                       const char *state,
+                                                       void *userdata),
+                                 void *check_callback_userdata)
+{
+    char *p;
+    unsigned int len;
+    int rc = 0;
+    struct xs_handle *xsh;
+    int nfds;
+    fd_set rfds;
+    struct timeval tv;
+    unsigned int num;
+    char **l = NULL;
+
+    xsh = xs_daemon_open();
+    if (xsh == NULL) {
+        LOG(ERROR, "Unable to open xenstore connection");
+        goto err;
+    }
+
+    xs_watch(xsh, path, path);
+    tv.tv_sec = timeout;
+    tv.tv_usec = 0;
+    nfds = xs_fileno(xsh) + 1;
+    assert(!spawning);
+
+    while (rc > 0 || (!rc && tv.tv_sec > 0)) {
+        p = xs_read(xsh, XBT_NULL, path, &len);
+        if ( NULL == p )
+            goto again;
+
+        if ( NULL != state && strcmp(p, state) )
+            goto again;
+
+        if ( NULL != check_callback ) {
+            rc = (*check_callback)(gc, domid, p, check_callback_userdata);
+            if ( rc > 0 )
+                goto again;
+        }
+
+        free(p);
+        xs_unwatch(xsh, path, path);
+        xs_daemon_close(xsh);
+        return rc;
+again:
+        free(p);
+        FD_ZERO(&rfds);
+        FD_SET(xs_fileno(xsh), &rfds);
+        rc = select(nfds, &rfds, NULL, NULL, &tv);
+        if (rc > 0) {
+            if (FD_ISSET(xs_fileno(xsh), &rfds)) {
+                l = xs_read_watch(xsh, &num);
+                if (l != NULL)
+                    free(l);
+                else
+                    goto again;
+            }
+        }
+    }
+    LOG(ERROR, "%s not ready", what);
+
+    xs_unwatch(xsh, path, path);
+    xs_daemon_close(xsh);
+err:
+    return -1;
+}
+
+
+/*----- spawn implementation -----*/
+
+/*
+ * Full set of possible states of a libxl__spawn_state and its _detachable:
+ *
+ *                   detaching rc      mid     timeout      xswatch
+ *  - Undefined         undef   undef   -        undef        undef
+ *  - Idle              any     any     Idle     Idle         Idle
+ *  - Attached OK       0       0       Active   Active       Active
+ *  - Attached Failed   0       non-0   Active   Idle         Idle
+ *  - Detaching         1       maybe   Active   Idle         Idle
+ *  - Partial           any     any     Idle     Active/Idle  Active/Idle
+ *
+ * When in states Detaching or Attached Failed, the middle process has
+ * been sent a SIGKILL.
+ *
+ * The difference between Attached OK and Attached Failed is not
+ * directly visible to callers - callers see these two the same,
+ * although of course Attached OK will hopefully eventually result in
+ * a call to detached_cb, whereas Attached Failed will end up
+ * in a call to failure_cb.
+ */
+
+/* Event callbacks. */
+static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
+                              int rc, const char *xsdata);
+static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
+                               pid_t pid, int status);
+
+/* Precondition: Partial.  Results: Idle. */
+static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss);
+
+/* Precondition: Attached or Detaching; caller has logged failure reason.
+ * Results: Detaching, or Attached Failed */
+static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc);
+
+void libxl__spawn_init(libxl__spawn_state *ss)
+{
+    libxl__ev_child_init(&ss->mid);
+    libxl__xswait_init(&ss->xswait);
+}
+
+int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss)
+{
+    STATE_AO_GC(ss->ao);
+    int r;
+    pid_t child;
+    int status, rc;
+
+    libxl__spawn_init(ss);
+    ss->rc = ss->detaching = 0;
+
+    ss->xswait.ao = ao;
+    ss->xswait.what = GCSPRINTF("%s startup", ss->what);
+    ss->xswait.path = ss->xspath;
+    ss->xswait.timeout_ms = ss->timeout_ms;
+    ss->xswait.callback = spawn_watch_event;
+    rc = libxl__xswait_start(gc, &ss->xswait);
+    if (rc) goto out_err;
+
+    pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death);
+    if (middle ==-1) { rc = ERROR_FAIL; goto out_err; }
+
+    if (middle) {
+        /* parent */
+        return 1;
+    }
+
+    /* we are now the middle process */
+
+    pid_t (*fork_replacement)(void*) =
+        CTX->childproc_hooks
+        ? CTX->childproc_hooks->fork_replacement
+        : 0;
+    child =
+        fork_replacement
+        ? fork_replacement(CTX->childproc_user)
+        : fork();
+
+    if (child == -1)
+        exit(255);
+    if (!child) {
+        return 0; /* caller runs child code */
+    }
+
+    int failsig = ss->midproc_cb(gc, ss, child);
+    if (failsig) {
+        kill(child, failsig);
+        _exit(127);
+    }
+
+    for (;;) {
+        pid_t got = waitpid(child, &status, 0);
+        if (got == -1) {
+            assert(errno == EINTR);
+            continue;
+        }
+        assert(got == child);
+        break;
+    }
+
+    r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) :
+         WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 :
+         -1);
+    _exit(r);
+
+ out_err:
+    spawn_cleanup(gc, ss);
+    return rc;
+}
+
+static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss)
+{
+    assert(!libxl__ev_child_inuse(&ss->mid));
+    libxl__xswait_stop(gc, &ss->xswait);
+}
+
+static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss)
+/* Precondition: Attached or Detaching, but caller must have just set
+ * at least one of detaching or rc.
+ * Results: Detaching or Attached Failed */
+{
+    int r;
+
+    assert(libxl__ev_child_inuse(&ss->mid));
+    assert(ss->detaching || ss->rc);
+    libxl__xswait_stop(gc, &ss->xswait);
+
+    pid_t child = ss->mid.pid;
+    r = kill(child, SIGKILL);
+    if (r && errno != ESRCH)
+        LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)",
+             ss->what, (unsigned long)child);
+}
+
+void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss)
+{
+    ss->detaching = 1;
+    spawn_detach(gc, ss);
+}
+
+void libxl__spawn_initiate_failure(libxl__egc *egc, libxl__spawn_state *ss,
+                                   int rc)
+/* The spawn state must be Attached on entry and will be Attached Failed
+ * on return.  */
+{
+    spawn_fail(egc, ss, rc);
+}
+
+static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc)
+/* Caller must have logged.  Must be last thing in calling function,
+ * as it may make the callback.  Precondition: Attached or Detaching. */
+{
+    EGC_GC;
+    assert(rc);
+    if (!ss->rc)
+        ss->rc = rc;
+    spawn_detach(gc, ss);
+}
+
+static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
+                              int rc, const char *p)
+{
+    /* On entry, is Attached. */
+    EGC_GC;
+    libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait);
+    if (rc) {
+        if (rc == ERROR_TIMEDOUT)
+            LOG(ERROR, "%s: startup timed out", ss->what);
+        spawn_fail(egc, ss, rc); /* must be last */
+        return;
+    }
+    LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p);
+    ss->confirm_cb(egc, ss, p); /* must be last */
+}
+
+static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
+                               pid_t pid, int status)
+    /* On entry, is Attached or Detaching */
+{
+    EGC_GC;
+    libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid);
+
+    if ((ss->rc || ss->detaching) &&
+        ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
+         (WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) {
+        /* as expected */
+        const char *what = GCSPRINTF("%s (dying as expected)", ss->what);
+        libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status);
+    } else if (!WIFEXITED(status)) {
+        int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR;
+        const char *what =
+            GCSPRINTF("%s intermediate process (startup monitor)", ss->what);
+        libxl_report_child_exitstatus(CTX, loglevel, what, pid, status);
+        ss->rc = ERROR_FAIL;
+    } else {
+        if (!status)
+            LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0,"
+                " when we were waiting for it to confirm startup",
+                ss->what, (unsigned long)pid);
+        else if (status <= 127)
+            LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d",
+                ss->what, (unsigned long)pid, status);
+        else if (status < 255) {
+            int sig = status - 128;
+            const char *str = strsignal(sig);
+            if (str)
+                LOG(ERROR, "%s [%ld]: died during startup due to fatal"
+                    " signal %s", ss->what, (unsigned long)pid, str);
+            else
+                LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal"
+                    " signal number %d", ss->what, (unsigned long)pid, sig);
+        }
+        ss->rc = ERROR_FAIL;
+    }
+
+    spawn_cleanup(gc, ss);
+
+    if (ss->rc && !ss->detaching) {
+        ss->failure_cb(egc, ss, ss->rc); /* must be last */
+        return;
+    }
+    
+    if (ss->rc && ss->detaching)
+        LOG(WARN,"%s underlying machinery seemed to fail (%d),"
+            " but its function seems to have been successful",
+            ss->what, ss->rc);
+
+    assert(ss->detaching);
+    ss->detached_cb(egc, ss);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_flask.c b/tools/libs/light/libxl_flask.c
new file mode 100644 (file)
index 0000000..38347a3
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Author: Machon Gregory, <mbgrego@tycho.ncsc.mil>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
+                               uint32_t *ssidref)
+{
+    int rc;
+
+    rc = xc_flask_context_to_sid(ctx->xch, buf, len, ssidref);
+
+    return rc;
+}
+
+int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref,
+                               char **buf, size_t *len)
+{
+    int rc;
+    char tmp[XC_PAGE_SIZE];
+
+    rc = xc_flask_sid_to_context(ctx->xch, ssidref, tmp, sizeof(tmp));
+
+    if (!rc) {
+        *len = strlen(tmp);
+        *buf = strdup(tmp);
+    }
+
+    return rc;
+}
+
+int libxl_flask_getenforce(libxl_ctx *ctx)
+{
+    int rc;
+
+    rc = xc_flask_getenforce(ctx->xch);
+
+    return rc;
+}
+
+int libxl_flask_setenforce(libxl_ctx *ctx, int mode)
+{
+    int rc;
+
+    rc = xc_flask_setenforce(ctx->xch, mode);
+
+    return rc;
+}
+
+int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size)
+{
+
+    int rc;
+
+    rc = xc_flask_load(ctx->xch, policy, size);
+
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_fork.c b/tools/libs/light/libxl_fork.c
new file mode 100644 (file)
index 0000000..9a4709b
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2012      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+/*
+ * Internal child process machinery for use by other parts of libxl
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*
+ * carefd arrangements
+ *
+ * carefd_begin and _unlock take out the no_forking lock, which we
+ * also take and release in our pthread_atfork handlers.  So when this
+ * lock is held the whole process cannot fork.  We therefore protect
+ * our fds from leaking into children made by other threads.
+ *
+ * We maintain a list of all the carefds, so that if the application
+ * wants to fork a long-running but non-execing child, we can close
+ * them all.
+ *
+ * So the record function sets CLOEXEC for the benefit of execing
+ * children, and makes a note of the fd for the benefit of non-execing
+ * ones.
+ */
+
+struct libxl__carefd {
+    LIBXL_LIST_ENTRY(libxl__carefd) entry;
+    int fd;
+};
+
+static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER;
+static int atfork_registered;
+static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
+    LIBXL_LIST_HEAD_INITIALIZER(carefds);
+
+/* Protected against concurrency by no_forking.  sigchld_users is
+ * protected against being interrupted by SIGCHLD (and thus read
+ * asynchronously by the signal handler) by sigchld_defer (see
+ * below). */
+static bool sigchld_installed; /* 0 means not */
+static pthread_mutex_t sigchld_defer_mutex = PTHREAD_MUTEX_INITIALIZER;
+static LIBXL_LIST_HEAD(, libxl_ctx) sigchld_users =
+    LIBXL_LIST_HEAD_INITIALIZER(sigchld_users);
+static struct sigaction sigchld_saved_action;
+
+static void sigchld_removehandler_core(void); /* idempotent */
+static void sigchld_user_remove(libxl_ctx *ctx); /* idempotent */
+static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old);
+
+static void defer_sigchld(void);
+static void release_sigchld(void);
+
+static void atfork_lock(void)
+{
+    int r = pthread_mutex_lock(&no_forking);
+    assert(!r);
+}
+
+static void atfork_unlock(void)
+{
+    int r = pthread_mutex_unlock(&no_forking);
+    assert(!r);
+}
+
+int libxl__atfork_init(libxl_ctx *ctx)
+{
+    int r, rc;
+    
+    atfork_lock();
+    if (atfork_registered) { rc = 0; goto out; }
+
+    r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock);
+    if (r) {
+        assert(r == ENOMEM);
+        libxl__alloc_failed(ctx, __func__, 0,0);
+    }
+
+    atfork_registered = 1;
+    rc = 0;
+ out:
+    atfork_unlock();
+    return rc;
+}
+
+void libxl__carefd_begin(void) { atfork_lock(); }
+void libxl__carefd_unlock(void) { atfork_unlock(); }
+
+libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd)
+{
+    libxl__carefd *cf = 0;
+
+    libxl_fd_set_cloexec(ctx, fd, 1);
+    cf = libxl__zalloc(&ctx->nogc_gc, sizeof(*cf));
+    cf->fd = fd;
+    LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry);
+    return cf;
+}
+
+libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd)
+{
+    libxl__carefd *cf = 0;
+    int saved_errno = errno;
+
+    if (fd >= 0)
+        cf = libxl__carefd_record(ctx, fd);
+    libxl__carefd_unlock();
+    errno = saved_errno;
+    return cf;
+}
+
+void libxl_postfork_child_noexec(libxl_ctx *ctx)
+{
+    /*
+     * Anything running without the no_forking lock (atfork_lock)
+     * might be interrupted by fork.  But libxl functions other than
+     * this one are then forbidden to the child.
+     *
+     * Conversely, this function might interrupt any other libxl
+     * operation (even though that other operation has the libxl ctx
+     * lock).  We don't take the lock ourselves, since we are running
+     * in the child and if the lock is held the thread that took it no
+     * longer exists.  To prevent us being interrupted by another call
+     * to ourselves (whether in another thread or by virtue of another
+     * fork) we take the atfork lock ourselves.
+     */
+    libxl__carefd *cf, *cf_tmp;
+    int r;
+
+    atfork_lock();
+
+    LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) {
+        if (cf->fd >= 0) {
+            r = close(cf->fd);
+            if (r)
+                LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING,
+                                 "failed to close fd=%d"
+                                 " (perhaps of another libxl ctx)", cf->fd);
+        }
+        free(cf);
+    }
+    LIBXL_LIST_INIT(&carefds);
+
+    if (sigchld_installed) {
+        /* We are in theory not at risk of concurrent execution of the
+         * SIGCHLD handler, because the application should call
+         * libxl_postfork_child_noexec before the child forks again.
+         * (If the SIGCHLD was in flight in the parent at the time of
+         * the fork, the thread it was delivered on exists only in the
+         * parent so is not our concern.)
+         *
+         * But in case the application violated this rule (and did so
+         * while multithreaded in the child), we use our deferral
+         * machinery.  The result is that the SIGCHLD may then be lost
+         * (i.e. signaled to the now-defunct libxl ctx(s)).  But at
+         * least we won't execute undefined behaviour (by examining
+         * the list in the signal handler concurrently with clearing
+         * it here), and since we won't actually reap the new children
+         * things will in fact go OK if the application doesn't try to
+         * use SIGCHLD, but instead just waits for the child(ren). */
+        defer_sigchld();
+
+        LIBXL_LIST_INIT(&sigchld_users);
+        /* After this the ->sigchld_user_registered entries in the
+         * now-obsolete contexts may be lies.  But that's OK because
+         * no-one will look at them. */
+
+        release_sigchld();
+        sigchld_removehandler_core();
+    }
+
+    atfork_unlock();
+}
+
+int libxl__carefd_close(libxl__carefd *cf)
+{
+    if (!cf) return 0;
+    atfork_lock();
+    int r = cf->fd < 0 ? 0 : close(cf->fd);
+    int esave = errno;
+    LIBXL_LIST_REMOVE(cf, entry);
+    atfork_unlock();
+    free(cf);
+    errno = esave;
+    return r;
+}
+
+int libxl__carefd_fd(const libxl__carefd *cf)
+{
+    if (!cf) return -1;
+    return cf->fd;
+}
+
+/*
+ * Low-level functions for child process handling, including
+ * the main SIGCHLD handler.
+ */
+
+/* Like waitpid(,,WNOHANG) but handles all errors except ECHILD. */
+static pid_t checked_waitpid(libxl__egc *egc, pid_t want, int *status)
+{
+    EGC_GC;
+    for (;;) {
+        pid_t got = waitpid(want, status, WNOHANG);
+        if (got != -1)
+            return got;
+        if (errno == ECHILD)
+            return got;
+        if (errno == EINTR)
+            continue;
+        LIBXL__EVENT_DISASTER(gc, "waitpid() failed", errno, 0);
+        return 0;
+    }
+}
+
+static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
+                                     int fd, short events, short revents);
+
+static void sigchld_handler(int signo)
+{
+    /* This function has to be reentrant!  Luckily it is. */
+
+    libxl_ctx *notify;
+    int esave = errno;
+
+    int r = pthread_mutex_lock(&sigchld_defer_mutex);
+    assert(!r);
+
+    LIBXL_LIST_FOREACH(notify, &sigchld_users, sigchld_users_entry) {
+        int e = libxl__self_pipe_wakeup(notify->sigchld_selfpipe[1]);
+        if (e) abort(); /* errors are probably EBADF, very bad */
+    }
+
+    r = pthread_mutex_unlock(&sigchld_defer_mutex);
+    assert(!r);
+
+    errno = esave;
+}
+
+static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old)
+{
+    struct sigaction ours;
+    int r;
+
+    memset(&ours,0,sizeof(ours));
+    ours.sa_handler = handler;
+    sigemptyset(&ours.sa_mask);
+    ours.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+    r = sigaction(SIGCHLD, &ours, old);
+    assert(!r);
+}
+
+/*
+ * SIGCHLD deferral
+ *
+ * sigchld_defer and sigchld_release are a bit like using sigprocmask
+ * to block the signal only they work for the whole process.  Sadly
+ * this has to be done by setting a special handler that records the
+ * "pendingness" of the signal here in the program.  How tedious.
+ *
+ * A property of this approach is that the signal handler itself
+ * must be reentrant (see the comment in release_sigchld).
+ *
+ * Callers have the atfork_lock so there is no risk of concurrency
+ * within these functions, aside from the risk of being interrupted by
+ * the signal.  We use sigchld_defer_mutex to guard against the
+ * possibility of the real signal handler being still running on
+ * another thread.
+ */
+
+static volatile sig_atomic_t sigchld_occurred_while_deferred;
+
+static void sigchld_handler_when_deferred(int signo)
+{
+    sigchld_occurred_while_deferred = 1;
+}
+
+static void defer_sigchld(void)
+{
+    assert(sigchld_installed);
+
+    sigchld_sethandler_raw(sigchld_handler_when_deferred, 0);
+
+    /* Now _this thread_ cannot any longer be interrupted by the
+     * signal, so we can take the mutex without risk of deadlock.  If
+     * another thread is in the signal handler, either it or we will
+     * block and wait for the other. */
+
+    int r = pthread_mutex_lock(&sigchld_defer_mutex);
+    assert(!r);
+}
+
+static void release_sigchld(void)
+{
+    assert(sigchld_installed);
+
+    int r = pthread_mutex_unlock(&sigchld_defer_mutex);
+    assert(!r);
+
+    sigchld_sethandler_raw(sigchld_handler, 0);
+    if (sigchld_occurred_while_deferred) {
+        sigchld_occurred_while_deferred = 0;
+        /* We might get another SIGCHLD here, in which case
+         * sigchld_handler will be interrupted and re-entered.
+         * This is OK. */
+        sigchld_handler(SIGCHLD);
+    }
+}
+
+/*
+ * Meat of the child process handling.
+ */
+
+static void sigchld_removehandler_core(void) /* idempotent */
+{
+    struct sigaction was;
+    int r;
+    
+    if (!sigchld_installed)
+        return;
+
+    r = sigaction(SIGCHLD, &sigchld_saved_action, &was);
+    assert(!r);
+    assert(!(was.sa_flags & SA_SIGINFO));
+    assert(was.sa_handler == sigchld_handler);
+
+    sigchld_installed = 0;
+}
+
+static void sigchld_installhandler_core(void) /* idempotent */
+{
+    if (sigchld_installed)
+        return;
+
+    sigchld_installed = 1;
+
+    sigchld_sethandler_raw(sigchld_handler, &sigchld_saved_action);
+
+    assert(((void)"application must negotiate with libxl about SIGCHLD",
+            !(sigchld_saved_action.sa_flags & SA_SIGINFO) &&
+            (sigchld_saved_action.sa_handler == SIG_DFL ||
+             sigchld_saved_action.sa_handler == SIG_IGN)));
+}
+
+static void sigchld_user_remove(libxl_ctx *ctx) /* idempotent */
+{
+    if (!ctx->sigchld_user_registered)
+        return;
+
+    atfork_lock();
+    defer_sigchld();
+
+    LIBXL_LIST_REMOVE(ctx, sigchld_users_entry);
+
+    release_sigchld();
+
+    if (LIBXL_LIST_EMPTY(&sigchld_users))
+        sigchld_removehandler_core();
+
+    atfork_unlock();
+
+    ctx->sigchld_user_registered = 0;
+}
+
+void libxl__sigchld_notneeded(libxl__gc *gc) /* non-reentrant, idempotent */
+{
+    sigchld_user_remove(CTX);
+    libxl__ev_fd_deregister(gc, &CTX->sigchld_selfpipe_efd);
+}
+
+int libxl__sigchld_needed(libxl__gc *gc) /* non-reentrant, idempotent */
+{
+    int rc;
+
+    if (CTX->sigchld_selfpipe[0] < 0) {
+        rc = libxl__pipe_nonblock(CTX, CTX->sigchld_selfpipe);
+        if (rc) goto out;
+    }
+    if (!libxl__ev_fd_isregistered(&CTX->sigchld_selfpipe_efd)) {
+        rc = libxl__ev_fd_register(gc, &CTX->sigchld_selfpipe_efd,
+                                   sigchld_selfpipe_handler,
+                                   CTX->sigchld_selfpipe[0], POLLIN);
+        if (rc) goto out;
+    } else {
+        rc = libxl__ev_fd_modify(gc, &CTX->sigchld_selfpipe_efd, POLLIN);
+        if (rc) goto out;
+    }
+    if (!CTX->sigchld_user_registered) {
+        atfork_lock();
+
+        sigchld_installhandler_core();
+
+        defer_sigchld();
+
+        LIBXL_LIST_INSERT_HEAD(&sigchld_users, CTX, sigchld_users_entry);
+
+        release_sigchld();
+        atfork_unlock();
+
+        CTX->sigchld_user_registered = 1;
+    }
+
+    rc = 0;
+ out:
+    return rc;
+}
+
+static bool chldmode_ours(libxl_ctx *ctx, bool creating)
+{
+    switch (ctx->childproc_hooks->chldowner) {
+    case libxl_sigchld_owner_libxl:
+        return creating || !LIBXL_LIST_EMPTY(&ctx->children);
+    case libxl_sigchld_owner_mainloop:
+        return 0;
+    case libxl_sigchld_owner_libxl_always:
+    case libxl_sigchld_owner_libxl_always_selective_reap:
+        return 1;
+    }
+    abort();
+}
+
+static void perhaps_sigchld_notneeded(libxl__gc *gc)
+{
+    if (!chldmode_ours(CTX, 0))
+        libxl__sigchld_notneeded(gc);
+}
+
+static int perhaps_sigchld_needed(libxl__gc *gc, bool creating)
+{
+    int rc;
+
+    if (chldmode_ours(CTX, creating)) {
+        rc = libxl__sigchld_needed(gc);
+        if (rc) return rc;
+    }
+    return 0;
+}
+
+static void childproc_reaped_ours(libxl__egc *egc, libxl__ev_child *ch,
+                                 int status)
+{
+    pid_t pid = ch->pid;
+    LIBXL_LIST_REMOVE(ch, entry);
+    ch->pid = -1;
+    ch->callback(egc, ch, pid, status);
+}
+
+static int childproc_reaped(libxl__egc *egc, pid_t pid, int status)
+{
+    EGC_GC;
+    libxl__ev_child *ch;
+
+    LIBXL_LIST_FOREACH(ch, &CTX->children, entry)
+        if (ch->pid == pid)
+            goto found;
+
+    /* not found */
+    return ERROR_UNKNOWN_CHILD;
+
+ found:
+    childproc_reaped_ours(egc, ch, status);
+
+    perhaps_sigchld_notneeded(gc);
+
+    return 0;
+}
+
+int libxl_childproc_reaped(libxl_ctx *ctx, pid_t pid, int status)
+{
+    EGC_INIT(ctx);
+    CTX_LOCK;
+    assert(CTX->childproc_hooks->chldowner
+           == libxl_sigchld_owner_mainloop);
+    int rc = childproc_reaped(egc, pid, status);
+    CTX_UNLOCK_EGC_FREE;
+    return rc;
+}
+
+static void childproc_checkall(libxl__egc *egc)
+{
+    EGC_GC;
+    libxl__ev_child *ch;
+
+    for (;;) {
+        int status;
+        pid_t got;
+
+        LIBXL_LIST_FOREACH(ch, &CTX->children, entry) {
+            got = checked_waitpid(egc, ch->pid, &status);
+            if (got)
+                goto found;
+        }
+        /* not found */
+        return;
+
+    found:
+        if (got == -1) {
+            LIBXL__EVENT_DISASTER
+                (gc, "waitpid() gave ECHILD but we have a child",
+                 ECHILD, 0);
+            /* it must have finished but we don't know its status */
+            status = 255<<8; /* no wait.h macro for this! */
+            assert(WIFEXITED(status));
+            assert(WEXITSTATUS(status)==255);
+            assert(!WIFSIGNALED(status));
+            assert(!WIFSTOPPED(status));
+        }
+        childproc_reaped_ours(egc, ch, status);
+        /* we need to restart the loop, as children may have been edited */
+    }
+}
+
+void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
+{
+    EGC_INIT(ctx);
+    CTX_LOCK;
+    assert(CTX->childproc_hooks->chldowner
+           == libxl_sigchld_owner_mainloop);
+    childproc_checkall(egc);
+    CTX_UNLOCK_EGC_FREE;
+}
+
+static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
+                                     int fd, short events, short revents)
+{
+    /* May make callbacks into the application for child processes.
+     * So, this function may unlock and relock the CTX.  This is OK
+     * because event callback functions are always called with the CTX
+     * locked exactly once, and from code which copes with reentrancy.
+     * (See also the comment in afterpoll_internal.) */
+    EGC_GC;
+
+    int selfpipe = CTX->sigchld_selfpipe[0];
+
+    if (revents & ~POLLIN) {
+        LOG(ERROR, "unexpected poll event 0x%x on SIGCHLD self pipe", revents);
+        LIBXL__EVENT_DISASTER(gc,
+                              "unexpected poll event on SIGCHLD self pipe",
+                              0, 0);
+    }
+    assert(revents & POLLIN);
+
+    int e = libxl__self_pipe_eatall(selfpipe);
+    if (e) LIBXL__EVENT_DISASTER(gc, "read sigchld pipe", e, 0);
+
+    if (CTX->childproc_hooks->chldowner
+        == libxl_sigchld_owner_libxl_always_selective_reap) {
+        childproc_checkall(egc);
+        return;
+    }
+
+    while (chldmode_ours(CTX, 0) /* in case the app changes the mode */) {
+        int status;
+        pid_t pid = checked_waitpid(egc, -1, &status);
+
+        if (pid == 0 || pid == -1 /* ECHILD */)
+            return;
+
+        int rc = childproc_reaped(egc, pid, status);
+
+        if (rc) {
+            if (CTX->childproc_hooks->reaped_callback) {
+                CTX_UNLOCK;
+                rc = CTX->childproc_hooks->reaped_callback
+                        (pid, status, CTX->childproc_user);
+                CTX_LOCK;
+                if (rc != 0 && rc != ERROR_UNKNOWN_CHILD) {
+                    char disasterbuf[200];
+                    snprintf(disasterbuf, sizeof(disasterbuf), " reported by"
+                             " libxl_childproc_hooks->reaped_callback"
+                             " (for pid=%lu, status=%d; error code %d)",
+                             (unsigned long)pid, status, rc);
+                    LIBXL__EVENT_DISASTER(gc, disasterbuf, 0, 0);
+                    return;
+                }
+            } else {
+                rc = ERROR_UNKNOWN_CHILD;
+            }
+            if (rc)
+                libxl_report_child_exitstatus(CTX, XTL_WARN,
+                                "unknown child", (long)pid, status);
+        }
+    }
+}
+
+pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch,
+                           libxl__ev_child_callback *death)
+{
+    CTX_LOCK;
+    int rc;
+
+    perhaps_sigchld_needed(gc, 1);
+
+    pid_t pid =
+        CTX->childproc_hooks->fork_replacement
+        ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user)
+        : fork();
+    if (pid == -1) {
+        LOGE(ERROR, "fork failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (!pid) {
+        /* woohoo! */
+        if (CTX->xsh) {
+            xs_daemon_destroy_postfork(CTX->xsh);
+            CTX->xsh = NULL; /* turns mistakes into crashes */
+        }
+        /* Yes, CTX is left locked in the child. */
+        return 0;
+    }
+
+    ch->pid = pid;
+    ch->callback = death;
+    LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry);
+    rc = pid;
+
+ out:
+    perhaps_sigchld_notneeded(gc);
+    CTX_UNLOCK;
+    return rc;
+}
+
+void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
+                             void *user)
+{
+    GC_INIT(ctx);
+    CTX_LOCK;
+
+    assert(LIBXL_LIST_EMPTY(&CTX->children));
+
+    if (!hooks)
+        hooks = &libxl__childproc_default_hooks;
+
+    ctx->childproc_hooks = hooks;
+    ctx->childproc_user = user;
+
+    perhaps_sigchld_notneeded(gc);
+    perhaps_sigchld_needed(gc, 0); /* idempotent, ok to ignore errors for now */
+
+    CTX_UNLOCK;
+    GC_FREE;
+}
+
+const libxl_childproc_hooks libxl__childproc_default_hooks = {
+    libxl_sigchld_owner_libxl, 0, 0
+};
+
+int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what) {
+    int rc;
+
+    assert(!CTX->xsh);
+    CTX->xsh = xs_daemon_open();
+    if (!CTX->xsh) {
+        LOGE(ERROR, "%s: xenstore reopen failed", what);
+        rc = ERROR_FAIL;  goto out;
+    }
+
+    libxl_fd_set_cloexec(CTX, xs_fileno(CTX->xsh), 1);
+
+    return 0;
+
+ out:
+    return rc;
+}
+
+typedef struct ev_child_killed {
+    libxl__ao *ao;
+    libxl__ev_child ch;
+} ev_child_killed;
+static void deregistered_child_callback(libxl__egc *, libxl__ev_child *,
+                                        pid_t, int status);
+
+void libxl__ev_child_kill_deregister(libxl__ao *ao, libxl__ev_child *ch,
+                                     int sig)
+{
+    AO_GC;
+
+    if (!libxl__ev_child_inuse(ch))
+        return;
+
+    pid_t pid = ch->pid;
+
+    ev_child_killed *new_ch = GCNEW(new_ch);
+    new_ch->ao = ao;
+    new_ch->ch.pid = pid;
+    new_ch->ch.callback = deregistered_child_callback;
+    LIBXL_LIST_INSERT_HEAD(&CTX->children, &new_ch->ch, entry);
+    ao->outstanding_killed_child++;
+
+    LIBXL_LIST_REMOVE(ch, entry);
+    ch->pid = -1;
+    int r = kill(pid, sig);
+    if (r)
+        LOGED(ERROR, ao->domid,
+              "failed to kill child [%ld] with signal %d",
+             (unsigned long)pid, sig);
+}
+
+static void deregistered_child_callback(libxl__egc *egc,
+                                        libxl__ev_child *ch,
+                                        pid_t pid,
+                                        int status)
+{
+    ev_child_killed *ck = CONTAINER_OF(ch, *ck, ch);
+    EGC_GC;
+
+    libxl_report_child_exitstatus(CTX, XTL_ERROR,
+                                  "killed fork (dying as expected)",
+                                  pid, status);
+    ck->ao->outstanding_killed_child--;
+    libxl__ao_complete_check_progress_reports(egc, ck->ao);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_freebsd.c b/tools/libs/light/libxl_freebsd.c
new file mode 100644 (file)
index 0000000..f7ef4a8
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2014
+ * Author Roger Pau Monne <roger.pau@entel.upc.edu>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__try_phy_backend(mode_t st_mode)
+{
+    if (S_ISREG(st_mode) || S_ISBLK(st_mode) || S_ISCHR(st_mode))
+        return 1;
+
+    return 0;
+}
+
+char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
+{
+    /* This translation table has been copied from the FreeBSD blkfront code. */
+    const static struct vdev_info {
+        int major;
+        int shift;
+        int base;
+        const char *name;
+    } info[] = {
+        {3,     6,  0,      "ada"}, /* ide0 */
+        {22,    6,  2,      "ada"}, /* ide1 */
+        {33,    6,  4,      "ada"}, /* ide2 */
+        {34,    6,  6,      "ada"}, /* ide3 */
+        {56,    6,  8,      "ada"}, /* ide4 */
+        {57,    6,  10,     "ada"}, /* ide5 */
+        {88,    6,  12,     "ada"}, /* ide6 */
+        {89,    6,  14,     "ada"}, /* ide7 */
+        {90,    6,  16,     "ada"}, /* ide8 */
+        {91,    6,  18,     "ada"}, /* ide9 */
+
+        {8,     4,  0,      "da"},  /* scsi disk0 */
+        {65,    4,  16,     "da"},  /* scsi disk1 */
+        {66,    4,  32,     "da"},  /* scsi disk2 */
+        {67,    4,  48,     "da"},  /* scsi disk3 */
+        {68,    4,  64,     "da"},  /* scsi disk4 */
+        {69,    4,  80,     "da"},  /* scsi disk5 */
+        {70,    4,  96,     "da"},  /* scsi disk6 */
+        {71,    4,  112,    "da"},  /* scsi disk7 */
+        {128,   4,  128,    "da"},  /* scsi disk8 */
+        {129,   4,  144,    "da"},  /* scsi disk9 */
+        {130,   4,  160,    "da"},  /* scsi disk10 */
+        {131,   4,  176,    "da"},  /* scsi disk11 */
+        {132,   4,  192,    "da"},  /* scsi disk12 */
+        {133,   4,  208,    "da"},  /* scsi disk13 */
+        {134,   4,  224,    "da"},  /* scsi disk14 */
+        {135,   4,  240,    "da"},  /* scsi disk15 */
+
+        {202,   4,  0,      "xbd"}, /* xbd */
+
+        {0, 0,  0,  NULL},
+    };
+    int major = devid >> 8;
+    int minor = devid & 0xff;
+    int i;
+
+    if (devid & (1 << 28))
+        return GCSPRINTF("%s%d", "xbd", (devid & ((1 << 28) - 1)) >> 8);
+
+    for (i = 0; info[i].major; i++)
+        if (info[i].major == major)
+            return GCSPRINTF("%s%d", info[i].name,
+                             info[i].base + (minor >> info[i].shift));
+
+    return GCSPRINTF("%s%d", "xbd", minor >> 4);
+}
+
+/* Hotplug scripts caller functions */
+static int libxl__hotplug_env_nic(libxl__gc *gc, libxl__device *dev, char ***env,
+                                  int num_exec)
+{
+    int nr = 0;
+    const int arraysize = 5;
+    libxl_nic_type type;
+
+    assert(dev->backend_kind == LIBXL__DEVICE_KIND_VIF);
+
+    /*
+     * On the first pass the PV interface is added to the bridge,
+     * on the second pass the tap interface will also be added.
+     */
+    type = num_exec == 0 ? LIBXL_NIC_TYPE_VIF : LIBXL_NIC_TYPE_VIF_IOEMU;
+
+    GCNEW_ARRAY(*env, arraysize);
+    (*env)[nr++] = "iface_dev";
+    (*env)[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
+                                                      dev->devid, type);
+    (*env)[nr++] = "emulated";
+    (*env)[nr++] = type == LIBXL_NIC_TYPE_VIF_IOEMU ? "1" : "0";
+    (*env)[nr++] = NULL;
+    assert(nr == arraysize);
+
+    return 0;
+}
+
+static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
+                              char ***args, char ***env,
+                              libxl__device_action action,
+                              int num_exec)
+{
+    libxl_nic_type nictype;
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *script;
+    int nr = 0, rc;
+
+    rc = libxl__nic_type(gc, dev, &nictype);
+    if (rc) {
+        LOGD(ERROR, dev->domid, "error when fetching nic type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /*
+     * For PV domains only one pass is needed (because there's no emulated
+     * interface). For HVM domains two passes are needed in order to add
+     * both the PV and the tap interfaces to the bridge.
+     */
+    if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
+        rc = 0;
+        goto out;
+    }
+
+    rc = libxl__hotplug_env_nic(gc, dev, env, num_exec);
+    if (rc)
+        goto out;
+
+    script = libxl__xs_read(gc, XBT_NULL,
+                            GCSPRINTF("%s/%s", be_path, "script"));
+    if (!script) {
+        LOGEVD(ERROR, errno, dev->domid,
+               "unable to read script from %s", be_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    const int arraysize = 4;
+    GCNEW_ARRAY(*args, arraysize);
+    (*args)[nr++] = script;
+    (*args)[nr++] = be_path;
+    (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+    (*args)[nr++] = NULL;
+    assert(nr == arraysize);
+    rc = 1;
+
+out:
+    return rc;
+}
+
+static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
+                               char ***args, char ***env,
+                               libxl__device_action action)
+{
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *script;
+    int nr = 0, rc;
+
+    script = libxl__xs_read(gc, XBT_NULL,
+                            GCSPRINTF("%s/%s", be_path, "script"));
+    if (!script) {
+        LOGEVD(ERROR, errno, dev->domid,
+               "unable to read script from %s", be_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    const int arraysize = 4;
+    GCNEW_ARRAY(*args, arraysize);
+    (*args)[nr++] = script;
+    (*args)[nr++] = be_path;
+    (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+    (*args)[nr++] = NULL;
+    assert(nr == arraysize);
+    rc = 1;
+
+out:
+    return rc;
+}
+
+int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+                                   char ***args, char ***env,
+                                   libxl__device_action action,
+                                   int num_exec)
+{
+    int rc;
+
+    switch (dev->backend_kind) {
+    case LIBXL__DEVICE_KIND_VIF:
+        /*
+         * If domain has a stubdom we don't have to execute hotplug scripts
+         * for emulated interfaces
+         */
+        if ((num_exec > 1) ||
+            (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
+            rc = 0;
+            goto out;
+        }
+        rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
+        break;
+    case LIBXL__DEVICE_KIND_VBD:
+        if (num_exec != 0) {
+            rc = 0;
+            goto out;
+        }
+        rc = libxl__hotplug_disk(gc, dev, args, env, action);
+        break;
+    default:
+        /* No need to execute any hotplug scripts */
+        rc = 0;
+        break;
+    }
+
+out:
+    return rc;
+}
+
+libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
+{
+    return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+int libxl__pci_numdevs(libxl__gc *gc)
+{
+    return ERROR_NI;
+}
+
+int libxl__pci_topology_init(libxl__gc *gc,
+                             physdev_pci_device_t *devs,
+                             int num_devs)
+{
+    return ERROR_NI;
+}
+
+int libxl__local_dm_preexec_restrict(libxl__gc *gc)
+{
+    return 0;
+}
diff --git a/tools/libs/light/libxl_genid.c b/tools/libs/light/libxl_genid.c
new file mode 100644 (file)
index 0000000..7f52356
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 Citrix Systems R&D Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <xenctrl.h>
+#include <xen/hvm/params.h>
+
+/*
+ * Generate a random VM generation ID.
+ *
+ * Returns ERROR_FAIL if a suitable source of random numbers is not
+ * available.
+ *
+ * See Microsoft's "Virtual Machine Generation ID" specification for
+ * further details, including when a new generation ID is required.
+ *
+ *   http://www.microsoft.com/en-us/download/details.aspx?id=30707
+ */
+int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id)
+{
+    GC_INIT(ctx);
+    int ret;
+
+    ret = libxl__random_bytes(gc, id->bytes, LIBXL_MS_VM_GENID_LEN);
+
+    GC_FREE;
+    return ret;
+}
+
+/*
+ * Is this VM generation ID all zeros?
+ */
+bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id)
+{
+    static const libxl_ms_vm_genid zero;
+
+    return memcmp(id->bytes, zero.bytes, LIBXL_MS_VM_GENID_LEN) == 0;
+}
+
+void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
+                            const libxl_ms_vm_genid *src)
+{
+    memcpy(dst, src, LIBXL_MS_VM_GENID_LEN);
+}
+
+int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
+                           const libxl_ms_vm_genid *id)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    const char *dom_path;
+    uint64_t genid[2];
+    uint64_t paddr = 0;
+    int rc;
+
+    memcpy(genid, id->bytes, LIBXL_MS_VM_GENID_LEN);
+
+    /*
+     * Set the "platform/generation-id" XenStore key to pass the ID to
+     * hvmloader.
+     */
+    dom_path = libxl__xs_get_dompath(gc, domid);
+    if (!dom_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = libxl__xs_printf(gc, XBT_NULL,
+                         GCSPRINTF("%s/platform/generation-id", dom_path),
+                         "%"PRIu64 ":%" PRIu64, genid[0], genid[1]);
+    if (rc < 0)
+        goto out;
+
+    /*
+     * Update the ID in guest memory (if available).
+     */
+    xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_VM_GENERATION_ID_ADDR, &paddr);
+    if (paddr) {
+        void *vaddr;
+
+        vaddr = xc_map_foreign_range(ctx->xch, domid, XC_PAGE_SIZE,
+                                     PROT_READ | PROT_WRITE,
+                                     paddr >> XC_PAGE_SHIFT);
+        if (vaddr == NULL) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        memcpy(vaddr + (paddr & ~XC_PAGE_MASK), genid, 2 * sizeof(*genid));
+        munmap(vaddr, XC_PAGE_SIZE);
+
+        /*
+         * The spec requires an ACPI Notify event is injected into the
+         * guest when the generation ID is changed.
+         *
+         * This is only called for domains that are suspended or newly
+         * created and they won't be in a state to receive such an
+         * event.
+         */
+    }
+
+    rc = 0;
+
+  out:
+    return rc;
+}
diff --git a/tools/libs/light/libxl_internal.c b/tools/libs/light/libxl_internal.c
new file mode 100644 (file)
index 0000000..d93a755
--- /dev/null
@@ -0,0 +1,806 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+void libxl__alloc_failed(libxl_ctx *ctx, const char *func,
+                         size_t nmemb, size_t size) {
+#define M "libxl: FATAL ERROR: memory allocation failure"
+#define M_SIZE M " (%s, %lu x %lu)\n"
+#define M_NSIZE M " (%s)\n"
+    if (size) {
+       libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
+                  M_SIZE, func, (unsigned long)nmemb, (unsigned long)size);
+       fprintf(stderr, M_SIZE, func, (unsigned long)nmemb,
+               (unsigned long)size);
+    } else {
+       libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
+                  M_NSIZE, func);
+       fprintf(stderr, M_NSIZE, func);
+
+    }
+
+    fflush(stderr);
+    _exit(-1);
+#undef M_NSIZE
+#undef M_SIZE
+#undef M
+}
+
+void libxl__ptr_add(libxl__gc *gc, void *ptr)
+{
+    int i;
+
+    if (!libxl__gc_is_real(gc))
+        return;
+
+    if (!ptr)
+        return;
+
+    /* fast case: we have space in the array for storing the pointer */
+    for (i = 0; i < gc->alloc_maxsize; i++) {
+        if (!gc->alloc_ptrs[i]) {
+            gc->alloc_ptrs[i] = ptr;
+            return;
+        }
+    }
+    int new_maxsize = gc->alloc_maxsize * 2 + 25;
+    assert(new_maxsize < INT_MAX / sizeof(void*) / 2);
+    gc->alloc_ptrs = realloc(gc->alloc_ptrs, new_maxsize * sizeof(void *));
+    if (!gc->alloc_ptrs)
+        libxl__alloc_failed(CTX, __func__, new_maxsize, sizeof(void*));
+
+    gc->alloc_ptrs[gc->alloc_maxsize++] = ptr;
+
+    while (gc->alloc_maxsize < new_maxsize)
+        gc->alloc_ptrs[gc->alloc_maxsize++] = 0;
+
+    return;
+}
+
+void libxl__free_all(libxl__gc *gc)
+{
+    void *ptr;
+    int i;
+
+    assert(libxl__gc_is_real(gc));
+
+    for (i = 0; i < gc->alloc_maxsize; i++) {
+        ptr = gc->alloc_ptrs[i];
+        gc->alloc_ptrs[i] = NULL;
+        free(ptr);
+    }
+    free(gc->alloc_ptrs);
+    gc->alloc_ptrs = 0;
+    gc->alloc_maxsize = 0;
+}
+
+void *libxl__malloc(libxl__gc *gc, size_t size)
+{
+    void *ptr = malloc(size);
+    if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
+
+    libxl__ptr_add(gc, ptr);
+    return ptr;
+}
+
+void *libxl__zalloc(libxl__gc *gc, size_t size)
+{
+    void *ptr = calloc(size, 1);
+    if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
+
+    libxl__ptr_add(gc, ptr);
+    return ptr;
+}
+
+void *libxl__calloc(libxl__gc *gc, size_t nmemb, size_t size)
+{
+    void *ptr = calloc(nmemb, size);
+    if (!ptr) libxl__alloc_failed(CTX, __func__, nmemb, size);
+
+    libxl__ptr_add(gc, ptr);
+    return ptr;
+}
+
+void *libxl__realloc(libxl__gc *gc, void *ptr, size_t new_size)
+{
+    void *new_ptr = realloc(ptr, new_size);
+    int i = 0;
+
+    if (new_ptr == NULL && new_size != 0)
+        libxl__alloc_failed(CTX, __func__, new_size, 1);
+
+    if (ptr == NULL) {
+        libxl__ptr_add(gc, new_ptr);
+    } else if (new_ptr != ptr && libxl__gc_is_real(gc)) {
+        for (i = 0; ; i++) {
+            assert(i < gc->alloc_maxsize);
+            if (gc->alloc_ptrs[i] == ptr) {
+                gc->alloc_ptrs[i] = new_ptr;
+                break;
+            }
+        }
+    }
+
+    return new_ptr;
+}
+
+char *libxl__vsprintf(libxl__gc *gc, const char *fmt, va_list ap)
+{
+    char *s;
+    va_list aq;
+    int ret;
+
+    va_copy(aq, ap);
+    ret = vsnprintf(NULL, 0, fmt, aq);
+    va_end(aq);
+
+    assert(ret >= 0);
+
+    s = libxl__zalloc(gc, ret + 1);
+    va_copy(aq, ap);
+    ret = vsnprintf(s, ret + 1, fmt, aq);
+    va_end(aq);
+
+    return s;
+}
+
+char *libxl__sprintf(libxl__gc *gc, const char *fmt, ...)
+{
+    char *s;
+    va_list ap;
+
+    va_start(ap, fmt);
+    s = libxl__vsprintf(gc, fmt, ap);
+    va_end(ap);
+
+    return s;
+}
+
+char *libxl__strdup(libxl__gc *gc, const char *c)
+{
+    char *s;
+
+    if (!c) return NULL;
+
+    s = strdup(c);
+
+    if (!s) libxl__alloc_failed(CTX, __func__, strlen(c), 1);
+
+    libxl__ptr_add(gc, s);
+
+    return s;
+}
+
+char *libxl__strndup(libxl__gc *gc, const char *c, size_t n)
+{
+    char *s;
+
+    if (!c) return NULL;
+
+    s = strndup(c, n);
+
+    if (!s) libxl__alloc_failed(CTX, __func__, n, 1);
+
+    libxl__ptr_add(gc, s);
+
+    return s;
+}
+
+char *libxl__dirname(libxl__gc *gc, const char *s)
+{
+    char *c = strrchr(s, '/');
+
+    if (!c)
+        return NULL;
+
+    return libxl__strndup(gc, s, c - s);
+}
+
+void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+             const char *file, int line, const char *func,
+             uint32_t domid, const char *fmt, va_list ap)
+{
+    /* WARNING this function may not call any libxl-provided
+     * memory allocation function, as those may
+     * call libxl__alloc_failed which calls libxl__logv. */
+    char *enomem = "[out of memory formatting log message]";
+    char *base = NULL;
+    int rc, esave;
+    char fileline[256];
+    char domain[256];
+
+    esave = errno;
+
+    rc = vasprintf(&base, fmt, ap);
+    if (rc<0) { base = enomem; goto x; }
+
+    fileline[0] = 0;
+    if (file) snprintf(fileline, sizeof(fileline), "%s:%d",file,line);
+    fileline[sizeof(fileline)-1] = 0;
+
+    domain[0] = 0;
+    if (libxl_domid_valid_guest(domid))
+        snprintf(domain, sizeof(domain), "Domain %"PRIu32":", domid);
+ x:
+    xtl_log(ctx->lg, msglevel, errnoval, "libxl",
+            "%s%s%s%s%s" "%s",
+            fileline, func&&file?":":"", func?func:"", func||file?": ":"",
+            domain, base);
+    if (base != enomem) free(base);
+    errno = esave;
+}
+
+void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+            const char *file, int line, const char *func,
+            uint32_t domid, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    libxl__logv(ctx, msglevel, errnoval, file, line, func, domid, fmt, ap);
+    va_end(ap);
+}
+
+char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path)
+{
+    if (s[0] == '/') return libxl__strdup(gc, s);
+    return GCSPRINTF("%s/%s", path, s);
+}
+
+
+int libxl__file_reference_map(libxl__file_reference *f)
+{
+    struct stat st_buf;
+    int ret, fd;
+    void *data;
+
+    if (f->mapped)
+        return 0;
+
+    fd = open(f->path, O_RDONLY);
+    if (fd < 0)
+        return ERROR_FAIL;
+
+    ret = fstat(fd, &st_buf);
+    if (ret < 0)
+        goto out;
+
+    ret = -1;
+    data = mmap(NULL, st_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (data == MAP_FAILED)
+        goto out;
+
+    f->mapped = 1;
+    f->data = data;
+    f->size = st_buf.st_size;
+
+    ret = 0;
+out:
+    close(fd);
+
+    return ret == 0 ? 0 : ERROR_FAIL;
+}
+
+int libxl__file_reference_unmap(libxl__file_reference *f)
+{
+    int ret;
+
+    if (!f->mapped)
+        return 0;
+
+    ret = munmap(f->data, f->size);
+    if (ret == 0) {
+        f->mapped = 0;
+        f->data = NULL;
+        f->size = 0;
+        return 0;
+    }
+
+    return ERROR_FAIL;
+}
+
+_hidden int libxl__parse_mac(const char *s, libxl_mac mac)
+{
+    const char *tok;
+    char *endptr;
+    int i;
+
+    for (i = 0, tok = s; *tok && (i < 6); ++i, tok = endptr) {
+        mac[i] = strtol(tok, &endptr, 16);
+        if (endptr != (tok + 2) || (*endptr != '\0' && *endptr != ':') )
+            return ERROR_INVAL;
+        if (*endptr == ':')
+            endptr++;
+    }
+    if ( i != 6 )
+        return ERROR_INVAL;
+
+    return 0;
+}
+
+_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b)
+{
+    int i;
+
+    for (i = 0; i<6; i++) {
+        if ((*a)[i] != (*b)[i])
+            return (*a)[i] - (*b)[i];
+    }
+
+    return 0;
+}
+
+_hidden int libxl__mac_is_default(libxl_mac *mac)
+{
+    return (!(*mac)[0] && !(*mac)[1] && !(*mac)[2] &&
+            !(*mac)[3] && !(*mac)[4] && !(*mac)[5]);
+}
+
+_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock)
+{
+    GC_INIT(ctx);
+    pthread_mutexattr_t attr;
+    int rc = 0;
+
+    if (pthread_mutexattr_init(&attr) != 0) {
+        LOGE(ERROR, "Failed to init mutex attributes");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
+        LOGE(ERROR, "Failed to set mutex attributes");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (pthread_mutex_init(lock, &attr) != 0) {
+        LOGE(ERROR, "Failed to init mutex");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+out:
+    pthread_mutexattr_destroy(&attr);
+    GC_FREE;
+    return rc;
+}
+
+int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid)
+{
+    char *path = NULL;
+    char *dm_version = NULL;
+    libxl_device_model_version value;
+
+    path = libxl__xs_libxl_path(gc, domid);
+    path = GCSPRINTF("%s/dm-version", path);
+    dm_version = libxl__xs_read(gc, XBT_NULL, path);
+    if (!dm_version) {
+        return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+    }
+
+    if (libxl_device_model_version_from_string(dm_version, &value) < 0) {
+        LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version);
+        return -1;
+    }
+    return value;
+}
+
+/* Portability note: this lock utilises flock(2) so a proper implementation of
+ * flock(2) is required.
+ */
+libxl__flock *libxl__lock_file(libxl__gc *gc, const char *lockfile)
+{
+    libxl__flock *lock;
+    int fd;
+    struct stat stab, fstab;
+
+    lock = libxl__zalloc(NOGC, sizeof(libxl__flock));
+    lock->path = libxl__strdup(NOGC, lockfile);
+
+    while (true) {
+        libxl__carefd_begin();
+        fd = open(lockfile, O_RDWR|O_CREAT, 0666);
+        if (fd < 0)
+            LOGE(ERROR,
+                 "cannot open lockfile %s, errno=%d",
+                 lockfile, errno);
+        lock->carefd = libxl__carefd_opened(CTX, fd);
+        if (fd < 0) goto out;
+
+        /* Lock the file in exclusive mode, wait indefinitely to
+         * acquire the lock
+         */
+        while (flock(fd, LOCK_EX)) {
+            switch (errno) {
+            case EINTR:
+                /* Signal received, retry */
+                continue;
+            default:
+                /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
+                LOGE(ERROR,
+                     "unexpected error while trying to lock %s, fd=%d, errno=%d",
+                      lockfile, fd, errno);
+                goto out;
+            }
+        }
+
+        if (fstat(fd, &fstab)) {
+            LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d",
+                  lockfile, fd, errno);
+            goto out;
+        }
+        if (stat(lockfile, &stab)) {
+            if (errno != ENOENT) {
+                LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno);
+                goto out;
+            }
+        } else {
+            if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
+                break;
+        }
+
+        libxl__carefd_close(lock->carefd);
+    }
+
+    return lock;
+
+out:
+    if (lock) libxl__unlock_file(lock);
+    return NULL;
+}
+
+void libxl__unlock_file(libxl__flock *lock)
+{
+    /* It's important to unlink the file before closing fd to avoid
+     * the following race (if close before unlink):
+     *
+     *   P1 LOCK                         P2 UNLOCK
+     *   fd1 = open(lockfile)
+     *                                   close(fd2)
+     *   flock(fd1)
+     *   fstat and stat check success
+     *                                   unlink(lockfile)
+     *   return lock
+     *
+     * In above case P1 thinks it has got hold of the lock but
+     * actually lock is released by P2 (lockfile unlinked).
+     */
+    if (lock->path) unlink(lock->path);
+    if (lock->carefd) libxl__carefd_close(lock->carefd);
+    free(lock->path);
+    free(lock);
+}
+
+libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid)
+{
+    const char *lockfile;
+    libxl__flock *lock;
+
+    lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
+    if (!lockfile) return NULL;
+
+    lock = libxl__lock_file(gc, lockfile);
+
+    /* Check the domain is still there, if not we should release the
+     * lock and clean up.
+     */
+    if (libxl_domain_info(CTX, NULL, domid)) {
+        libxl__unlock_file(lock);
+        return NULL;
+    }
+
+    return lock;
+}
+
+libxl__flock *libxl__lock_domid_history(libxl__gc *gc)
+{
+    const char *lockfile;
+
+    lockfile = libxl__domid_history_path(gc, ".lock");
+    if (!lockfile) return NULL;
+
+    return libxl__lock_file(gc, lockfile);
+}
+
+int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
+                                    libxl_domain_config *d_config)
+{
+    uint8_t *data = NULL;
+    int rc, len;
+
+    rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len);
+    if (rc) {
+        LOGEVD(ERROR, rc, domid,
+              "failed to retrieve domain configuration");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (len == 0) {
+        /* No logging, not necessary an error from caller's PoV. */
+        rc = ERROR_JSON_CONFIG_EMPTY;
+        goto out;
+    }
+    rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data);
+
+out:
+    free(data);
+    return rc;
+}
+
+int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
+                                    libxl_domain_config *d_config)
+{
+    char *d_config_json;
+    int rc;
+
+    d_config_json = libxl_domain_config_to_json(CTX, d_config);
+    if (!d_config_json) {
+        LOGED(ERROR, domid,
+              "failed to convert domain configuration to JSON");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__userdata_store(gc, domid, "libxl-json",
+                               (const uint8_t *)d_config_json,
+                               strlen(d_config_json) + 1 /* include '\0' */);
+    if (rc) {
+        LOGEVD(ERROR, rc, domid, "failed to store domain configuration");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+out:
+    free(d_config_json);
+    return rc;
+}
+
+void libxl__update_domain_configuration(libxl__gc *gc,
+                                        libxl_domain_config *dst,
+                                        const libxl_domain_config *src)
+{
+    int i, idx, num;
+    const libxl__device_type *dt;
+
+    for (idx = 0;; idx++) {
+        dt = device_type_tbl[idx];
+        if (!dt)
+            break;
+
+        num = *libxl__device_type_get_num(dt, src);
+        if (!dt->update_config || !num)
+            continue;
+
+        for (i = 0; i < num; i++)
+            dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i),
+                                  libxl__device_type_get_elem(dt, src, i));
+    }
+
+    /* update guest UUID */
+    libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid);
+
+    /* video ram */
+    dst->b_info.video_memkb = src->b_info.video_memkb;
+}
+
+static void ev_slowlock_init_internal(libxl__ev_slowlock *lock,
+                                      const char *userdata_userid)
+{
+    libxl__ev_child_init(&lock->child);
+    lock->userdata_userid = userdata_userid;
+    lock->path = NULL;
+    lock->fd = -1;
+    lock->held = false;
+}
+
+void libxl__ev_devlock_init(libxl__ev_slowlock *lock)
+{
+    ev_slowlock_init_internal(lock, "libxl-device-changes-lock");
+}
+
+void libxl__ev_qmplock_init(libxl__ev_slowlock *lock)
+{
+    ev_slowlock_init_internal(lock, "qmp-socket-lock");
+}
+
+static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock);
+static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
+                                   pid_t pid, int status);
+
+void libxl__ev_slowlock_lock(libxl__egc *egc, libxl__ev_slowlock *lock)
+{
+    STATE_AO_GC(lock->ao);
+    const char *lockfile;
+
+    lockfile = libxl__userdata_path(gc, lock->domid,
+                                    lock->userdata_userid, "l");
+    if (!lockfile) goto out;
+    lock->path = libxl__strdup(NOGC, lockfile);
+
+    ev_lock_prepare_fork(egc, lock);
+    return;
+out:
+    lock->callback(egc, lock, ERROR_LOCK_FAIL);
+}
+
+static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock)
+{
+    STATE_AO_GC(lock->ao);
+    pid_t pid;
+    int fd;
+
+    /* Convenience aliases */
+    libxl_domid domid = lock->domid;
+    const char *lockfile = lock->path;
+
+    lock->fd = open(lockfile, O_RDWR|O_CREAT, 0666);
+    if (lock->fd < 0) {
+        LOGED(ERROR, domid, "cannot open lockfile %s", lockfile);
+        goto out;
+    }
+    fd = lock->fd;
+
+    /* Enable this optimisation only in releases, so the fork code is
+     * exercised while libxl is built with debug=y. */
+#ifndef CONFIG_DEBUG
+    /*
+     * We try to grab the lock before forking as it is likely to be free.
+     * Even though we are supposed to CTX_UNLOCK before attempting to grab
+     * the ev_lock, it is fine to do a non-blocking request now with the
+     * CTX_LOCK held as if that fails we'll try again in a fork (CTX_UNLOCK
+     * will be called in libxl), that will avoid deadlocks.
+     */
+    int r = flock(fd, LOCK_EX | LOCK_NB);
+    if (!r) {
+        libxl_fd_set_cloexec(CTX, fd, 1);
+        /* We held a lock, no need to fork but we need to check it. */
+        ev_lock_child_callback(egc, &lock->child, 0, 0);
+        return;
+    }
+#endif
+
+    pid = libxl__ev_child_fork(gc, &lock->child, ev_lock_child_callback);
+    if (pid < 0)
+        goto out;
+    if (!pid) {
+        /* child */
+        int exit_val = 0;
+
+        /* Lock the file in exclusive mode, wait indefinitely to
+         * acquire the lock */
+        while (flock(fd, LOCK_EX)) {
+            switch (errno) {
+            case EINTR:
+                /* Signal received, retry */
+                continue;
+            default:
+                /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
+                LOGED(ERROR, domid,
+                      "unexpected error while trying to lock %s, fd=%d",
+                      lockfile, fd);
+                exit_val = 1;
+                break;
+            }
+        }
+        _exit(exit_val);
+    }
+
+    /* Now that the child has the fd, set cloexec in the parent to prevent
+     * more leakage than necessary */
+    libxl_fd_set_cloexec(CTX, fd, 1);
+    return;
+out:
+    libxl__ev_slowlock_unlock(gc, lock);
+    lock->callback(egc, lock, ERROR_LOCK_FAIL);
+}
+
+static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
+                                   pid_t pid, int status)
+{
+    EGC_GC;
+    libxl__ev_slowlock *lock = CONTAINER_OF(child, *lock, child);
+    struct stat stab, fstab;
+    int rc = ERROR_LOCK_FAIL;
+
+    /* Convenience aliases */
+    int fd = lock->fd;
+    const char *lockfile = lock->path;
+    libxl_domid domid = lock->domid;
+
+    if (status) {
+        libxl_report_child_exitstatus(CTX, XTL_ERROR, "flock child",
+                                      pid, status);
+        goto out;
+    }
+
+    if (fstat(fd, &fstab)) {
+        LOGED(ERROR, domid, "cannot fstat %s, fd=%d", lockfile, fd);
+        goto out;
+    }
+    if (stat(lockfile, &stab)) {
+        if (errno != ENOENT) {
+            LOGED(ERROR, domid, "cannot stat %s", lockfile);
+            goto out;
+        }
+    } else {
+        if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) {
+            /* We held the lock */
+            lock->held = true;
+            rc = 0;
+            goto out;
+        }
+    }
+
+    /* We didn't grab the lock, let's try again */
+    flock(lock->fd, LOCK_UN);
+    close(lock->fd);
+    lock->fd = -1;
+    ev_lock_prepare_fork(egc, lock);
+    return;
+
+out:
+    if (lock->held) {
+        /* Check the domain is still there, if not we should release the
+         * lock and clean up.  */
+        if (libxl_domain_info(CTX, NULL, domid))
+            rc = ERROR_LOCK_FAIL;
+    }
+    if (rc) {
+        LOGD(ERROR, domid, "Failed to grab lock for %s",
+             lock->userdata_userid);
+        libxl__ev_slowlock_unlock(gc, lock);
+    }
+    lock->callback(egc, lock, rc);
+}
+
+void libxl__ev_slowlock_unlock(libxl__gc *gc, libxl__ev_slowlock *lock)
+{
+    int r;
+
+    assert(!libxl__ev_child_inuse(&lock->child));
+
+    /* See the rationale in libxl__unlock_domain_userdata()
+     * about why we do unlink() before unlock(). */
+
+    if (lock->path && lock->held)
+        unlink(lock->path);
+
+    if (lock->fd >= 0) {
+        /* We need to call unlock as the fd may have leaked into other
+         * processes */
+        r = flock(lock->fd, LOCK_UN);
+        if (r)
+            LOGED(ERROR, lock->domid, "failed to unlock fd=%d, path=%s",
+                  lock->fd, lock->path);
+        close(lock->fd);
+    }
+    free(lock->path);
+    ev_slowlock_init_internal(lock, lock->userdata_userid);
+}
+
+void libxl__ev_slowlock_dispose(libxl__gc *gc, libxl__ev_slowlock *lock)
+{
+    libxl__ev_child_kill_deregister(lock->ao, &lock->child, SIGKILL);
+    libxl__ev_slowlock_unlock(gc, lock);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h
new file mode 100644 (file)
index 0000000..1fcf85c
--- /dev/null
@@ -0,0 +1,4859 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#ifndef LIBXL_INTERNAL_H
+#define LIBXL_INTERNAL_H
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "xentoolcore_internal.h"
+
+#include "libxl_sr_stream_format.h"
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <sys/mman.h>
+#include <poll.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include <xenevtchn.h>
+#include <xenstore.h>
+#define XC_WANT_COMPAT_MAP_FOREIGN_API
+#include <xenctrl.h>
+#include <xenguest.h>
+#include <xenhypfs.h>
+#include <xenctrl_dom.h>
+
+#include <xen-tools/libs.h>
+
+#include "xentoollog.h"
+
+#include <xen/io/xenbus.h>
+
+#ifdef LIBXL_H
+# error libxl.h should be included via libxl_internal.h, not separately
+#endif
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
+# define LIBXL_EXTERNAL_CALLERS_ONLY \
+    __attribute__((warning("may not be called from within libxl")))
+#endif
+
+#include "libxl.h"
+#include "_paths.h"
+#include "_libxl_save_msgs_callout.h"
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#define _hidden __attribute__((visibility("hidden")))
+#define _protected __attribute__((visibility("protected")))
+#else
+#define _hidden
+#define _protected
+#endif
+
+#include "flexarray.h"
+#include "libxl_utils.h"
+
+#include "libxl_json.h"
+
+#include "_libxl_types_internal.h"
+#include "_libxl_types_internal_json.h"
+
+#define LIBXL_INIT_TIMEOUT 10
+#define LIBXL_DESTROY_TIMEOUT 10
+#define LIBXL_HOTPLUG_TIMEOUT 40
+/* QEMU may be slow to load and start due to a bug in Linux where the I/O
+ * subsystem sometime produce high latency under load. */
+#define LIBXL_DEVICE_MODEL_START_TIMEOUT 60
+#define LIBXL_DEVICE_MODEL_SAVE_FILE XEN_LIB_DIR "/qemu-save" /* .$domid */
+#define LIBXL_DEVICE_MODEL_RESTORE_FILE XEN_LIB_DIR "/qemu-resume" /* .$domid */
+#define LIBXL_QMP_CMD_TIMEOUT 10
+#define LIBXL_STUBDOM_START_TIMEOUT 30
+#define LIBXL_QEMU_BODGE_TIMEOUT 2
+#define LIBXL_XENCONSOLE_LIMIT 1048576
+#define LIBXL_XENCONSOLE_PROTOCOL "vt100"
+#define LIBXL_MAXMEM_CONSTANT 1024
+#define LIBXL_PV_EXTRA_MEMORY 1024
+#define LIBXL_HVM_EXTRA_MEMORY 2048
+#define LIBXL_MIN_DOM0_MEM (128*1024)
+#define LIBXL_INVALID_GFN (~(uint64_t)0)
+#define LIBXL_VGA_HOLE_SIZE 0x20
+/* use 0 as the domid of the toolstack domain for now */
+#define LIBXL_TOOLSTACK_DOMID 0
+#define QEMU_SIGNATURE "DeviceModelRecord0002"
+#define STUBDOM_CONSOLE_LOGGING 0
+#define STUBDOM_CONSOLE_SAVE 1
+#define STUBDOM_CONSOLE_RESTORE 2
+#define STUBDOM_CONSOLE_SERIAL 3
+#define STUBDOM_SPECIAL_CONSOLES 3
+#define LIBXL_LINUX_STUBDOM_MEM 128
+#define TAP_DEVICE_SUFFIX "-emu"
+#define DOMID_XS_PATH "domid"
+#define PVSHIM_BASENAME "xen-shim"
+#define PVSHIM_CMDLINE "pv-shim console=xen,pv"
+
+/* Size macros. */
+#define __AC(X,Y)   (X##Y)
+#define _AC(X,Y)    __AC(X,Y)
+#define MB(_mb)     (_AC(_mb, ULL) << 20)
+#define GB(_gb)     (_AC(_gb, ULL) << 30)
+
+#define DIV_ROUNDUP(n, d) (((n) + (d) - 1) / (d))
+
+#define MASK_EXTR(v, m) (((v) & (m)) / ((m) & -(m)))
+#define MASK_INSR(v, m) (((v) * ((m) & -(m))) & (m))
+
+#define LIBXL__LOGGING_ENABLED
+
+#ifdef LIBXL__LOGGING_ENABLED
+#define LIBXL__LOG(ctx, loglevel, _f, _a...)   libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
+#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...)   libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
+#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...)   libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
+
+/* Same log functions as above, but with _d being a domain id. */
+#define LIBXL__LOGD(ctx, loglevel, _d, _f, _a...)   libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, _d, _f, ##_a)
+#define LIBXL__LOGD_ERRNO(ctx, loglevel, _d, _f, _a...)   libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, _d, _f, ##_a)
+#define LIBXL__LOGD_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...)   libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, _d, _f, ##_a)
+#else
+#define LIBXL__LOG(ctx, loglevel, _f, _a...)
+#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...)
+#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...)
+
+#define LIBXLD__LOG(ctx, loglevel, _d, _f, _a...)
+#define LIBXLD__LOG_ERRNO(ctx, loglevel, _d, _f, _a...)
+#define LIBXLD__LOG_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...)
+#endif
+  /* all of these macros preserve errno (saving and restoring) */
+
+/* 
+ * A macro to help retain the first failure in "do as much as you can"
+ * situations.  Note the hard-coded use of the variable name `rc`.
+ */
+#define ACCUMULATE_RC(rc_acc) ((rc_acc) = (rc_acc) ?: rc)
+
+/* Convert pfn to physical address space. */
+#define pfn_to_paddr(x) ((uint64_t)(x) << XC_PAGE_SHIFT)
+
+/* logging */
+_hidden void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+             const char *file /* may be 0 */, int line /* ignored if !file */,
+             const char *func /* may be 0 */,
+             uint32_t domid /* may be INVALID_DOMID */,
+             const char *fmt, va_list al)
+     __attribute__((format(printf,8,0)));
+
+_hidden void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
+            const char *file /* may be 0 */, int line /* ignored if !file */,
+            const char *func /* may be 0 */,
+            uint32_t domid /* may be INVALID_DOMID */,
+            const char *fmt, ...)
+     __attribute__((format(printf,8,9)));
+
+     /* these functions preserve errno (saving and restoring) */
+
+typedef struct libxl__gc libxl__gc;
+typedef struct libxl__egc libxl__egc;
+typedef struct libxl__ao libxl__ao;
+typedef struct libxl__aop_occurred libxl__aop_occurred;
+typedef struct libxl__osevent_hook_nexus libxl__osevent_hook_nexus;
+typedef struct libxl__osevent_hook_nexi libxl__osevent_hook_nexi;
+typedef struct libxl__device_type libxl__device_type;
+typedef struct libxl__json_object libxl__json_object;
+typedef struct libxl__carefd libxl__carefd;
+typedef struct libxl__ev_slowlock libxl__ev_slowlock;
+typedef struct libxl__dm_resume_state libxl__dm_resume_state;
+typedef struct libxl__ao_device libxl__ao_device;
+typedef struct libxl__multidev libxl__multidev;
+typedef struct libxl__ev_immediate libxl__ev_immediate;
+
+typedef struct libxl__domain_create_state libxl__domain_create_state;
+typedef void libxl__domain_create_cb(struct libxl__egc *egc,
+                                     libxl__domain_create_state *dcs,
+                                     int rc, uint32_t domid);
+
+typedef struct libxl__colo_device_nic libxl__colo_device_nic;
+typedef struct libxl__colo_qdisk libxl__colo_qdisk;
+typedef struct libxl__colo_proxy_state libxl__colo_proxy_state;
+typedef struct libxl__colo_save_state libxl__colo_save_state;
+typedef struct libxl__colo_restore_state libxl__colo_restore_state;
+
+_hidden void libxl__alloc_failed(libxl_ctx *, const char *func,
+                         size_t nmemb, size_t size) __attribute__((noreturn));
+  /* func, size and nmemb are used only in the log message.
+   * You may pass size==0 if size and nmemb are not meaningful
+   * and should not be printed. */
+
+typedef struct libxl__ev_fd libxl__ev_fd;
+typedef void libxl__ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
+                                   int fd, short events, short revents);
+  /* Note that revents may contain POLLERR or POLLHUP regardless of
+   * events; otherwise revents contains only bits in events.  Contrary
+   * to the documentation for poll(2), POLLERR and POLLHUP can occur
+   * even if only POLLIN was set in events.  (POLLNVAL is a fatal
+   * error and will cause libxl event machinery to fail an assertion.)
+   *
+   * It is not permitted to listen for the same or overlapping events
+   * on the same fd using multiple different libxl__ev_fd's.
+   *
+   * Note that (depending on the underlying event loop implementation)
+   * it is possible that a the fd callback system is `level triggered'
+   * or `event triggered'.  That is, the callback may be called only
+   * once for each transition from not ready to ready.  So the
+   * callback must generally contain a loop which exhausts the fd,
+   * rather than relying on being called again if the fd is still
+   * ready.
+   *
+   * (Spurious wakeups, and spurious bits set in revents, are
+   * suppressed by the libxl event core.)
+   */
+struct libxl__ev_fd {
+    /* caller should include this in their own struct */
+    /* read-only for caller, who may read only when registered: */
+    int fd;
+    short events;
+    libxl__ev_fd_callback *func;
+    /* remainder is private for libxl__ev_fd... */
+    LIBXL_LIST_ENTRY(libxl__ev_fd) entry;
+    libxl__osevent_hook_nexus *nexus;
+};
+
+
+typedef struct libxl__ao_abortable libxl__ao_abortable;
+typedef void libxl__ao_abortable_callback(libxl__egc *egc,
+                  libxl__ao_abortable *ao_abortable, int rc /* ABORTED */);
+
+struct libxl__ao_abortable {
+    /* caller must fill this in and it must remain valid */
+    libxl__ao *ao;
+    libxl__ao_abortable_callback *callback;
+    /* remainder is private for abort machinery */
+    bool registered;
+    LIBXL_LIST_ENTRY(libxl__ao_abortable) entry;
+    /*
+     * For nested aos:
+     *  Semantically, abort affects the whole tree of aos,
+     *    not just the parent.
+     *  libxl__ao_abortable.ao refers to the child, so
+     *    that the child callback sees the right ao.  (After all,
+     *    it was code dealing with the child that set .ao.)
+     *  But, the abortable is recorded on the "abortables" list
+     *    for the ultimate root ao, so that every possible child
+     *    abort occurs as a result of the abort of the parent.
+     *  We set ao->aborting only in the root.
+     */
+};
+
+_hidden int libxl__ao_abortable_register(libxl__ao_abortable*);
+_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable*);
+
+static inline void libxl__ao_abortable_init
+  (libxl__ao_abortable *c) { c->registered = 0; }
+static inline bool libxl__ao_abortable_isregistered
+  (const libxl__ao_abortable *c) { return c->registered; }
+
+int libxl__ao_aborting(libxl__ao *ao); /* -> 0 or ERROR_ABORTED */
+
+
+typedef struct libxl__ev_time libxl__ev_time;
+typedef void libxl__ev_time_callback(libxl__egc *egc, libxl__ev_time *ev,
+                                     const struct timeval *requested_abs,
+                                     int rc); /* TIMEDOUT or ABORTED */
+struct libxl__ev_time {
+    /* caller should include this in their own struct */
+    /* read-only for caller, who may read only when registered: */
+    libxl__ev_time_callback *func;
+    /* remainder is private for libxl__ev_time... */
+    int infinite; /* not registered in list or with app if infinite */
+    LIBXL_TAILQ_ENTRY(libxl__ev_time) entry;
+    struct timeval abs;
+    libxl__osevent_hook_nexus *nexus;
+    libxl__ao_abortable abrt;
+};
+
+typedef struct libxl__ev_xswatch libxl__ev_xswatch;
+typedef void libxl__ev_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch*,
+                            const char *watch_path, const char *event_path);
+struct libxl__ev_xswatch {
+    /* caller should include this in their own struct */
+    /* read-only for caller, who may read only when registered: */
+    char *path;
+    libxl__ev_xswatch_callback *callback;
+    /* remainder is private for libxl__ev_xswatch... */
+    int slotnum; /* registered iff slotnum >= 0 */
+    uint32_t counterval;
+};
+
+typedef struct libxl__ev_evtchn libxl__ev_evtchn;
+typedef void libxl__ev_evtchn_callback(libxl__egc *egc, libxl__ev_evtchn*);
+struct libxl__ev_evtchn {
+    /* caller must fill these in, and they must all remain valid */
+    libxl__ev_evtchn_callback *callback;
+    int port;
+    /* remainder is private for libxl__ev_evtchn_... */
+    bool waiting;
+    LIBXL_LIST_ENTRY(libxl__ev_evtchn) entry;
+};
+
+/*
+ * An entry in the watch_slots table is either:
+ *  1. an entry in the free list, ie NULL or pointer to next free list entry
+ *  2. an pointer to a libxl__ev_xswatch
+ *
+ * But we don't want to use unions or type-punning because the
+ * compiler might "prove" that our code is wrong and misoptimise it.
+ *
+ * The rules say that all struct pointers have identical
+ * representation and alignment requirements (C99+TC1+TC2 6.2.5p26) so
+ * what we do is simply declare our array as containing only the free
+ * list pointers, and explicitly convert from and to our actual
+ * xswatch pointers when we store and retrieve them.
+ */
+typedef struct libxl__ev_watch_slot {
+    LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty;
+} libxl__ev_watch_slot;
+
+_hidden libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc,
+                                                      int slotnum);
+
+
+typedef struct libxl__ev_child libxl__ev_child;
+typedef void libxl__ev_child_callback(libxl__egc *egc, libxl__ev_child*,
+                                      pid_t pid, int status);
+struct libxl__ev_child {
+    /* caller should include this in their own struct */
+    /* read-only for caller: */
+    pid_t pid; /* -1 means unused ("unregistered", ie Idle) */
+    libxl__ev_child_callback *callback;
+    /* remainder is private for libxl__ev_... */
+    LIBXL_LIST_ENTRY(struct libxl__ev_child) entry;
+};
+
+/* libxl__ev_immediate
+ *
+ * Allow to call a non-reentrant callback.
+ *
+ * `callback' will be called immediately as a new event.
+ */
+struct libxl__ev_immediate {
+    /* filled by user */
+    void (*callback)(libxl__egc *, libxl__ev_immediate *);
+    /* private to libxl__ev_immediate */
+    LIBXL_STAILQ_ENTRY(libxl__ev_immediate) entry;
+};
+void libxl__ev_immediate_register(libxl__egc *, libxl__ev_immediate *);
+
+/*
+ * Lock for device hotplug, qmp_lock.
+ *
+ * libxl__ev_slowlock implement a lock that is outside of CTX_LOCK in the
+ * lock hierarchy. It can be used when one want to make QMP calls to QEMU,
+ * which may take a significant amount time.
+ * It is to be acquired by an ao event callback.
+ *
+ * If libxl__ev_devlock is needed, it should be acquired while every
+ * libxl__ev_qmp are Idle for the current domain.
+ *
+ * It is to be acquired when adding/removing devices or making changes
+ * to them when this is a slow operation and json_lock isn't appropriate.
+ *
+ * Possible states of libxl__ev_slowlock:
+ *   Undefined
+ *    Might contain anything.
+ *  Idle
+ *    Struct contents are defined enough to pass to any
+ *    libxl__ev_slowlock_* function.
+ *    The struct does not contain references to any allocated private
+ *    resources so can be thrown away.
+ *  Active
+ *    Waiting to get a lock.
+ *    Needs to wait until the callback is called.
+ *  LockAcquired
+ *    libxl__ev_slowlock_unlock will need to be called to release the lock
+ *    and the resources of libxl__ev_slowlock.
+ *
+ *  libxl__ev_*lock_init: Undefined/Idle -> Idle
+ *  libxl__ev_slowlock_lock: Idle -> Active
+ *    May call callback synchronously.
+ *  libxl__ev_slowlock_unlock: LockAcquired/Idle -> Idle
+ *  libxl__ev_slowlock_dispose: Idle/Active/LockAcquired -> Idle
+ *    The callback will not be called anymore.
+ *  callback:     When called: Active -> LockAcquired (on error: Idle)
+ *    The callback is only called once.
+ */
+struct libxl__ev_slowlock {
+    /* filled by user */
+    libxl__ao *ao;
+    libxl_domid domid;
+    void (*callback)(libxl__egc *, libxl__ev_slowlock *, int rc);
+    /* private to libxl__ev_slowlock* */
+    libxl__ev_child child;
+    const char *userdata_userid;
+    char *path; /* path of the lock file itself */
+    int fd;
+    bool held;
+};
+_hidden void libxl__ev_devlock_init(libxl__ev_slowlock *);
+_hidden void libxl__ev_qmplock_init(libxl__ev_slowlock *);
+_hidden void libxl__ev_slowlock_lock(libxl__egc *, libxl__ev_slowlock *);
+_hidden void libxl__ev_slowlock_unlock(libxl__gc *, libxl__ev_slowlock *);
+_hidden void libxl__ev_slowlock_dispose(libxl__gc *, libxl__ev_slowlock *);
+
+/*
+ * QMP asynchronous calls
+ *
+ * This facility allows a command to be sent to QEMU, and the response
+ * to be handed to a callback function.
+ *
+ * Commands can be submited one after an other with the same
+ * connection (e.g. the result from the "add-fd" command need to be
+ * use in a follow-up command before disconnecting from QMP). A
+ * libxl__ev_qmp can be reused when the callback is been called in
+ * order to use the same connection.
+ *
+ * Only one connection at a time can be made to one QEMU, so avoid
+ * keeping a libxl__ev_qmp Connected for to long and call
+ * libxl__ev_qmp_dispose as soon as it is not needed anymore.
+ *
+ * Possible states of a libxl__ev_qmp:
+ *  Undefined
+ *    Might contain anything.
+ *  Idle
+ *    Struct contents are defined enough to pass to any
+ *    libxl__ev_qmp_* function.
+ *    The struct does not contain references to any allocated private
+ *    resources so can be thrown away.
+ *  Active
+ *    Currently waiting for the callback to be called.
+ *    _dispose must be called to reclaim resources.
+ *  Connected
+ *    Struct contain allocated ressources.
+ *    Calling _send() with this same ev will use the same QMP connection.
+ *    _dispose() must be called to reclaim resources.
+ *
+ * libxl__ev_qmp_init: Undefined/Idle -> Idle
+ *
+ * libxl__ev_qmp_send: Idle/Connected -> Active (on error: Idle)
+ *    Sends a command to QEMU.
+ *    callback will be called when a response is received or when an
+ *    error as occured.
+ *    callback isn't called synchronously.
+ *
+ * libxl__ev_qmp_dispose: Connected/Active/Idle -> Idle
+ *
+ * callback: When called: Active -> Connected (on error: Idle/Connected)
+ *    When called, ev is Connected and can be reused or disposed of.
+ *    On error, the callback is called with response == NULL and the
+ *    error code in rc. The new state of ev depending on the value of rc:
+ *    - rc == ERROR_QMP_*: This is an error associated with the cmd to
+ *      run, ev is Connected.
+ *    - otherwise: An other error happend, ev is now Idle.
+ *    The callback is only called once.
+ */
+typedef struct libxl__ev_qmp libxl__ev_qmp;
+typedef void libxl__ev_qmp_callback(libxl__egc *egc, libxl__ev_qmp *ev,
+                                    const libxl__json_object *response,
+                                    int rc);
+
+_hidden void libxl__ev_qmp_init(libxl__ev_qmp *ev);
+_hidden int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
+                               const char *cmd, libxl__json_object *args);
+_hidden void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev);
+
+typedef enum {
+    /* initial state */
+    qmp_state_disconnected = 1,
+    /* waiting for lock */
+    qmp_state_waiting_lock,
+    /* connected to QMP socket, waiting for greeting message */
+    qmp_state_connecting,
+    /* qmp_capabilities command sent, waiting for reply */
+    qmp_state_capability_negotiation,
+    /* sending user's cmd and waiting for reply */
+    qmp_state_waiting_reply,
+    /* ready to send commands */
+    qmp_state_connected,
+} libxl__qmp_state;
+
+struct libxl__ev_qmp {
+    /* caller should include this in their own struct */
+    /* caller must fill these in, and they must all remain valid */
+    libxl__ao *ao;
+    libxl_domid domid;
+    libxl__ev_qmp_callback *callback;
+    int payload_fd; /* set to send a fd with the command, -1 otherwise */
+
+    /* read-only when Connected
+     * and not to be accessed by the caller otherwise */
+    struct {
+        int major;
+        int minor;
+        int micro;
+    } qemu_version;
+
+    /*
+     * remaining fields are private to libxl_ev_qmp_*
+     */
+
+    libxl__carefd *cfd;
+    libxl__ev_fd efd;
+    libxl__qmp_state state;
+    libxl__ev_slowlock lock;
+    libxl__ev_immediate ei;
+    int rc;
+    int id;
+    int next_id;        /* next id to use */
+    /* receive buffer */
+    char *rx_buf;
+    size_t rx_buf_size; /* current allocated size */
+    size_t rx_buf_used; /* actual data in the buffer */
+    /* sending buffer */
+    char *tx_buf;
+    size_t tx_buf_len;  /* tx_buf size */
+    size_t tx_buf_off;  /* already sent */
+    /* The message to send when ready */
+    char *msg;
+    int msg_id;
+};
+
+/* QMP parameters helpers */
+
+_hidden void libxl__qmp_param_add_string(libxl__gc *gc,
+                                         libxl__json_object **param,
+                                         const char *name, const char *s);
+_hidden void libxl__qmp_param_add_bool(libxl__gc *gc,
+                                       libxl__json_object **param,
+                                       const char *name, bool b);
+_hidden void libxl__qmp_param_add_integer(libxl__gc *gc,
+                                          libxl__json_object **param,
+                                          const char *name, const int i);
+#define QMP_PARAMETERS_SPRINTF(args, name, format, ...) \
+    libxl__qmp_param_add_string(gc, args, name, \
+                                GCSPRINTF(format, __VA_ARGS__))
+
+
+/*
+ * evgen structures, which are the state we use for generating
+ * events for the caller.
+ *
+ * In general in each case there's an internal and an external
+ * version of the _evdisable_FOO function; the internal one is
+ * used during cleanup.
+ */
+struct libxl__evgen_domain_death {
+    uint32_t domid;
+    unsigned shutdown_reported:1, death_reported:1;
+    LIBXL_TAILQ_ENTRY(libxl_evgen_domain_death) entry;
+        /* on list .death_reported ? CTX->death_list : CTX->death_reported */
+    libxl_ev_user user;
+};
+_hidden void
+libxl__evdisable_domain_death(libxl__gc*, libxl_evgen_domain_death*);
+
+struct libxl__evgen_disk_eject {
+    libxl__ev_xswatch watch;
+    uint32_t domid;
+    LIBXL_LIST_ENTRY(libxl_evgen_disk_eject) entry;
+    libxl_ev_user user;
+    char *vdev, *be_ptr_path;
+};
+_hidden void
+libxl__evdisable_disk_eject(libxl__gc*, libxl_evgen_disk_eject*);
+
+typedef struct libxl__poller libxl__poller;
+struct libxl__poller {
+    /*
+     * These are used to allow other threads to wake up a thread which
+     * may be stuck in poll, because whatever it was waiting for
+     * hadn't happened yet.  Threads which generate events will write
+     * a byte to each pipe.  A thread which is waiting will empty its
+     * own pipe, and put its poller on the pollers_event list, before
+     * releasing the ctx lock and going into poll; when it comes out
+     * of poll it will take the poller off the pollers_event list.
+     *
+     * A thread which is waiting for completion of a synchronous ao
+     * will allocate a poller and record it in the ao, so that other
+     * threads can wake it up.
+     *
+     * When a thread is done with a poller it should put it onto
+     * pollers_idle, where it can be reused later.
+     *
+     * The "poller_app" is never idle, but is sometimes on
+     * pollers_event.
+     */
+    LIBXL_LIST_ENTRY(libxl__poller) entry;
+
+    struct pollfd *fd_polls;
+    int fd_polls_allocd;
+
+    int fd_rindices_allocd;
+    int (*fd_rindices)[3]; /* see libxl_event.c:beforepoll_internal */
+
+    int wakeup_pipe[2]; /* 0 means no fd allocated */
+    bool pipe_nonempty;
+
+    /*
+     * We also use the poller to record whether any fds have been
+     * deregistered since we entered poll.  Each poller which is not
+     * idle is on the list pollers_active.  fds_deregistered is
+     * cleared by beforepoll, and tested by afterpoll.  Whenever an fd
+     * event is deregistered, we set the fds_deregistered of all non-idle
+     * pollers.  So afterpoll can tell whether any POLLNVAL is
+     * plausibly due to an fd being closed and reopened.
+     *
+     * Additionally, we record whether any fd or time event sources
+     * have been registered.  This is necessary because sometimes we
+     * need to wake up the only libxl thread stuck in
+     * eventloop_iteration so that it will pick up new fds or earlier
+     * timeouts.  osevents_added is cleared by beforepoll, and set by
+     * fd or timeout event registration.  When we are about to leave
+     * libxl (strictly, when we are about to give up an egc), we check
+     * whether there are any pollers.  If there are, then at least one
+     * of them must have osevents_added clear.  If not, we wake up the
+     * first one on the list.  Any entry on pollers_active constitutes
+     * a promise to also make this check, so the baton will never be
+     * dropped.
+     */
+    LIBXL_LIST_ENTRY(libxl__poller) active_entry;
+    bool fds_deregistered;
+    bool osevents_added;
+};
+
+struct libxl__gc {
+    /* mini-GC */
+    int alloc_maxsize; /* -1 means this is the dummy non-gc gc */
+    void **alloc_ptrs;
+    libxl_ctx *owner;
+};
+
+struct libxl__ctx {
+    xentoollog_logger *lg;
+    xc_interface *xch;
+    struct xs_handle *xsh;
+    libxl__gc nogc_gc;
+
+    const libxl_event_hooks *event_hooks;
+    void *event_hooks_user;
+
+    pthread_mutex_t lock; /* protects data structures hanging off the ctx */
+      /* Always use libxl__ctx_lock and _unlock (or the convenience
+       * macors CTX_LOCK and CTX_UNLOCK) to manipulate this.
+       *
+       * You may acquire this mutex recursively if it is convenient to
+       * do so.  You may not acquire this lock at the same time as any
+       * other lock.  If you need to call application code outside
+       * libxl (ie, a callback) with this lock held then it is
+       * necessaray to impose restrictions on the caller to maintain a
+       * proper lock hierarchy, and these restrictions must then be
+       * documented in the libxl public interface.
+       */
+
+    LIBXL_TAILQ_HEAD(libxl__event_list, libxl_event) occurred;
+
+    int osevent_in_hook;
+    const libxl_osevent_hooks *osevent_hooks;
+    void *osevent_user;
+      /* See the comment for OSEVENT_HOOK_INTERN in libxl_event.c
+       * for restrictions on the use of the osevent fields. */
+
+    libxl__poller *poller_app; /* libxl_osevent_beforepoll and _afterpoll */
+    LIBXL_LIST_HEAD(, libxl__poller) pollers_event, pollers_idle;
+    LIBXL_LIST_HEAD(, libxl__poller) pollers_active;
+
+    LIBXL_SLIST_HEAD(libxl__osevent_hook_nexi, libxl__osevent_hook_nexus)
+        hook_fd_nexi_idle, hook_timeout_nexi_idle;
+    LIBXL_LIST_HEAD(, libxl__ev_fd) efds;
+    LIBXL_TAILQ_HEAD(, libxl__ev_time) etimes;
+
+    libxl__ev_watch_slot *watch_slots;
+    int watch_nslots, nwatches;
+    LIBXL_SLIST_HEAD(, libxl__ev_watch_slot) watch_freeslots;
+    uint32_t watch_counter; /* helps disambiguate slot reuse */
+    libxl__ev_fd watch_efd;
+
+    xenevtchn_handle *xce; /* waiting must be done only with libxl__ev_evtchn* */
+    LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting;
+    libxl__ev_fd evtchn_efd;
+
+    LIBXL_LIST_HEAD(, libxl__ao) aos_inprogress;
+
+    LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death)
+        death_list /* sorted by domid */,
+        death_reported;
+    libxl__ev_xswatch death_watch;
+
+    LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens;
+
+    const libxl_childproc_hooks *childproc_hooks;
+    void *childproc_user;
+    int sigchld_selfpipe[2]; /* [0]==-1 means handler not installed */
+    libxl__ev_fd sigchld_selfpipe_efd;
+    LIBXL_LIST_HEAD(, libxl__ev_child) children;
+    bool sigchld_user_registered;
+    LIBXL_LIST_ENTRY(libxl_ctx) sigchld_users_entry;
+
+    libxl_version_info version_info;
+
+    bool libxl_domain_need_memory_0x041200_called,
+         libxl_domain_need_memory_called;
+};
+
+/*
+ * libxl__device is a transparent structure that doesn't contain private fields
+ * or external memory references, and as such can be copied by assignment.
+ */
+typedef struct {
+    uint32_t backend_devid;
+    uint32_t backend_domid;
+    uint32_t devid;
+    uint32_t domid;
+    libxl__device_kind backend_kind;
+    libxl__device_kind kind;
+} libxl__device;
+
+/* Used to know if backend of given device is QEMU */
+#define QEMU_BACKEND(dev) (\
+    (dev)->backend_kind == LIBXL__DEVICE_KIND_QDISK || \
+    (dev)->backend_kind == LIBXL__DEVICE_KIND_VFB || \
+    (dev)->backend_kind == LIBXL__DEVICE_KIND_QUSB || \
+    (dev)->backend_kind == LIBXL__DEVICE_KIND_9PFS || \
+    (dev)->backend_kind == LIBXL__DEVICE_KIND_VKBD)
+
+#define XC_PCI_BDF             "0x%x, 0x%x, 0x%x, 0x%x"
+#define PCI_DEVFN(slot, func)   ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn)         ((devfn) & 0x07)
+#define AUTO_PHP_SLOT          0x100
+
+#define PROC_PCI_NUM_RESOURCES 7
+#define PCI_BAR_IO             0x01
+
+#define PRINTF_ATTRIBUTE(x, y) __attribute__((format(printf, x, y)))
+
+struct libxl__egc {
+    /* For event-generating functions only.
+     * The egc and its gc may be accessed only on the creating thread. */
+    struct libxl__gc gc;
+    struct libxl__event_list occurred_for_callback;
+    LIBXL_TAILQ_HEAD(, libxl__ao) aos_for_callback;
+    LIBXL_TAILQ_HEAD(, libxl__aop_occurred) aops_for_callback;
+    LIBXL_STAILQ_HEAD(, libxl__ev_immediate) ev_immediates;
+};
+
+struct libxl__aop_occurred {
+    /*
+     * An aop belongs to, and may be accessed only on, the thread
+     * which created it.  It normally lives in that thread's egc.
+     *
+     * While an aop exists, it corresponds to one refcount in
+     * ao->progress_reports_outstanding, preventing ao destruction.
+     */
+    LIBXL_TAILQ_ENTRY(libxl__aop_occurred) entry;
+    libxl__ao *ao;
+    libxl_event *ev;
+    const libxl_asyncprogress_how *how;
+};
+
+#define LIBXL__AO_MAGIC              0xA0FACE00ul
+#define LIBXL__AO_MAGIC_DESTROYED    0xA0DEAD00ul
+
+struct libxl__ao {
+    /*
+     * An ao and its gc may be accessed only with the ctx lock held.
+     *
+     * Special exception: If an ao has been added to
+     * egc->aos_for_callback, the thread owning the egc may remove the
+     * ao from that list and make the callback without holding the
+     * lock.
+     *
+     * Corresponding restriction: An ao may be added only to one
+     * egc->aos_for_callback, once; rc and how must already have been
+     * set and may not be subsequently modified.  (This restriction is
+     * easily and obviously met since the ao is queued for callback
+     * only in libxl__ao_complete.)
+     */
+    uint32_t magic;
+    unsigned constructing:1, in_initiator:1, complete:1, notified:1,
+        aborting:1;
+    int manip_refcnt;
+    libxl__ao *nested_root;
+    int nested_progeny;
+    int progress_reports_outstanding;
+    int rc;
+    LIBXL_LIST_HEAD(, libxl__ao_abortable) abortables;
+    LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry;
+    libxl__gc gc;
+    libxl_asyncop_how how;
+    libxl__poller *poller;
+    uint32_t domid;
+    LIBXL_TAILQ_ENTRY(libxl__ao) entry_for_callback;
+    int outstanding_killed_child;
+};
+
+#define LIBXL_INIT_GC(gc,ctx) do{               \
+        (gc).alloc_maxsize = 0;                 \
+        (gc).alloc_ptrs = 0;                    \
+        (gc).owner = (ctx);                     \
+    } while(0)
+    /* NB, also, a gc struct ctx->nogc_gc is initialised in libxl_ctx_alloc */
+
+static inline libxl_ctx *libxl__gc_owner(libxl__gc *gc)
+{
+    return gc->owner;
+}
+
+static inline int libxl__gc_is_real(const libxl__gc *gc)
+{
+    return gc->alloc_maxsize >= 0;
+}
+
+/*
+ * Memory allocation tracking/helpers
+ *
+ * See comment "libxl memory management" in libxl.h for a description
+ * of the framework which these calls belong to.
+ *
+ * These functions deal with memory allocations of type (a) and (d) in
+ * that description.
+ *
+ * All pointers returned by these functions are registered for garbage
+ * collection on exit from the outermost libxl callframe.
+ *
+ * However, where the argument is stated to be "gc_opt", &ctx->nogc_gc
+ * may be passed instead, in which case no garbage collection will
+ * occur; the pointer must later be freed with free().  (Passing NULL
+ * for gc_opt is not permitted.)  This is for memory allocations of
+ * types (b) and (c).  The convenience macro NOGC should be used where
+ * possible.
+ *
+ * NOGC (and ctx->nogc_gc) may ONLY be used with functions which
+ * explicitly declare that it's OK.  Use with nonconsenting functions
+ * may result in leaks of those functions' internal allocations on the
+ * psuedo-gc.
+ */
+/* register ptr in gc for free on exit from outermost libxl callframe. */
+
+#define NN(...) __attribute__((nonnull(__VA_ARGS__)))
+#define NN1 __attribute__((nonnull(1)))
+ /* It used to be legal to pass NULL for gc_opt.  Get the compiler to
+  * warn about this if any slip through. */
+
+_hidden void libxl__ptr_add(libxl__gc *gc_opt, void *ptr /* may be NULL */) NN1;
+/* if this is the outermost libxl callframe then free all pointers in @gc */
+_hidden void libxl__free_all(libxl__gc *gc);
+/* allocate @size bytes. (a gc'd malloc(3)) */
+_hidden void *libxl__malloc(libxl__gc *gc_opt, size_t size) NN1;
+/* allocate and zero @size. (similar to a gc'd malloc(3)+memzero()) */
+_hidden void *libxl__zalloc(libxl__gc *gc_opt, size_t size) NN1;
+/* allocate and zero memory for an array of @nmemb members of @size each.
+ * (similar to a gc'd calloc(3)). */
+_hidden void *libxl__calloc(libxl__gc *gc_opt, size_t nmemb, size_t size) NN1;
+/* change the size of the memory block pointed to by @ptr to @new_size bytes.
+ * unlike other allocation functions here any additional space between the
+ * oldsize and @new_size is not initialised (similar to a gc'd realloc(3)).
+ * if @ptr is non-NULL and @gc_opt is not nogc_gc then @ptr must have been
+ * registered with @gc_opt previously. */
+_hidden void *libxl__realloc(libxl__gc *gc_opt, void *ptr, size_t new_size) NN1;
+/* print @fmt into an allocated string large enoughto contain the result.
+ * (similar to gc'd asprintf(3)). */
+_hidden char *libxl__sprintf(libxl__gc *gc_opt, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3) NN1;
+_hidden char *libxl__vsprintf(libxl__gc *gc, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0);
+/* duplicate the string @c (similar to a gc'd strdup(3)). */
+_hidden char *libxl__strdup(libxl__gc *gc_opt,
+                            const char *c /* may be NULL */) NN1;
+/* duplicate at most @n bytes of string @c (similar to a gc'd strndup(3)). */
+_hidden char *libxl__strndup(libxl__gc *gc_opt,
+                             const char *c /* may be NULL */,
+                             size_t n) NN1;
+/* strip the last path component from @s and return as a newly allocated
+ * string. (similar to a gc'd dirname(3)). */
+_hidden char *libxl__dirname(libxl__gc *gc_opt, const char *s) NN1;
+
+/* Make a pipe and set both ends nonblocking.  On error, nothing
+ * is left open and both fds[]==-1, and a message is logged.
+ * Useful for self-pipes. */
+_hidden int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2]);
+/* Closes the pipe fd(s).  Either or both of fds[] may be -1 meaning
+ * `not open'.  Ignores any errors.  Sets fds[] to -1. */
+_hidden void libxl__pipe_close(int fds[2]);
+
+/* Change the flags for the file description associated with fd to
+ *    (flags & mask) | val.
+ * If r_oldflags != NULL then sets *r_oldflags to the original set of
+ * flags.
+ */
+_hidden int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
+                                        int mask, int val, int *r_oldflags);
+/* Restores the flags for the file description associated with fd to
+ * to the previous value (returned by libxl__fd_flags_modify_save)
+ */
+_hidden int libxl__fd_flags_restore(libxl__gc *gc, int fd, int old_flags);
+
+/* Each of these logs errors and returns a libxl error code.
+ * They do not mind if path is already removed.
+ * For _file, path must not be a directory; for _directory it must be. */
+_hidden int libxl__remove_file(libxl__gc *gc, const char *path);
+_hidden int libxl__remove_directory(libxl__gc *gc, const char *path);
+_hidden int libxl__remove_file_or_directory(libxl__gc *gc, const char *path);
+
+
+_hidden char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array);
+
+/* treats kvs as pairs of keys and values and writes each to dir. */
+_hidden int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
+                             const char *dir, char **kvs);
+/* as writev but also sets the permissions on each path */
+_hidden int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
+                                   const char *dir, char *kvs[],
+                                   struct xs_permissions *perms,
+                                   unsigned int num_perms);
+/* _atonce creates a transaction and writes all keys at once */
+_hidden int libxl__xs_writev_atonce(libxl__gc *gc,
+                             const char *dir, char **kvs);
+   /* Each fn returns 0 on success.
+    * On error: returns -1, sets errno (no logging) */
+
+_hidden char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid);
+   /* On error: logs, returns NULL, sets errno. */
+
+_hidden char *libxl__xs_read(libxl__gc *gc, xs_transaction_t t,
+                             const char *path);
+_hidden char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
+                                   const char *path, unsigned int *nb);
+   /* On error: returns NULL, sets errno (no logging) */
+_hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid);
+
+_hidden int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
+                                           libxl_domid *domid_out);
+
+/*----- "checked" xenstore access functions -----*/
+/* Each of these functions will check that it succeeded; if it
+ * fails it logs and returns ERROR_FAIL.
+ */
+
+int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
+                      const char *path, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(4, 0);
+int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
+                     const char *path, const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5);
+
+/* On success, path will exist and will have an empty value */
+int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
+                    const char *path, struct xs_permissions *perms,
+                    unsigned int num_perms);
+
+/* On success, *result_out came from the gc.
+ * On error, *result_out is undefined.
+ * ENOENT is regarded as error.
+ */
+int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
+                             const char *path, const char **result_out);
+
+/* On success, *result_out came from the gc.
+ * On error, *result_out is undefined.
+ * ENOENT counts as success but sets *result_out=0
+ */
+int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
+                           const char *path, const char **result_out);
+
+/* Does not include a trailing null.
+ * May usefully be combined with GCSPRINTF if the format string
+ * behaviour of libxl__xs_printf is desirable. */
+int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
+                            const char *path, const char *string);
+
+/* ENOENT is not an error (even if the parent directories don't exist) */
+int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path);
+
+/* Transaction functions, best used together.
+ * The caller should initialise *t to 0 (XBT_NULL) before calling start.
+ * Each function leaves *t!=0 iff the transaction needs cleaning up.
+ *
+ * libxl__xs_transaction_commit returns:
+ *   <0  failure - a libxl error code
+ *   +1  commit conflict; transaction has been destroyed and caller
+ *        must go round again (call _start again and retry)
+ *    0  committed successfully
+ *
+ * The intended usage pattern looks like this:
+ *    int some_function()
+ *    {
+ *        int rc;
+ *        xs_transaction_t t = 0;
+ *        // other initialisations
+ *
+ *        // do whatever you need to do before the xenstore stuff
+ *        // errors?  set rc and goto out.
+ *
+ *        for (;;) {
+ *            rc = libxl__xs_transaction_start(gc, &t);
+ *            if (rc) goto out;
+ *
+ *            // do your work here, including all xenstore reads and writes
+ *            // libxl__xs_*_checked are useful; pass them t.
+ *            // errors?  set rc and goto out.
+ *
+ *            rc = libxl__xs_transaction_commit(gc, &t);
+ *            if (!rc) break;
+ *            if (rc<0) goto out;
+ *        }
+ *
+ *        // now the xenstore transaction succeeded
+ *        // do whatever else you need to do
+ *        // errors?  set rc and goto out.
+ *
+ *        return something;
+ *
+ *     out:
+ *        // other cleanups
+ *        libxl__xs_transaction_abort(gc, &t);
+ *        // other cleanups
+ *        return rc;
+ *    }
+ *
+ * Formally the states of *t are:
+ *
+ *  name     value of *t  description
+ *   Idle         0         no transaction exists
+ *   Ready        non-0     ready for work, nothing done yet
+ *   Busy         non-0     writes have been made but we are not finished
+ *   Uncommitted  non-0     writes have been made and should be committed
+ *
+ * libxl__xs_transaction_start:  Idle -> Ready (on error: Idle)
+ *
+ * The transaction goes from Ready to Busy, and from Busy to
+ * Uncommitted, by the use of xenstore read and write operations
+ * (libxl__xs_..., xs_...) made by libxl__xs_transaction's caller.
+ *
+ * libxl__xs_transaction_commit:  Ready/Uncommitted -> Idle
+ *     on success (returns 0): xenstore has been updated
+ *     on error (<0) or conflict (+1): updates discarded
+ *
+ * libxl__xs_transaction_abort:  Any -> Idle  (any updates discarded)
+ */
+int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t);
+int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t);
+void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t);
+
+
+
+/*
+ * This is a recursive delete, from top to bottom. What this function does
+ * is remove empty folders that contained the deleted entry.
+ *
+ * It mimics xenstore-rm -t behaviour.
+ */
+_hidden int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
+                                   const char *user_path);
+
+/*
+ * Event generation functions provided by the libxl event core to the
+ * rest of libxl.  Implemented in terms of _beforepoll/_afterpoll
+ * and/or the fd registration machinery, as provided by the
+ * application.
+ *
+ * Semantics are similar to those of the fd and timeout registration
+ * functions provided to libxl_osevent_register_hooks.
+ *
+ * Non-0 returns from libxl__ev_{modify,deregister} have already been
+ * logged by the core and should be returned unmodified to libxl's
+ * caller; NB that they may be valid libxl error codes but they may
+ * also be positive numbers supplied by the caller.
+ *
+ * In each case, there is a libxl__ev_FOO structure which can be in
+ * one of three states:
+ *
+ *   Undefined   - Might contain anything.  All-bits-zero is
+ *                 an undefined state.
+ *
+ *   Idle        - Struct contents are defined enough to pass to any
+ *                 libxl__ev_FOO function but not registered and
+ *                 callback will not be called.  The struct does not
+ *                 contain references to any allocated resources so
+ *                 can be thrown away.
+ *
+ *   Active      - Request for events has been registered and events
+ *                 may be generated.  _deregister must be called to
+ *                 reclaim resources.
+ *
+ * These functions are provided for each kind of event KIND:
+ *
+ *   int libxl__ev_KIND_register(libxl__gc *gc, libxl__ev_KIND *GEN,
+ *                              libxl__ev_KIND_callback *FUNC,
+ *                              DETAILS);
+ *      On entry *GEN must be in state Undefined or Idle.
+ *      Returns a libxl error code; on error return *GEN is Idle.
+ *      On successful return *GEN is Active and FUNC wil be
+ *      called by the event machinery in future.  FUNC will
+ *      not be called from within the call to _register.
+ *      FUNC will be called with the context locked (with CTX_LOCK).
+ *
+ *  void libxl__ev_KIND_deregister(libxl__gc *gc, libxl__ev_KIND *GEN_upd);
+ *      On entry *GEN must be in state Active or Idle.
+ *      On return it is Idle.  (Idempotent.)
+ *
+ *  void libxl__ev_KIND_init(libxl__ev_KIND *GEN);
+ *      Provided for initialising an Undefined KIND.
+ *      On entry *GEN must be in state Idle or Undefined.
+ *      On return it is Idle.  (Idempotent.)
+ *
+ *  int libxl__ev_KIND_isregistered(const libxl__ev_KIND *GEN);
+ *      On entry *GEN must be Idle or Active.
+ *      Returns nonzero if it is Active, zero otherwise.
+ *      Cannot fail.
+ *
+ *  int libxl__ev_KIND_modify(libxl__gc*, libxl__ev_KIND *GEN,
+ *                            DETAILS);
+ *      Only provided for some kinds of generator.
+ *      On entry *GEN must be Active and on return, whether successful
+ *      or not, it will be Active.
+ *      Returns a libxl error code; on error the modification
+ *      is not effective.
+ *
+ * All of these functions are fully threadsafe and may be called by
+ * general code in libxl even from within event callback FUNCs.
+ * The ctx will be locked on entry to each FUNC and FUNC should not
+ * unlock it.
+ *
+ * Callers of libxl__ev_KIND_register must ensure that the
+ * registration is undone, with _deregister, in libxl_ctx_free.
+ * This means that normally each kind of libxl__evgen (ie each
+ * application-requested event source) needs to be on a list so that
+ * it can be automatically deregistered as promised in libxl_event.h.
+ */
+
+
+_hidden int libxl__ev_fd_register(libxl__gc*, libxl__ev_fd *ev_out,
+                                  libxl__ev_fd_callback*,
+                                  int fd, short events /* as for poll(2) */);
+_hidden int libxl__ev_fd_modify(libxl__gc*, libxl__ev_fd *ev,
+                                short events);
+_hidden void libxl__ev_fd_deregister(libxl__gc*, libxl__ev_fd *ev);
+static inline void libxl__ev_fd_init(libxl__ev_fd *efd)
+                    { efd->fd = -1; }
+static inline int libxl__ev_fd_isregistered(const libxl__ev_fd *efd)
+                    { return efd->fd >= 0; }
+
+_hidden int libxl__ev_time_register_rel(libxl__ao*, libxl__ev_time *ev_out,
+                                        libxl__ev_time_callback*,
+                                        int milliseconds /* as for poll(2) */);
+_hidden int libxl__ev_time_register_abs(libxl__ao*, libxl__ev_time *ev_out,
+                                        libxl__ev_time_callback*,
+                                        struct timeval);
+_hidden int libxl__ev_time_modify_rel(libxl__gc*, libxl__ev_time *ev,
+                                      int milliseconds /* as for poll(2) */);
+_hidden int libxl__ev_time_modify_abs(libxl__gc*, libxl__ev_time *ev,
+                                      struct timeval);
+_hidden void libxl__ev_time_deregister(libxl__gc*, libxl__ev_time *ev);
+static inline void libxl__ev_time_init(libxl__ev_time *ev)
+                { ev->func = 0; libxl__ao_abortable_init(&ev->abrt); }
+static inline int libxl__ev_time_isregistered(const libxl__ev_time *ev)
+                { return !!ev->func; }
+
+
+_hidden int libxl__ev_xswatch_register(libxl__gc*, libxl__ev_xswatch *xsw_out,
+                                       libxl__ev_xswatch_callback*,
+                                       const char *path /* copied */);
+_hidden void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch*);
+
+static inline void libxl__ev_xswatch_init(libxl__ev_xswatch *xswatch_out)
+                { xswatch_out->slotnum = -1; }
+static inline int libxl__ev_xswatch_isregistered(const libxl__ev_xswatch *xw)
+                { return xw->slotnum >= 0; }
+
+
+/*
+ * The evtchn facility is one-shot per call to libxl__ev_evtchn_wait.
+ * You should:
+ *   Use libxl__ctx_evtchn_init to make sure CTX->xce is valid;
+ *   Call some suitable xc bind function on (or to obtain) the port;
+ *   Then call libxl__ev_evtchn_wait.
+ *
+ * When the event is signaled then the callback will be made, once.
+ * Then you must call libxl__ev_evtchn_wait again, if desired.
+ *
+ * You must NOT call xenevtchn_unmask.  wait will do that for you.
+ *
+ * Calling libxl__ev_evtchn_cancel will arrange for libxl to disregard
+ * future occurrences of event.  Both libxl__ev_evtchn_wait and
+ * libxl__ev_evtchn_cancel are idempotent.
+ *
+ * (Note of course that an event channel becomes signaled when it is
+ * first bound, so you will get one call to libxl__ev_evtchn_wait
+ * "right away"; unless you have won a very fast race, the condition
+ * you were waiting for won't exist yet so when you check for it
+ * you'll find you need to call wait again.)
+ *
+ * You must not wait on the same port twice at once (that is, with
+ * two separate libxl__ev_evtchn's).
+ */
+_hidden int libxl__ev_evtchn_wait(libxl__gc*, libxl__ev_evtchn *evev);
+_hidden void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev);
+
+static inline void libxl__ev_evtchn_init(libxl__ev_evtchn *evev)
+                { evev->waiting = 0; }
+static inline bool libxl__ev_evtchn_iswaiting(const libxl__ev_evtchn *evev)
+                { return evev->waiting; }
+
+_hidden int libxl__ctx_evtchn_init(libxl__gc *gc); /* for libxl_ctx_alloc */
+
+/*
+ * For making subprocesses.  This is the only permitted mechanism for
+ * code in libxl to do so.
+ *
+ * In the parent, returns the pid, filling in childw_out.
+ * In the child, returns 0.
+ * If it fails, returns a libxl error (all of which are -ve).
+ *
+ * The child should go on to exec (or exit) soon.  The child may not
+ * make any further calls to libxl infrastructure, except for memory
+ * allocation and logging.  If the child needs to use xenstore it
+ * must open its own xs handle and use it directly, rather than via
+ * the libxl event machinery.
+ *
+ * The parent may signal the child but it must not reap it.  That will
+ * be done by the event machinery.
+ *
+ * The child death event will generate exactly one event callback; until
+ * then the childw is Active and may not be reused.
+ *
+ * libxl__ev_child_kill_deregister: Active -> Idle
+ *   This will transfer ownership of the child process death event from
+ *   `ch' to `ao', thus deregister the callback.
+ *   The `ao' completion will wait until the child have been reaped by the
+ *   event machinery.
+ */
+_hidden pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *childw_out,
+                                 libxl__ev_child_callback *death);
+static inline void libxl__ev_child_init(libxl__ev_child *childw_out)
+                { childw_out->pid = -1; }
+static inline int libxl__ev_child_inuse(const libxl__ev_child *childw_out)
+                { return childw_out->pid >= 0; }
+_hidden void libxl__ev_child_kill_deregister(libxl__ao *ao,
+                                             libxl__ev_child *ch,
+                                             int sig);
+
+/* Useable (only) in the child to once more make the ctx useable for
+ * xenstore operations.  logs failure in the form "what: <error
+ * message>". */
+_hidden int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what);
+
+
+/*
+ * Other event-handling support provided by the libxl event core to
+ * the rest of libxl.
+ */
+
+_hidden void libxl__event_occurred(libxl__egc*, libxl_event *event);
+  /* Arranges to notify the application that the event has occurred.
+   * event should be suitable for passing to libxl_event_free. */
+
+_hidden libxl_event *libxl__event_new(libxl__egc*, libxl_event_type,
+                                      uint32_t domid,
+                                      libxl_ev_user for_user);
+  /* Convenience function.
+   * Allocates a new libxl_event, fills in domid and type.
+   * Cannot fail. */
+
+#define NEW_EVENT(egc, type, domid, user)                        \
+    libxl__event_new((egc), LIBXL_EVENT_TYPE_##type, (domid), (user))
+    /* Convenience macro. */
+
+/*
+ * In general, call this via the macro LIBXL__EVENT_DISASTER.
+ *
+ * Event-generating functions, or ao machinery, may call this if they
+ * might have wanted to generate an event (either an internal one ie a
+ * libxl__ev_FOO_callback or an application event), but are prevented
+ * from doing so due to eg lack of memory.
+ *
+ * NB that this function may return and the caller isn't supposed to
+ * then crash, although it may fail (and henceforth leave things in a
+ * state where many or all calls fail).
+ */
+_hidden void libxl__event_disaster(libxl__gc*, const char *msg, int errnoval,
+                                   libxl_event_type type /* may be 0 */,
+                                   const char *file, int line,
+                                   const char *func);
+#define LIBXL__EVENT_DISASTER(gc, msg, errnoval, type) \
+    libxl__event_disaster(gc, msg, errnoval, type, __FILE__,__LINE__,__func__)
+
+
+/* Fills in, or disposes of, the resources held by, a poller whose
+ * space the caller has allocated.  ctx must be locked. */
+_hidden int libxl__poller_init(libxl__gc *gc, libxl__poller *p);
+_hidden void libxl__poller_dispose(libxl__poller *p);
+
+/* Obtain a fresh poller from malloc or the idle list, and put it
+ * away again afterwards.  _get can fail, returning NULL.
+ * ctx must be locked. */
+_hidden libxl__poller *libxl__poller_get(libxl__gc *gc);
+_hidden void libxl__poller_put(libxl_ctx*, libxl__poller *p /* may be NULL */);
+
+/* Notifies whoever is polling using p that they should wake up.
+ * ctx must be locked. */
+_hidden void libxl__poller_wakeup(libxl__gc *egc, libxl__poller *p);
+
+/* Internal to fork and child reaping machinery */
+extern const libxl_childproc_hooks libxl__childproc_default_hooks;
+int libxl__sigchld_needed(libxl__gc*); /* non-reentrant idempotent, logs errs */
+void libxl__sigchld_notneeded(libxl__gc*); /* non-reentrant idempotent */
+void libxl__sigchld_check_stale_handler(void);
+int libxl__self_pipe_wakeup(int fd); /* returns 0 or -1 setting errno */
+int libxl__self_pipe_eatall(int fd); /* returns 0 or -1 setting errno */
+
+
+_hidden int libxl__atfork_init(libxl_ctx *ctx);
+
+
+/* File references */
+typedef struct {
+    /*
+     * Path is always set if the file reference is valid. However if
+     * mapped is true then the actual file may already be unlinked.
+     */
+    const char * path;
+    int mapped;
+    void * data;
+    size_t size;
+} libxl__file_reference;
+_hidden int libxl__file_reference_map(libxl__file_reference *f);
+_hidden int libxl__file_reference_unmap(libxl__file_reference *f);
+
+/* from xl_dom */
+_hidden libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid);
+_hidden int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid);
+_hidden libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid);
+_hidden int libxl__sched_set_params(libxl__gc *gc, uint32_t domid,
+                                    libxl_domain_sched_params *scparams);
+_hidden int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
+                                              libxl_domain_config *const d_config);
+
+typedef struct {
+    uint32_t store_port;
+    uint32_t store_domid;
+    unsigned long store_mfn;
+
+    uint32_t console_port;
+    uint32_t console_domid;
+    unsigned long console_mfn;
+    char *console_tty;
+
+    char *saved_state;
+    int dm_monitor_fd;
+
+    libxl__file_reference pv_kernel;
+    libxl__file_reference pv_ramdisk;
+    const char *shim_path;
+    const char *shim_cmdline;
+    const char *pv_cmdline;
+
+    /* 
+     * dm_runas: If set, pass qemu the `-runas` parameter with this
+     *  string as an argument
+     * dm_kill_uid: If set, the devicemodel should be killed by
+     *  destroying all processes with this uid.
+     */
+    char *dm_runas, *dm_kill_uid;
+
+    xen_vmemrange_t *vmemranges;
+    uint32_t num_vmemranges;
+
+    xen_pfn_t vuart_gfn;
+    evtchn_port_t vuart_port;
+
+    /* ARM only to deal with broken firmware */
+    uint32_t clock_frequency;
+
+    /* Whether this domain is being migrated/restored, or booting fresh.  Only
+     * applicable to the primary domain, not support domains (e.g. stub QEMU). */
+    bool restore;
+} libxl__domain_build_state;
+
+_hidden void libxl__domain_build_state_init(libxl__domain_build_state *s);
+_hidden void libxl__domain_build_state_dispose(libxl__domain_build_state *s);
+
+_hidden int libxl__build_pre(libxl__gc *gc, uint32_t domid,
+              libxl_domain_config * const d_config,
+              libxl__domain_build_state *state);
+_hidden int libxl__build_post(libxl__gc *gc, uint32_t domid,
+               libxl_domain_build_info *info, libxl__domain_build_state *state,
+               char **vms_ents, char **local_ents);
+
+_hidden int libxl__build_pv(libxl__gc *gc, uint32_t domid,
+             libxl_domain_config *const d_config, libxl__domain_build_state *state);
+_hidden int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
+              libxl_domain_config *d_config,
+              libxl__domain_build_state *state);
+
+_hidden int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
+                                        const char *cmd);
+_hidden int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
+                                 const char *old_name, const char *new_name,
+                                 xs_transaction_t trans);
+
+/* Deprecated, use libxl__dm_resume instead. */
+_hidden int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid);
+
+_hidden const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
+                                         const char *userdata_userid,
+                                         const char *wh);
+_hidden void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid);
+/* Caller must hold userdata store lock before calling
+ * libxl__userdata_{retrieve,store}
+ * See libxl__{un,}lock_domain_userdata.
+ */
+_hidden int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
+                                     const char *userdata_userid,
+                                     uint8_t **data_r, int *datalen_r);
+_hidden int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
+                                  const char *userdata_userid,
+                                  const uint8_t *data, int datalen);
+
+/* Deprecated, use libxl__domain_resume instead */
+_hidden int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid,
+                                            int suspend_cancel);
+/* Deprecated, use libxl__domain_unpause instead */
+_hidden int libxl__domain_unpause_deprecated(libxl__gc *,
+                                             libxl_domid domid);
+
+/* Call libxl__dm_resume_init() and fill the first few fields,
+ * then call one of libxl__domain_resume / libxl__domain_unpause
+ * or directly libxl__dm_resume if only the device model needs to be
+ * "resumed". */
+struct libxl__dm_resume_state {
+    /* caller must fill these in, and they must all remain valid */
+    libxl__ao *ao;
+    libxl_domid domid;
+    void (*callback)(libxl__egc *, libxl__dm_resume_state *, int rc);
+
+    /* private to libxl__domain_resume and libxl__domain_unpause */
+    void (*dm_resumed_callback)(libxl__egc *,
+                                libxl__dm_resume_state *, int rc);
+    /* private to libxl__domain_resume */
+    bool suspend_cancel;
+
+    /* private to libxl__dm_resume */
+    libxl__ev_qmp qmp;
+    libxl__ev_time time;
+    libxl__ev_xswatch watch;
+};
+_hidden void libxl__dm_resume(libxl__egc *egc,
+                              libxl__dm_resume_state *dmrs);
+_hidden void libxl__domain_resume(libxl__egc *egc,
+                                  libxl__dm_resume_state *dmrs,
+                                  bool suspend_cancel);
+_hidden void libxl__domain_unpause(libxl__egc *,
+                                   libxl__dm_resume_state *dmrs);
+
+/* returns 0 or 1, or a libxl error code */
+_hidden int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid);
+
+_hidden const char *libxl__domain_pvcontrol_xspath(libxl__gc*, uint32_t domid);
+_hidden char * libxl__domain_pvcontrol_read(libxl__gc *gc,
+                                            xs_transaction_t t, uint32_t domid);
+
+/* from xl_device */
+_hidden char *libxl__device_disk_string_of_backend(libxl_disk_backend backend);
+_hidden char *libxl__device_disk_string_of_format(libxl_disk_format format);
+_hidden const char *libxl__qemu_disk_format_string(libxl_disk_format format);
+_hidden int libxl__device_disk_set_backend(libxl__gc*, libxl_device_disk*);
+
+_hidden int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor);
+_hidden int libxl__device_disk_dev_number(const char *virtpath,
+                                          int *pdisk, int *ppartition);
+_hidden char *libxl__devid_to_vdev(libxl__gc *gc, int devid);
+
+_hidden int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
+                                      libxl__device_console *console,
+                                      libxl__domain_build_state *state,
+                                      libxl__device *device);
+_hidden int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
+                                    libxl__device_console *console,
+                                    libxl__domain_build_state *state);
+
+/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
+_hidden int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
+                                 libxl__device *device);
+_hidden int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
+        libxl__device *device, char **bents, char **fents, char **ro_fents);
+_hidden char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
+                                                 libxl__device_kind device_kind);
+_hidden char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device);
+_hidden char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
+                                                uint32_t domid, uint32_t devid,
+                                                libxl__device_kind device_kind);
+_hidden char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device);
+_hidden char *libxl__domain_device_libxl_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
+                                              libxl__device_kind device_kind);
+_hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path,
+                                      libxl__device *dev);
+_hidden int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
+                                    libxl_console_type type, char **tty_path);
+_hidden int libxl__device_destroy(libxl__gc *gc, libxl__device *dev);
+_hidden int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
+                                    const char *state);
+_hidden int libxl__nic_type(libxl__gc *gc, libxl__device *dev,
+                            libxl_nic_type *nictype);
+_hidden int libxl__init_console_from_channel(libxl__gc *gc,
+                                             libxl__device_console *console,
+                                             int dev_num,
+                                             libxl_device_channel *channel);
+_hidden int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
+                                 libxl__device_kind device);
+_hidden int libxl__resolve_domid(libxl__gc *gc, const char *name,
+                                 uint32_t *domid);
+
+/*
+ * For each aggregate type which can be used as an input we provide:
+ *
+ * int libxl__<type>_setdefault(gc, <type> *p):
+ *
+ *     Idempotently sets any members of "p" which is currently set to
+ *     a special value indicating that the defaults should be used
+ *     (per libxl_<type>_init) to a specific value.
+ *
+ *     All libxl API functions are expected to have arranged for this
+ *     to be called before using any values within these structures.
+ */
+_hidden int libxl__domain_config_setdefault(libxl__gc *gc,
+                                            libxl_domain_config *d_config,
+                                            uint32_t domid /* logging only */);
+_hidden int libxl__domain_create_info_setdefault(libxl__gc *gc,
+                                        libxl_domain_create_info *c_info,
+                                        const libxl_physinfo *info);
+_hidden int libxl__domain_build_info_setdefault(libxl__gc *gc,
+                                        libxl_domain_build_info *b_info);
+_hidden void libxl__rdm_setdefault(libxl__gc *gc,
+                                   libxl_domain_build_info *b_info);
+
+_hidden int libxl__domain_need_memory_calculate(libxl__gc *gc,
+                                      libxl_domain_build_info *b_info,
+                                      uint64_t *need_memkb);
+
+_hidden const char *libxl__device_nic_devname(libxl__gc *gc,
+                                              uint32_t domid,
+                                              uint32_t devid,
+                                              libxl_nic_type type);
+
+_hidden int libxl__get_domid(libxl__gc *gc, uint32_t *domid);
+
+/*----- xswait: wait for a xenstore node to be suitable -----*/
+
+typedef struct libxl__xswait_state libxl__xswait_state;
+
+/*
+ * rc describes the circumstances of this callback:
+ *
+ * rc==0
+ *
+ *     The xenstore path (may have) changed.  It has been read for
+ *     you.  The result is in data (allocated from the ao gc).
+ *     data may be NULL, which means that the xenstore read gave
+ *     ENOENT.
+ *
+ *     If you are satisfied, you MUST call libxl__xswait_stop.
+ *     Otherwise, xswait will continue waiting and watching and
+ *     will call you back later.
+ *
+ * rc==ERROR_TIMEDOUT, rc==ERROR_ABORTED
+ *
+ *     The specified timeout was reached.
+ *     This has NOT been logged (except to the debug log).
+ *     xswait will not continue (but calling libxl__xswait_stop is OK).
+ *
+ * rc!=0, !=ERROR_TIMEDOUT, !=ERROR_ABORTED
+ *
+ *     Some other error occurred.
+ *     This HAS been logged.
+ *     xswait will not continue (but calling libxl__xswait_stop is OK).
+ *
+ * xswait.path may start with with '@', in which case no read is done
+ * and the callback will always get data==0.
+ */
+typedef void libxl__xswait_callback(libxl__egc *egc,
+      libxl__xswait_state *xswa, int rc, const char *data);
+
+struct libxl__xswait_state {
+    /* caller must fill these in, and they must all remain valid */
+    libxl__ao *ao;
+    const char *what; /* for error msgs: noun phrase, what we're waiting for */
+    const char *path;
+    int timeout_ms; /* as for poll(2) */
+    libxl__xswait_callback *callback;
+    /* remaining fields are private to xswait */
+    libxl__ev_time time_ev;
+    libxl__ev_xswatch watch_ev;
+};
+
+void libxl__xswait_init(libxl__xswait_state*);
+void libxl__xswait_stop(libxl__gc*, libxl__xswait_state*); /*idempotent*/
+bool libxl__xswait_inuse(const libxl__xswait_state *ss);
+
+int libxl__xswait_start(libxl__gc*, libxl__xswait_state*);
+
+/*
+ * libxl__ev_devstate - waits a given time for a device to
+ * reach a given state.  Follows the libxl_ev_* conventions.
+ * Will generate only one event, and after that is automatically
+ * cancelled.
+ */
+typedef struct libxl__ev_devstate libxl__ev_devstate;
+typedef void libxl__ev_devstate_callback(libxl__egc *egc, libxl__ev_devstate*,
+                                         int rc);
+  /* rc will be 0, ERROR_TIMEDOUT, ERROR_ABORTED, ERROR_INVAL
+   * (meaning path was removed), or ERROR_FAIL if other stuff went
+   * wrong (in which latter case, logged) */
+
+struct libxl__ev_devstate {
+    /* read-only for caller, who may read only when waiting: */
+    int wanted;
+    libxl__ev_devstate_callback *callback;
+    /* as for the remainder, read-only public parts may also be
+     * read by the caller (notably, watch.path), but only when waiting: */
+    libxl__xswait_state w;
+};
+
+static inline void libxl__ev_devstate_init(libxl__ev_devstate *ds)
+{
+    libxl__xswait_init(&ds->w);
+}
+
+static inline void libxl__ev_devstate_cancel(libxl__gc *gc,
+                                             libxl__ev_devstate *ds)
+{
+    libxl__xswait_stop(gc,&ds->w);
+}
+
+_hidden int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
+                                    libxl__ev_devstate_callback cb,
+                                    const char *state_path,
+                                    int state, int milliseconds);
+
+/*
+ * libxl__ev_domaindeathcheck_register - arranges to call back (once)
+ * if the domain is destroyed.  If the domain dies, we log a message
+ * of the form "<what>: <explanation of the situation, including the domid>".
+ */
+
+typedef struct libxl__domaindeathcheck libxl__domaindeathcheck;
+typedef void libxl___domaindeathcheck_callback(libxl__egc *egc,
+        libxl__domaindeathcheck*,
+        int rc /* DESTROYED or ABORTED */);
+
+struct libxl__domaindeathcheck {
+    /* must be filled in by caller, and remain valid: */
+    const char *what;
+    uint32_t domid;
+    libxl___domaindeathcheck_callback *callback;
+    /* private */
+    libxl__ao_abortable abrt;
+    libxl__ev_xswatch watch;
+};
+
+_hidden int libxl__domaindeathcheck_start(libxl__ao *ao,
+                                          libxl__domaindeathcheck *dc);
+
+void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc);
+void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc);
+
+
+/*
+ * libxl__try_phy_backend - Check if there's support for the passed
+ * type of file using the PHY backend
+ * st_mode: mode_t of the file, as returned by stat function
+ *
+ * Returns 1 on success, and 0 if not suitable for phy backend.
+ */
+_hidden int libxl__try_phy_backend(mode_t st_mode);
+
+
+_hidden char *libxl__devid_to_localdev(libxl__gc *gc, int devid);
+
+_hidden int libxl__pci_numdevs(libxl__gc *gc);
+_hidden int libxl__pci_topology_init(libxl__gc *gc,
+                                     physdev_pci_device_t *devs,
+                                     int num_devs);
+
+/* from libxl_pci */
+
+_hidden void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
+                                   libxl_device_pci *pcidev, bool starting,
+                                   libxl__ao_device *aodev);
+_hidden void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
+                                           libxl__multidev *);
+_hidden int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
+                                         libxl_device_pci *pci, bool hotplug);
+_hidden bool libxl__is_igd_vga_passthru(libxl__gc *gc,
+                                        const libxl_domain_config *d_config);
+
+/* from libxl_dtdev */
+
+_hidden int libxl__device_dt_add(libxl__gc *gc, uint32_t domid,
+                                 const libxl_device_dtdev *dtdev);
+
+/*
+ *----- spawn -----
+ *
+ * Higher-level double-fork and separate detach eg as for device models
+ *
+ * Each libxl__spawn_state is in one of these states
+ *    Undefined, Idle, Attached, Detaching
+ */
+
+typedef struct libxl__obsolete_spawn_starting libxl__spawn_starting;
+/* this type is never defined, so no objects of this type exist
+ * fixme-ao  This should go away completely.  */
+
+typedef struct libxl__spawn_state libxl__spawn_state;
+
+/* Clears out a spawn state; idempotent. */
+_hidden void libxl__spawn_init(libxl__spawn_state*);
+
+/*
+ * libxl__spawn_spawn - Create a new process which will become daemonic
+ * Forks twice, to allow the child to detach entirely from the parent.
+ *
+ * We call the two generated processes the "middle child" (result of
+ * the first fork) and the "inner child" (result of the second fork
+ * which takes place in the middle child).
+ *
+ * The inner child must soon exit or exec.  It must also soon exit or
+ * notify the parent of its successful startup by writing to the
+ * xenstore path xspath OR via other means that the parent will have
+ * to set up.
+ *
+ * The user (in the parent) will be called back (confirm_cb) every
+ * time that xenstore path is modified.
+ *
+ * In both children, the ctx is not fully usable: gc and logging
+ * operations are OK, but operations on Xen and xenstore are not.
+ * (The restrictions are the same as those which apply to children
+ * made with libxl__ev_child_fork.)
+ *
+ * midproc_cb will be called in the middle child, with the pid of the
+ * inner child; this could for example record the pid.  midproc_cb
+ * should be fast, and should return.  It will be called (reentrantly)
+ * within libxl__spawn_init.
+ *
+ * failure_cb will be called in the parent on failure of the
+ * intermediate or final child; an error message will have been
+ * logged.
+ *
+ * confirm_cb, failure_cb and detached_cb will not be called
+ * reentrantly from within libxl__spawn_spawn.
+ *
+ * what: string describing the spawned process, used for logging
+ *
+ * Logs errors.  A copy of "what" is taken.
+ * Return values:
+ *  < 0   error, *spawn is now Idle and need not be detached
+ *   +1   caller is the parent, *spawn is Attached and must be detached
+ *    0   caller is now the inner child, should probably call libxl__exec
+ *
+ * The spawn state must be Undefined or Idle on entry.
+ */
+_hidden int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *spawn);
+
+/*
+ * libxl__spawn_request_detach - Detaches the daemonic child.
+ *
+ * Works by killing the intermediate process from spawn_spawn.
+ * After this function returns, failures of either child are no
+ * longer reported via failure_cb.
+ *
+ * This is not synchronous: there will be a further callback when
+ * the detach is complete.
+ *
+ * If called before the inner child has been created, this may prevent
+ * it from running at all.  Thus this should be called only when the
+ * inner child has notified that it is ready.  Normally it will be
+ * called from within confirm_cb.
+ *
+ * Logs errors.
+ *
+ * The spawn state must be Attached entry and will be Detaching
+ * on return.
+ */
+_hidden void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state*);
+
+/*
+ * libxl__spawn_initiate_failure - Propagate failure from the caller to the
+ * callee.
+ *
+ * Works by killing the intermediate process from spawn_spawn.
+ * After this function returns, a failure will be reported.
+ *
+ * This is not synchronous: there will be a further callback when
+ * the detach is complete.
+ *
+ * Caller must have logged a failure reason.
+ *
+ * The spawn state must be Attached on entry and will remain Attached. It
+ * is possible for a spawn to fail for multiple reasons, for example
+ * call(s) to libxl__spawn_initiate_failure and also for some other reason.
+ * In that case the first rc value from any source will take precedence.
+ */
+_hidden void libxl__spawn_initiate_failure(libxl__egc *egc,
+                                           libxl__spawn_state *ss, int rc);
+
+/*
+ * If successful, this should return 0.
+ *
+ * Otherwise it should return a signal number, which will be
+ * sent to the inner child; the overall spawn will then fail.
+ */
+typedef int /* signal number */
+libxl__spawn_midproc_cb(libxl__gc*, libxl__spawn_state*, pid_t inner);
+
+/*
+ * Called if the spawn failed.  The reason will have been logged.
+ * The spawn state will be Idle on entry to the callback (and
+ * it may be reused immediately if desired).
+ */
+typedef void libxl__spawn_failure_cb(libxl__egc*, libxl__spawn_state*,
+                                     int rc);
+
+/*
+ * Called when the xspath watch triggers.  xspath will have been read
+ * and the result placed in xsdata; if that failed because the key
+ * didn't exist, xspath==0.  (If it failed for some other reason,
+ * the spawn machinery calls failure_cb instead.)
+ *
+ * If the child has indicated its successful startup, or a failure
+ * has occurred, this should call libxl__spawn_detach.
+ *
+ * If the child is still starting up, should simply return, doing
+ * nothing.
+ *
+ * The spawn state will be Active on entry to the callback; there
+ * are no restrictions on the state on return; it may even have
+ * been detached and reused.
+ */
+typedef void libxl__spawn_confirm_cb(libxl__egc*, libxl__spawn_state*,
+                                     const char *xsdata);
+
+/*
+ * Called when the detach (requested by libxl__spawn_initiate_detach) has
+ * completed.  On entry to the callback the spawn state is Idle.
+ */
+typedef void libxl__spawn_detached_cb(libxl__egc*, libxl__spawn_state*);
+
+struct libxl__spawn_state {
+    /* must be filled in by user and remain valid */
+    libxl__ao *ao;
+    const char *what;
+    const char *xspath;
+    const char *pidpath; /* only used by libxl__spawn_midproc_record_pid */
+    int timeout_ms; /* -1 means forever */
+    libxl__spawn_midproc_cb *midproc_cb;
+    libxl__spawn_failure_cb *failure_cb;
+    libxl__spawn_confirm_cb *confirm_cb;
+    libxl__spawn_detached_cb *detached_cb;
+
+    /* remaining fields are private to libxl_spawn_... */
+    int detaching; /* we are in Detaching */
+    int rc; /* might be non-0 whenever we are not Idle */
+    libxl__ev_child mid; /* always in use whenever we are not Idle */
+    libxl__xswait_state xswait;
+};
+
+static inline int libxl__spawn_inuse(const libxl__spawn_state *ss)
+    { return libxl__ev_child_inuse(&ss->mid); }
+
+/*
+ * libxl_spawner_record_pid - Record given pid in xenstore
+ *
+ * This function can be passed directly as an intermediate_hook to
+ * libxl__spawn_spawn.  On failure, returns the value SIGTERM.
+ */
+_hidden int libxl__spawn_record_pid(libxl__gc*, libxl__spawn_state*,
+                                    pid_t innerchild);
+
+/*
+ * libxl__xenstore_child_wait_deprecated - Wait for daemonic child IPC
+ *
+ * This is a NOT function for waiting for ordinary child processes.
+ * If you want to run (fork/exec/wait) subprocesses from libxl:
+ *  - Make your libxl entrypoint use the ao machinery
+ *  - Use libxl__ev_child_fork, and use the callback programming style
+ *
+ * This function is intended for interprocess communication with a
+ * service process.  If the service process does not respond quickly,
+ * the whole caller may be blocked.  Therefore this function is
+ * deprecated.  This function is currently used only by
+ * libxl__wait_for_device_model_deprecated.
+ *
+ * gc: allocation pool
+ * domid: guest to work with
+ * timeout: how many seconds to wait for the state to appear
+ * what: string describing the spawned process
+ * path: path to the state file in xenstore
+ * state: expected string to wait for in path (optional)
+ * spawning: malloc'd pointer to libxl__spawn_starting (optional)
+ * check_callback: (optional)
+ * check_callback_userdata: data to pass to the callback function
+ *
+ * Returns 0 on success, and < 0 on error.
+ *
+ * This function waits the given timeout for the given path to appear
+ * in xenstore, and optionally for state in path.
+ * If path appears and state matches, check_callback is called.
+ * If check_callback returns > 0, waiting for path or state continues.
+ * Otherwise libxl__xenstore_child_wait_deprecated returns.
+ */
+_hidden int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
+                                 uint32_t domid,
+                                 uint32_t timeout, char *what,
+                                 char *path, char *state,
+                                 libxl__spawn_starting *spawning,
+                                 int (*check_callback)(libxl__gc *gc,
+                                                       uint32_t domid,
+                                                       const char *state,
+                                                       void *userdata),
+                                 void *check_callback_userdata);
+
+
+ /* low-level stuff, for synchronous subprocesses etc. */
+
+/*
+ * env should be passed using the following format,
+ *
+ * env[0]: name of env variable
+ * env[1]: value of env variable
+ * env[n]: ...
+ *
+ * So it efectively becomes something like:
+ * export env[n]=env[n+1]
+ * (where n%2 = 0)
+ *
+ * The last entry of the array always has to be NULL.
+ *
+ * stdinfd, stdoutfd, stderrfd will be dup2'd onto the corresponding
+ * fd in the child, if they are not -1.  The original copy of the
+ * descriptor will be closed in the child (unless it's 0, 1 or 2
+ * ie the source descriptor is itself stdin, stdout or stderr).
+ *
+ * Logs errors, never returns.
+ */
+_hidden  void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd,
+                          int stderrfd, const char *arg0, char *const args[],
+                          char *const env[]) __attribute__((noreturn));
+
+/* from xl_create */
+
+ /* on entry, libxl_domid_valid_guest(domid) must be false;
+  * on exit (even error exit), domid may be valid and refer to a domain */
+_hidden int libxl__domain_make(libxl__gc *gc,
+                               libxl_domain_config *d_config,
+                               libxl__domain_build_state *state,
+                               uint32_t *domid, bool soft_reset);
+
+_hidden int libxl__domain_build(libxl__gc *gc,
+                                libxl_domain_config *d_config,
+                                uint32_t domid,
+                                libxl__domain_build_state *state);
+
+/* for device model creation */
+_hidden const char *libxl__domain_device_model(libxl__gc *gc,
+                                        const libxl_domain_build_info *info);
+_hidden int libxl__need_xenpv_qemu(libxl__gc *gc,
+                                   libxl_domain_config *d_config);
+_hidden bool libxl__query_qemu_backend(libxl__gc *gc,
+                                       uint32_t domid,
+                                       uint32_t backend_id,
+                                       const char *type,
+                                       bool def);
+_hidden int libxl__dm_active(libxl__gc *gc, uint32_t domid);
+_hidden int libxl__dm_check_start(libxl__gc *gc,
+                                  libxl_domain_config *d_config,
+                                  uint32_t domid);
+
+/*
+ * This function will fix reserved device memory conflict
+ * according to user's configuration.
+ */
+_hidden int libxl__domain_device_construct_rdm(libxl__gc *gc,
+                                   libxl_domain_config *d_config,
+                                   uint64_t rdm_mem_guard,
+                                   struct xc_dom_image *dom);
+
+/*
+ * This function will cause the whole libxl process to hang
+ * if the device model does not respond.  It is deprecated.
+ *
+ * Instead of calling this function:
+ *  - Make your libxl entrypoint use the ao machinery
+ *  - Use libxl__ev_xswatch_register, and use the callback programming
+ *    style
+ */
+_hidden int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
+                                uint32_t domid, char *state,
+                                libxl__spawn_starting *spawning
+                                                    /* NULL allowed */,
+                                int (*check_callback)(libxl__gc *gc,
+                                                      uint32_t domid,
+                                                      const char *state,
+                                                      void *userdata),
+                                void *check_callback_userdata);
+
+_hidden const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *g_cfg);
+
+_hidden char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path);
+
+#define LIBXL__LOG_DEBUG   XTL_DEBUG
+#define LIBXL__LOG_VERBOSE XTL_VERBOSE
+#define LIBXL__LOG_INFO    XTL_INFO
+#define LIBXL__LOG_WARNING XTL_WARN
+#define LIBXL__LOG_ERROR   XTL_ERROR
+
+_hidden char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid);
+_hidden char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid);
+
+_hidden int libxl__enum_from_string(const libxl_enum_string_table *t,
+                                    const char *s, int *e) NN(2);
+
+_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
+
+_hidden yajl_gen_status libxl__string_gen_json(yajl_gen hand, const char *p);
+
+typedef yajl_gen_status (*libxl__gen_json_callback)(yajl_gen hand, void *);
+_hidden char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
+                                    libxl__gen_json_callback gen, void *p);
+
+_hidden void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool retore,
+                                 libxl_domain_build_info *info);
+
+/* Calls poll() again - useful to check whether a signaled condition
+ * is still true.  Cannot fail.  Returns currently-true revents. */
+_hidden short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events);
+
+_hidden char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid);
+
+struct libxl__xen_console_reader {
+    char *buffer;
+    unsigned int size;
+    unsigned int count;
+    unsigned int clear;
+    unsigned int incremental;
+    unsigned int index;
+};
+
+/* parse the string @s as a sequence of 6 colon separated bytes in to @mac */
+_hidden int libxl__parse_mac(const char *s, libxl_mac mac);
+/* compare mac address @a and @b. 0 if the same, -ve if a<b and +ve if a>b */
+_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b);
+/* return true if mac address is all zero (the default value) */
+_hidden int libxl__mac_is_default(libxl_mac *mac);
+/* init a recursive mutex */
+_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock);
+
+_hidden int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r);
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+/* from libxl_qmp */
+typedef struct libxl__qmp_handler libxl__qmp_handler;
+
+/* Initialise and connect to the QMP socket.
+ *   Return an handler or NULL if there is an error
+ */
+_hidden libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc,
+                                                  uint32_t domid);
+/* Resume QEMU. */
+_hidden int libxl__qmp_resume(libxl__gc *gc, int domid);
+/* Load current QEMU state from file. */
+_hidden int libxl__qmp_restore(libxl__gc *gc, int domid, const char *filename);
+/* Start NBD server */
+_hidden int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
+                                        const char *host, const char *port);
+/* Add a disk to NBD server */
+_hidden int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid,
+                                      const char *disk);
+/* Start replication */
+_hidden int libxl__qmp_start_replication(libxl__gc *gc, int domid,
+                                         bool primary);
+/* Get replication error that occurs when the vm is running */
+_hidden int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid);
+/* Do checkpoint */
+_hidden int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid);
+/* Stop replication */
+_hidden int libxl__qmp_stop_replication(libxl__gc *gc, int domid,
+                                        bool primary);
+/* Stop NBD server */
+_hidden int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid);
+/* Add or remove a child to/from quorum */
+_hidden int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid,
+                                         const char *parant,
+                                         const char *child, const char *node);
+/* run a hmp command in qmp mode */
+_hidden int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
+                           char **out);
+/* close and free the QMP handler */
+_hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
+/* remove the socket file, if the file has already been removed,
+ * nothing happen */
+_hidden void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid);
+
+/* `data' should contain a byte to send.
+ * When dealing with a non-blocking fd, it returns
+ *   ERROR_NOT_READY on EWOULDBLOCK
+ * logs on other failures. */
+int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
+                       const char data,
+                       int nfds, const int fds[], const char *what);
+
+/* Insists on receiving exactly nfds and datalen.  On failure, logs
+ * and leaves *fds untouched. */
+int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
+                       void *databuf, size_t datalen,
+                       int nfds, int fds[], const char *what);
+
+/* from libxl_json */
+#include <yajl/yajl_gen.h>
+
+_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
+_hidden yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str);
+
+typedef enum {
+    JSON_NULL    = (1 << 0),
+    JSON_BOOL    = (1 << 1),
+    JSON_INTEGER = (1 << 2),
+    JSON_DOUBLE  = (1 << 3),
+    /* number is store in string, it's too big to be a long long or a double */
+    JSON_NUMBER  = (1 << 4),
+    JSON_STRING  = (1 << 5),
+    JSON_MAP     = (1 << 6),
+    JSON_ARRAY   = (1 << 7),
+    JSON_ANY     = 255 /* this is a mask of all values above, adjust as needed */
+} libxl__json_node_type;
+
+struct libxl__json_object {
+    libxl__json_node_type type;
+    union {
+        bool b;
+        long long i;
+        double d;
+        char *string;
+        /* List of libxl__json_object */
+        flexarray_t *array;
+        /* List of libxl__json_map_node */
+        flexarray_t *map;
+    } u;
+    struct libxl__json_object *parent;
+};
+
+typedef int (*libxl__json_parse_callback)(libxl__gc *gc,
+                                          libxl__json_object *o,
+                                          void *p);
+_hidden int libxl__object_from_json(libxl_ctx *ctx, const char *type,
+                                    libxl__json_parse_callback parse,
+                                    void *p,
+                                    const char *s);
+
+typedef struct {
+    char *map_key;
+    libxl__json_object *obj;
+} libxl__json_map_node;
+
+static inline bool libxl__json_object_is_null(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_NULL;
+}
+static inline bool libxl__json_object_is_bool(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_BOOL;
+}
+static inline bool libxl__json_object_is_string(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_STRING;
+}
+static inline bool libxl__json_object_is_integer(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_INTEGER;
+}
+static inline bool libxl__json_object_is_double(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_DOUBLE;
+}
+static inline bool libxl__json_object_is_number(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_NUMBER;
+}
+static inline bool libxl__json_object_is_map(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_MAP;
+}
+static inline bool libxl__json_object_is_array(const libxl__json_object *o)
+{
+    return o != NULL && o->type == JSON_ARRAY;
+}
+
+/*
+ * `o` may be NULL for all libxl__json_object_get_* functions.
+ */
+
+static inline bool libxl__json_object_get_bool(const libxl__json_object *o)
+{
+    if (libxl__json_object_is_bool(o))
+        return o->u.b;
+    else
+        return false;
+}
+static inline
+const char *libxl__json_object_get_string(const libxl__json_object *o)
+{
+    if (libxl__json_object_is_string(o))
+        return o->u.string;
+    else
+        return NULL;
+}
+static inline
+const char *libxl__json_object_get_number(const libxl__json_object *o)
+{
+    if (libxl__json_object_is_number(o))
+        return o->u.string;
+    else
+        return NULL;
+}
+static inline
+flexarray_t *libxl__json_object_get_map(const libxl__json_object *o)
+{
+    if (libxl__json_object_is_map(o))
+        return o->u.map;
+    else
+        return NULL;
+}
+static inline
+flexarray_t *libxl__json_object_get_array(const libxl__json_object *o)
+{
+    if (libxl__json_object_is_array(o))
+        return o->u.array;
+    else
+        return NULL;
+}
+static inline long long libxl__json_object_get_integer(const libxl__json_object *o)
+{
+    if (libxl__json_object_is_integer(o))
+        return o->u.i;
+    else
+        return -1;
+}
+
+/*
+ * `o` may be NULL for the following libxl__json_*_get functions.
+ */
+
+_hidden libxl__json_object *libxl__json_array_get(const libxl__json_object *o,
+                                                  int i);
+_hidden
+libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
+                                               int i);
+_hidden const libxl__json_object *libxl__json_map_get(const char *key,
+                                          const libxl__json_object *o,
+                                          libxl__json_node_type expected_type);
+
+/*
+ * NOGC can be used with those json_object functions, but the
+ * libxl__json_object* will need to be freed with libxl__json_object_free.
+ */
+_hidden libxl__json_object *libxl__json_object_alloc(libxl__gc *gc_opt,
+                                                     libxl__json_node_type type);
+_hidden yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc_opt,
+                                                   yajl_gen hand,
+                                                   const libxl__json_object *param);
+_hidden void libxl__json_object_free(libxl__gc *gc_opt,
+                                     libxl__json_object *obj);
+
+_hidden libxl__json_object *libxl__json_parse(libxl__gc *gc_opt, const char *s);
+
+/* `args` may be NULL */
+_hidden char *libxl__json_object_to_json(libxl__gc *gc,
+                                         const libxl__json_object *args);
+/* Always return a valid string, but invalid json on error. */
+#define JSON(o) \
+    (libxl__json_object_to_json(gc, (o)) ? : "<invalid-json-object>")
+
+  /* Based on /local/domain/$domid/dm-version xenstore key
+   * default is qemu xen traditional */
+_hidden int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid);
+  /* Return the system-wide default device model */
+_hidden libxl_device_model_version libxl__default_device_model(libxl__gc *gc);
+
+static inline
+bool libxl__stubdomain_is_linux_running(libxl__gc *gc, uint32_t domid)
+{
+    /* same logic as in libxl__stubdomain_is_linux */
+    return libxl__device_model_version_running(gc, domid)
+        == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+static inline
+bool libxl__stubdomain_is_linux(libxl_domain_build_info *b_info)
+{
+    /* right now qemu-tranditional implies MiniOS stubdomain and qemu-xen
+     * implies Linux stubdomain */
+    return libxl_defbool_val(b_info->device_model_stubdomain) &&
+        b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+#define DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, fmt, _a...)              \
+    libxl__sprintf(gc, "/local/domain/%u/device-model/%u" fmt, dm_domid,   \
+                   domid, ##_a)
+
+/*
+ * Calling context and GC for event-generating functions:
+ *
+ * These are for use by parts of libxl which directly or indirectly
+ * call libxl__event_occurred.  These contain a gc but also a list of
+ * deferred events.
+ *
+ * You should never need to initialise an egc unless you are part of
+ * the event machinery itself.  Otherwise you will always be given an
+ * egc if you need one.  Even functions which generate specific kinds
+ * of events don't need to - rather, they will be passed an egc into
+ * their own callback function and should just use the one they're
+ * given.
+ *
+ * Functions using LIBXL_INIT_EGC may *not* generally be called from
+ * within libxl, because libxl__egc_cleanup may call back into the
+ * application.  This should be enforced by declaring all such
+ * functions in libxl.h or libxl_event.h with
+ * LIBXL_EXTERNAL_CALLERS_ONLY.  You should in any case not find it
+ * necessary to call egc-creators from within libxl.
+ *
+ * The callbacks must all take place with the ctx unlocked because
+ * the application is entitled to reenter libxl from them.  This
+ * would be bad not because the lock is not recursive (it is) but
+ * because the application might make blocking libxl calls which
+ * would hold the lock unreasonably long.
+ *
+ * For the same reason libxl__egc_cleanup (or EGC_FREE) must be called
+ * with the ctx *unlocked*.  So the right pattern has the EGC_...
+ * macro calls on the outside of the CTX_... ones.
+ */
+
+/* useful for all functions which take an egc: */
+
+#define EGC_GC                                  \
+    libxl__gc *const gc __attribute__((unused)) = &egc->gc
+
+/* egc initialisation and destruction: */
+
+#define LIBXL_INIT_EGC(egc,ctx) do{                     \
+        LIBXL_INIT_GC((egc).gc,ctx);                    \
+        LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \
+        LIBXL_TAILQ_INIT(&(egc).aos_for_callback);      \
+        LIBXL_TAILQ_INIT(&(egc).aops_for_callback);     \
+        LIBXL_STAILQ_INIT(&(egc).ev_immediates);        \
+    } while(0)
+
+_hidden void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc);
+  /* Passes the baton for added osevents.  See comment for
+   * osevents_added in struct libxl__poller. */
+_hidden void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc);
+  /* Frees memory allocated within this egc's gc, and and report all
+   * occurred events via callback, if applicable.  May reenter the
+   * application; see restrictions above.  The ctx must be UNLOCKED. */
+
+/* convenience macros: */
+
+#define EGC_INIT(ctx)                       \
+    libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx);      \
+    EGC_GC
+
+#define CTX_UNLOCK_EGC_FREE  do{                        \
+        libxl__egc_ao_cleanup_1_baton(&egc->gc);        \
+        CTX_UNLOCK;                                     \
+        libxl__egc_cleanup_2_ul_cb_gc(egc);             \
+    }while(0)
+
+
+/*
+ * Machinery for asynchronous operations ("ao")
+ *
+ * All "slow" functions (see below for the exact definition) need to
+ * use the asynchronous operation ("ao") machinery.  The function
+ * should take a parameter const libxl_asyncop_how *ao_how and must
+ * start with a call to AO_CREATE or equivalent.  These functions MAY
+ * NOT be called from inside libxl (regardless of what is passed for
+ * ao_how), because they can cause reentrancy hazards due to
+ * callbacks.
+ *
+ * For the same reason functions taking an ao_how may make themselves
+ * an egc with EGC_INIT (and they will generally want to, to be able
+ * to immediately complete an ao during its setup).
+ *
+ *
+ * "Slow" functions includes any that might block on a guest or an
+ * external script.  More broadly, it includes any operations which
+ * are sufficiently slow that an application might reasonably want to
+ * initiate them, and then carry on doing something else, while the
+ * operation completes.  That is, a "fast" function must be fast
+ * enough that we do not mind blocking all other management operations
+ * on the same host while it completes.
+ *
+ * There are certain primitive functions which make a libxl operation
+ * necessarily "slow" for API reasons.  These are:
+ *  - awaiting xenstore watches (although read-modify-write xenstore
+ *    transactions are OK for fast functions)
+ *  - spawning subprocesses
+ *  - anything with a timeout
+ *
+ *
+ * Lifecycle of an ao:
+ *
+ * - Created by libxl__ao_create (or the AO_CREATE convenience macro).
+ *
+ * - After creation, can be used by code which implements
+ *   the operation as follows:
+ *      - the ao's gc, for allocating memory for the lifetime
+ *        of the operation (possibly with the help of the AO_GC
+ *        macro to introduce the gc into scope)
+ *      - the ao itself may be passed about to sub-functions
+ *        so that they can stash it away etc.
+ *      - in particular, the ao pointer must be stashed in some
+ *        per-operation structure which is also passed as a user
+ *        pointer to the internal event generation request routines
+ *        libxl__evgen_FOO, so that at some point a CALLBACK will be
+ *        made when the operation is complete.
+ *      - if the operation provides progress reports, the aop_how(s)
+ *        must be copied into the per-operation structure using
+ *        libxl__ao_progress_gethow.
+ *
+ * - If the initiation is unsuccessful, the initiating function must
+ *   call libxl__ao_create_fail before unlocking and returning whatever
+ *   error code is appropriate (AO_CREATE_FAIL macro).
+ *
+ * If initiation is successful:
+ *
+ * - The initiating function must run libxl__ao_inprogress right
+ *   before unlocking and returning, and return whatever it returns.
+ *   This is best achieved with the AO_INPROGRESS macro.
+ *
+ * - If the operation supports progress reports, it may generate
+ *   suitable events with NEW_EVENT and report them with
+ *   libxl__ao_progress_report (with the ctx locked).
+ *
+ * - Eventually, some callback function, whose callback has been
+ *   requested directly or indirectly, should call libxl__ao_complete
+ *   (with the ctx locked, as it will generally already be in any
+ *   event callback function).  This must happen exactly once for each
+ *   ao, as the last that happens with that ao.
+ *
+ * - However, it is permissible for the initiating function to call
+ *   libxl__ao_inprogress and/or libxl__ao_complete (directly or
+ *   indirectly), before it uses AO_INPROGRESS to return.  (The ao
+ *   infrastructure will arrange to defer destruction of the ao, etc.,
+ *   until the proper time.)  An initiating function should do this
+ *   if it takes a codepath which completes synchronously.
+ *
+ * - Conversely it is forbidden to call libxl__ao_complete in the
+ *   initiating function _after_ AO_INPROGRESS, because
+ *   libxl__ao_complete requires the ctx to be locked.
+ *
+ * - Note that during callback functions, two gcs are available:
+ *    - The one in egc, whose lifetime is only this callback
+ *    - The one in ao, whose lifetime is the asynchronous operation
+ *   Usually a callback function should use CONTAINER_OF to obtain its
+ *   own state structure, containing a pointer to the ao.  It should
+ *   then obtain the ao and use the ao's gc; this is most easily done
+ *   using the convenience macro STATE_AO_GC.
+ */
+
+#define AO_CREATE(ctx, domid, ao_how)                           \
+    libxl__ctx_lock(ctx);                                       \
+    libxl__ao *ao = libxl__ao_create(ctx, domid, ao_how,        \
+                               __FILE__, __LINE__, __func__);   \
+    if (!ao) { libxl__ctx_unlock(ctx); return ERROR_NOMEM; }    \
+    libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx);              \
+    AO_GC;
+
+#define AO_INPROGRESS ({                                        \
+        libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc);          \
+        /* __ao_inprogress will do egc..1_baton if needed */   \
+        CTX_UNLOCK;                                             \
+        libxl__egc_cleanup_2_ul_cb_gc(egc);                     \
+        CTX_LOCK;                                               \
+        int ao__rc = libxl__ao_inprogress(ao,                   \
+                               __FILE__, __LINE__, __func__);   \
+        libxl__ctx_unlock(ao__ctx); /* gc is now invalid */     \
+        (ao__rc);                                               \
+   })
+
+#define AO_CREATE_FAIL(rc) ({                                   \
+        libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc);          \
+        assert(rc);                                             \
+        libxl__ao_create_fail(ao);                              \
+        libxl__egc_ao_cleanup_1_baton(&egc->gc);                \
+        libxl__ctx_unlock(ao__ctx); /* gc is now invalid */     \
+        libxl__egc_cleanup_2_ul_cb_gc(egc);                     \
+        (rc);                                                   \
+    })
+
+
+/*
+ * Given, in scope,
+ *   libxl__ao *ao;
+ * produces, in scope,
+ *   libxl__gc *gc;
+ */
+#define AO_GC                                   \
+    libxl__gc *const gc __attribute__((unused)) = &ao->gc
+
+/*
+ * void STATE_AO_GC(libxl__ao *ao_spec);
+ * // Produces, in scope:
+ *   libxl__ao *ao;  // set from ao_spec
+ *   libxl__gc *gc;
+ */
+#define STATE_AO_GC(op_ao)                      \
+    libxl__ao *const ao = (op_ao);              \
+    libxl__gc *const gc __attribute__((unused)) = libxl__ao_inprogress_gc(ao)
+
+
+/* All of these MUST be called with the ctx locked.
+ * libxl__ao_inprogress MUST be called with the ctx locked exactly once. */
+_hidden libxl__ao *libxl__ao_create(libxl_ctx*, uint32_t domid,
+                                    const libxl_asyncop_how*,
+       const char *file, int line, const char *func);
+_hidden int libxl__ao_inprogress(libxl__ao *ao,
+       const char *file, int line, const char *func); /* temporarily unlocks */
+_hidden void libxl__ao_create_fail(libxl__ao *ao);
+_hidden void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc);
+_hidden libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao);
+
+/* Can be called at any time.  Use is essential for any aop user. */
+_hidden void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
+                                       const libxl_asyncprogress_how *from_app);
+
+/* Must be called with the ctx locked.  Will fill in ev->for_user,
+ * so caller need not do that. */
+_hidden void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
+   const libxl_asyncprogress_how *how, libxl_event *ev /* consumed */);
+
+/* For use by ao machinery ONLY */
+_hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao);
+_hidden void libxl__ao_complete_check_progress_reports(libxl__egc*, libxl__ao*);
+
+
+/*
+ * Short-lived sub-ao, aka "nested ao".
+ *
+ * Some asynchronous operations are very long-running.  Generally,
+ * since an ao has a gc, any allocations made in that ao will live
+ * until the ao is completed.  When this is not desirable, these
+ * functions may be used to manage a "sub-ao".
+ *
+ * The returned sub-ao is suitable for passing to gc-related functions
+ * and macros such as libxl__ao_inprogress_gc, AO_GC, and STATE_AO_GC.
+ *
+ * It MUST NOT be used with AO_INPROGRESS, AO_CREATE_FAIL,
+ * libxl__ao_complete, libxl__ao_progress_report, and so on.
+ *
+ * The caller must ensure that all of the sub-ao's are freed before
+ * the parent is.  Multiple levels of nesting are OK (although
+ * hopefully they won't be necessary).
+ */
+
+_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent); /* cannot fail */
+_hidden void libxl__nested_ao_free(libxl__ao *child);
+
+
+/*
+ * File descriptors and CLOEXEC
+ */
+
+/*
+ * For libxl functions which create file descriptors, at least one
+ * of the following must be true:
+ *  (a) libxl does not care if copies of this open-file are inherited
+ *      by random children and might remain open indefinitely
+ *  (b) libxl must take extra care for the fd (the actual descriptor,
+ *      not the open-file) as below.  We call this a "carefd".
+ *
+ * The rules for opening a carefd are:
+ *  (i)   Before bringing any carefds into existence,
+ *        libxl code must call libxl__carefd_begin.
+ *  (ii)  Then for each carefd brought into existence,
+ *        libxl code must call libxl__carefd_record
+ *        and remember the libxl__carefd_record*.
+ *  (iii) Then it must call libxl__carefd_unlock.
+ *  (iv)  When in a child process the fd is to be passed across
+ *        exec by libxl, the libxl code must unset FD_CLOEXEC
+ *        on the fd eg by using libxl_fd_set_cloexec.
+ *  (v)   Later, when the fd is to be closed in the same process,
+ *        libxl code must not call close.  Instead, it must call
+ *        libxl__carefd_close.
+ * Steps (ii) and (iii) can be combined by calling the convenience
+ * function libxl__carefd_opened.
+ */
+/* libxl__carefd_begin and _unlock (or _opened) must be called always
+ * in pairs.  They may be called with the CTX lock held.  In between
+ * _begin and _unlock, the following are prohibited:
+ *   - anything which might block
+ *   - any callbacks to the application
+ *   - nested calls to libxl__carefd_begin
+ *   - fork (libxl__fork)
+ * In general nothing should be done before _unlock that could be done
+ * afterwards.
+ */
+
+_hidden void libxl__carefd_begin(void);
+_hidden void libxl__carefd_unlock(void);
+
+/* fd may be -1, in which case this returns a dummy libxl__fd_record
+ * on which it _carefd_close is a no-op.  Cannot fail. */
+_hidden libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd);
+
+/* Combines _record and _unlock in a single call.  If fd==-1,
+ * still does the unlock, but returns 0. */
+_hidden libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd);
+
+/* Works just like close(2).  You may pass NULL, in which case it's
+ * a successful no-op. */
+_hidden int libxl__carefd_close(libxl__carefd*);
+
+/* You may pass NULL in which case the answer is -1. */
+_hidden int libxl__carefd_fd(const libxl__carefd*);
+
+/* common paths */
+_hidden const char *libxl__private_bindir_path(void);
+_hidden const char *libxl__xenfirmwaredir_path(void);
+_hidden const char *libxl__xen_config_dir_path(void);
+_hidden const char *libxl__xen_script_dir_path(void);
+_hidden const char *libxl__lock_dir_path(void);
+_hidden const char *libxl__run_dir_path(void);
+_hidden const char *libxl__seabios_path(void);
+_hidden const char *libxl__ovmf_path(void);
+_hidden const char *libxl__ipxe_path(void);
+
+/*----- subprocess execution with timeout -----*/
+
+typedef struct libxl__async_exec_state libxl__async_exec_state;
+
+typedef void libxl__async_exec_callback(libxl__egc *egc,
+                        libxl__async_exec_state *aes, int rc, int status);
+/*
+ * Meaning of status and rc:
+ *  rc==0, status==0    all went well
+ *  rc==0, status!=0    everything OK except child exited nonzero (logged)
+ *  rc!=0               something else went wrong (status is real
+ *                       exit status; maybe reflecting SIGKILL, and
+ *                       therefore not very interesting, if aes code
+ *                       killed the child).  Logged unless ABORTED.
+ */
+
+struct libxl__async_exec_state {
+    /* caller must fill these in */
+    libxl__ao *ao;
+    const char *what; /* for error msgs, what we're executing */
+    int timeout_ms;
+    libxl__async_exec_callback *callback;
+    /* caller must fill in; as for libxl__exec */
+    int stdfds[3];
+    char **args; /* execution arguments */
+    char **env; /* execution environment */
+
+    /* private */
+    libxl__ev_time time;
+    libxl__ev_child child;
+    int rc;
+};
+
+void libxl__async_exec_init(libxl__async_exec_state *aes);
+int libxl__async_exec_start(libxl__async_exec_state *aes);
+bool libxl__async_exec_inuse(const libxl__async_exec_state *aes);
+
+_hidden void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what);
+/* kill SIGHUP a pid stored in xenstore */
+_hidden int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
+                                const char *what);
+
+/*----- device addition/removal -----*/
+
+typedef void libxl__device_callback(libxl__egc*, libxl__ao_device*);
+
+/* This functions sets the necessary libxl__ao_device struct values to use
+ * safely inside functions. It marks the operation as "active"
+ * since we need to be sure that all device status structs are set
+ * to active before start queueing events, or we might call
+ * ao_complete before all devices had finished
+ *
+ * libxl__initiate_device_{remove/addition} should not be called without
+ * calling libxl__prepare_ao_device first, since it initializes the private
+ * fields of the struct libxl__ao_device to what this functions expect.
+ *
+ * Once _prepare has been called on a libxl__ao_device, it is safe to just
+ * discard this struct, there's no need to call any destroy function.
+ * _prepare can also be called multiple times with the same libxl__ao_device.
+ *
+ * But if any of the fields `backend_ds', `timeout', `xswait', `qmp' is
+ * used by a caller of _prepare, the caller will have to arrange to clean
+ * or dispose of them.
+ */
+_hidden void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev);
+
+/* generic callback for devices that only need to set ao_complete */
+_hidden void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev);
+
+typedef struct {
+    enum {
+        LIBXL__FORCE_AUTO, /* Re-execute with FORCE_ON if op times out */
+        LIBXL__FORCE_ON,
+        LIBXL__FORCE_OFF,
+    } flag;
+} libxl__force;
+
+struct libxl__ao_device {
+    /* filled in by user */
+    libxl__ao *ao;
+    libxl__device_action action;
+    libxl__device *dev;
+    libxl__force force;
+    libxl__device_callback *callback;
+    /* return value, zeroed by user on entry, is valid on callback */
+    int rc;
+    /* private for multidev */
+    int active;
+    libxl__multidev *multidev; /* reference to the containing multidev */
+    /* private for add/remove implementation */
+    libxl__ev_devstate backend_ds;
+    /* Bodge for Qemu devices */
+    libxl__ev_time timeout;
+    /* xenstore watch for backend path of driver domains */
+    libxl__xswait_state xswait;
+    int num_exec;
+    /* for calling hotplug scripts */
+    libxl__async_exec_state aes;
+    /* If we need to update JSON config */
+    bool update_json;
+    /* for asynchronous execution of synchronous-only syscalls etc. */
+    libxl__ev_child child;
+    libxl__ev_qmp qmp;
+    /* 'device_config' can be used to to pass to callbacks a pointer of one
+     * of the type 'libxl_device_$type' corresponding to the device been
+     * hotplug. 'device_type' should have the corresponding
+     * 'libxl__$type_devtype'. */
+    void *device_config;
+    const libxl__device_type *device_type;
+};
+
+/*
+ * Multiple devices "multidev" handling.
+ *
+ * Firstly, you should
+ *    libxl__multidev_begin
+ *    multidev->callback = ...
+ * Then zero or more times
+ *    libxl__multidev_prepare
+ *    libxl__initiate_device_{remove/addition}
+ *       (or some other thing which will eventually call
+ *        aodev->callback or libxl__multidev_one_callback)
+ * Finally, once
+ *    libxl__multidev_prepared
+ * which will result (perhaps reentrantly) in one call to
+ * multidev->callback().
+ */
+
+/* Starts preparing to add/remove a bunch of devices. */
+_hidden void libxl__multidev_begin(libxl__ao *ao, libxl__multidev*);
+
+/* Prepares to add/remove one of many devices.
+ * Calls libxl__prepare_ao_device on libxl__ao_device argument provided and
+ * also sets the aodev->callback (to libxl__multidev_one_callback)
+ * The user should not mess with aodev->callback.
+ */
+_hidden void libxl__multidev_prepare_with_aodev(libxl__multidev*,
+                                                libxl__ao_device*);
+
+/* A wrapper function around libxl__multidev_prepare_with_aodev.
+ * Allocates a libxl__ao_device and prepares it for addition/removal.
+ * Returns the newly allocated libxl__ao_dev.
+ */
+_hidden libxl__ao_device *libxl__multidev_prepare(libxl__multidev*);
+
+/* Indicates to multidev that this one device has been processed.
+ * Normally the multidev user does not need to touch this function, as
+ * multidev_prepare will name it in aodev->callback.  However, if you
+ * want to do something more complicated you can set aodev->callback
+ * yourself to something else, so long as you eventually call
+ * libxl__multidev_one_callback.
+ */
+_hidden void libxl__multidev_one_callback(libxl__egc *egc,
+                                          libxl__ao_device *aodev);
+
+/* Notifies the multidev machinery that we have now finished preparing
+ * and initiating devices.  multidev->callback may then be called as
+ * soon as there are no prepared but not completed operations
+ * outstanding, perhaps reentrantly.  If rc!=0 (error should have been
+ * logged) multidev->callback will get a non-zero rc.
+ * callback may be set by the user at any point before prepared. */
+_hidden void libxl__multidev_prepared(libxl__egc*, libxl__multidev*, int rc);
+
+typedef void libxl__devices_callback(libxl__egc*, libxl__multidev*, int rc);
+struct libxl__multidev {
+    /* set by user: */
+    libxl__devices_callback *callback;
+    /* for private use by libxl__...ao_devices... machinery: */
+    libxl__ao *ao;
+    libxl__ao_device **array;
+    int used, allocd;
+    libxl__ao_device *preparation;
+};
+
+/*
+ * Algorithm for handling device removal (including domain
+ * destruction).  This is somewhat subtle because we may already have
+ * killed the domain and caused the death of qemu.
+ *
+ * In current versions of qemu there is no mechanism for ensuring that
+ * the resources used by its devices (both emulated and any PV devices
+ * provided by qemu) are freed (eg, fds closed) before it shuts down,
+ * and no confirmation from a terminating qemu back to the toolstack.
+ *
+ * This will need to be fixed in future Xen versions. In the meantime
+ * (Xen 4.2) we implement a bodge.
+ *
+ *      WE WANT TO UNPLUG         WE WANT TO SHUT DOWN OR DESTROY
+ *                    |                           |
+ *                    |             LIBXL SENDS SIGHUP TO QEMU
+ *                    |      .....................|........................
+ *                    |      : XEN 4.3+ PLANNED   |                       :
+ *                    |      :      QEMU TEARS DOWN ALL DEVICES           :
+ *                    |      :      FREES RESOURCES (closing fds)         :
+ *                    |      :      SETS PV BACKENDS TO STATE 5,          :
+ *                    |      :       waits for PV frontends to shut down  :
+ *                    |      :       SETS PV BACKENDS TO STATE 6          :
+ *                    |      :                    |                       :
+ *                    |      :      QEMU NOTIFIES TOOLSTACK (via          :
+ *                    |      :       xenstore) that it is exiting         :
+ *                    |      :      QEMU EXITS (parent may be init)       :
+ *                    |      :                    |                       :
+ *                    |      :        TOOLSTACK WAITS FOR QEMU            :
+ *                    |      :        notices qemu has finished           :
+ *                    |      :....................|.......................:
+ *                    |      .--------------------'
+ *                    V      V
+ *                  for each device
+ *                 we want to unplug/remove
+ *       ..................|...........................................
+ *       :                 V                       XEN 4.2 RACY BODGE :
+ *       :      device is provided by    qemu                         :
+ *       :            |            `-----------.                      :
+ *       :   something|                        V                      :
+ *       :    else, eg|             domain (that is domain for which  :
+ *       :     blkback|              this PV device is the backend,   :
+ *       :            |              which might be the stub dm)      :
+ *       :            |                is still alive?                :
+ *       :            |                  |        |                   :
+ *       :            |                  |alive   |dead               :
+ *       :            |<-----------------'        |                   :
+ *       :            |    hopefully qemu is      |                   :
+ *       :            |       still running       |                   :
+ *       :............|.................          |                   :
+ *             ,----->|                :     we may be racing         :
+ *             |    backend state?     :      with qemu's death       :
+ *             ^      |         |      :          |                   :
+ *     xenstore|      |other    |6     :      WAIT 2.0s               :
+ *     conflict|      |         |      :       TIMEOUT                :
+ *             |   WRITE B.E.   |      :          |                   :
+ *             |    STATE:=5    |      :     hopefully qemu has       :
+ *             `---'  |         |      :      gone by now and         :
+ *                    |ok       |      :      freed its resources     :
+ *                    |         |      :          |                   :
+ *              WAIT FOR        |      :     SET B.E.                 :
+ *              STATE==6        |      :      STATE:=6                :
+ *              /     |         |      :..........|...................:
+ *      timeout/    ok|         |                 |
+ *            /       |         |                 |
+ *           |    RUN HOTPLUG <-'<----------------'
+ *           |      SCRIPT
+ *           |        |
+ *           `---> NUKE
+ *                  BACKEND
+ *                    |
+ *                   DONE.
+ */
+
+/*
+ * As of Xen 4.5 we maintain various information, including hotplug
+ * device information, in JSON files, so that we can use this JSON
+ * file as a template to reconstruct domain configuration.
+ *
+ * In essense there are now two views of device state, one is the
+ * primary config (xenstore or QEMU), the other is JSON file.
+ *
+ * Here we maintain one invariant: every device in the primary config
+ * must have an entry in JSON file.
+ *
+ * All device hotplug routines should comply to following pattern:
+ *   lock json config (json_lock)
+ *       read json config
+ *       update in-memory json config with new entry, replacing
+ *          any stale entry
+ *       for loop -- xs transaction
+ *           open xs transaction
+ *           check device existence, bail if it exists
+ *           write in-memory json config to disk
+ *           commit xs transaction
+ *       end for loop
+ *   unlock json config
+ *
+ * Or in case QEMU is the primary config, this pattern can be use:
+ *   qmp_lock (libxl__ev_devlock_init)
+ *      lock json config (json_lock)
+ *          read json config
+ *          update in-memory json config with new entry, replacing
+ *             any stale entry
+ *      unlock json config
+ *      apply new config to primary config
+ *      lock json config (json_lock)
+ *          read json config
+ *          update in-memory json config with new entry, replacing
+ *             any stale entry
+ *          write in-memory json config to disk
+ *      unlock json config
+ *   unlock qmp_lock
+ *   (CTX_LOCK can be acquired and released several time while holding the
+ *    qmp_lock)
+ *
+ * Device removal routines are not touched.
+ *
+ * Here is the proof that we always maintain that invariant and we
+ * don't leak files during interaction of hotplug thread and other
+ * threads / processes.
+ *
+ * # Safe against parallel add
+ *
+ * When another thread / process tries to add same device, it's
+ * blocked by json_lock. The loser of two threads will bail at
+ * existence check, so that we don't overwrite anything.
+ *
+ * # Safe against domain destruction
+ *
+ * If the thread / process trying to destroy domain loses the race, it's
+ * blocked by json_lock. If the hotplug thread is loser, it bails at
+ * acquiring lock because lock acquisition function checks existence of
+ * the domain.
+ *
+ * # Safe against parallel removal
+ *
+ * When another thread / process tries to remove a device, it's _NOT_
+ * blocked by json_lock, but xenstore transaction can help maintain
+ * invariant. The removal threads either a) sees that device in
+ * xenstore, b) doesn't see that device in xenstore.
+ *
+ * In a), it sees that device in xenstore. At that point hotplug is
+ * already finished (both JSON and xenstore changes committed). So that
+ * device can be safely removed. JSON entry is left untouched and
+ * becomes stale, but this is a valid state -- next time when a
+ * device with same identifier gets added, the stale entry gets
+ * overwritten.
+ *
+ * In b), it doesn't see that device in xenstore, but it will commence
+ * anyway. Eventually a forcibly removal is initiated, which will forcely
+ * remove xenstore entry.
+ *
+ * If hotplug threads creates xenstore entry (therefore JSON entry as
+ * well) before force removal, that xenstore entry is removed. We're
+ * left with JSON stale entry but not xenstore entry, which is a valid
+ * state.
+ *
+ * If hotplug thread has not created xenstore entry when the removal
+ * is committed, we're obviously safe. Hotplug thread will add in
+ * xenstore entry afterwards. We have both JSON and xenstore entry,
+ * it's a valid state.
+ */
+
+/* Waits for the passed device to reach state XenbusStateInitWait.
+ * This is not really useful by itself, but is important when executing
+ * hotplug scripts, since we need to be sure the device is in the correct
+ * state before executing them.
+ *
+ * Once finished, aodev->callback will be executed.
+ */
+_hidden void libxl__wait_device_connection(libxl__egc*,
+                                           libxl__ao_device *aodev);
+
+/* Arranges that dev will be removed to the guest, and the
+ * hotplug scripts will be executed (if necessary). When
+ * this is done (or an error happens), the callback in
+ * aodev->callback will be called.
+ *
+ * The libxl__ao_device passed to this function should be
+ * prepared using libxl__prepare_ao_device prior to calling
+ * this function.
+ *
+ * Once finished, aodev->callback will be executed.
+ */
+_hidden void libxl__initiate_device_generic_remove(libxl__egc *egc,
+                                                   libxl__ao_device *aodev);
+
+_hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
+                                                   libxl__ao_device *aodev);
+
+/*
+ * libxl__get_hotplug_script_info returns the args and env that should
+ * be passed to the hotplug script for the requested device.
+ *
+ * Since a device might not need to execute any hotplug script, this function
+ * can return the following values:
+ * < 0: Error
+ * 0: No need to execute hotplug script
+ * 1: Execute hotplug script
+ *
+ * The last parameter, "num_exec" refeers to the number of times hotplug
+ * scripts have been called for this device.
+ *
+ * The main body of libxl will, for each device, keep calling
+ * libxl__get_hotplug_script_info, with incrementing values of
+ * num_exec, and executing the resulting script accordingly,
+ * until libxl__get_hotplug_script_info returns<=0.
+ */
+_hidden int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+                                           char ***args, char ***env,
+                                           libxl__device_action action,
+                                           int num_exec);
+
+/*----- local disk attach: attach a disk locally to run the bootloader -----*/
+
+typedef struct libxl__disk_local_state libxl__disk_local_state;
+typedef void libxl__disk_local_state_callback(libxl__egc*,
+                                              libxl__disk_local_state*,
+                                              int rc);
+
+/* A libxl__disk_local_state may be in the following states:
+ * Undefined, Idle, Attaching, Attached, Detaching.
+ */
+struct libxl__disk_local_state {
+    /* filled by the user */
+    libxl__ao *ao;
+    const libxl_device_disk *in_disk;
+    libxl_device_disk disk;
+    const char *blkdev_start;
+    libxl__disk_local_state_callback *callback;
+    /* filled by libxl__device_disk_local_initiate_attach */
+    char *diskpath;
+    /* private for implementation of local detach */
+    libxl__ao_device aodev;
+    int rc;
+};
+
+/*
+ * Prepares a dls for use.
+ * State Undefined -> Idle
+ */
+static inline void libxl__device_disk_local_init(libxl__disk_local_state *dls)
+{
+    dls->rc = 0;
+}
+
+/*
+ * See if we can find a way to access a disk locally
+ */
+_hidden char * libxl__device_disk_find_local_path(libxl__gc *gc,
+                                                  libxl_domid guest_domid,
+                                                  const libxl_device_disk *disk,
+                                                  bool qdisk_direct);
+
+
+/* Make a disk available in this (the control) domain. Always calls
+ * dls->callback when finished.
+ * State Idle -> Attaching
+ *
+ * The state of dls on entry to the callback depends on the value
+ * of rc passed to the callback:
+ *     rc == 0: Attached if rc == 0
+ *     rc != 0: Idle
+ */
+_hidden void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
+                                                libxl__disk_local_state *dls);
+
+/* Disconnects a disk device form the control domain. If the passed
+ * dls is not attached (or has already been detached),
+ * libxl__device_disk_local_initiate_detach will just call the callback
+ * directly.
+ * State Idle/Attached -> Detaching
+ *
+ * The state of dls on entry to the callback is Idle.
+ */
+_hidden void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
+                                                libxl__disk_local_state *dls);
+
+/*----- datacopier: copies data from one fd to another -----*/
+
+typedef struct libxl__datacopier_state libxl__datacopier_state;
+typedef struct libxl__datacopier_buf libxl__datacopier_buf;
+
+/* onwrite==1 means problem happened when writing
+ *     rc==FAIL    errnoval >0    we had a write error, logged
+ * onwrite==0 means problem happened when reading
+ *     rc==0       errnoval==0    we got eof and all data was written
+ *     rc==FAIL    errnoval >0    we had a read error, logged
+ * onwrite==-1 means some other internal problem
+ *     rc==FAIL    errnoval==EIO  some other internal failure, logged
+ *     rc==ABORTED errnoval==0    abort requested, not logged
+ * If we get POLLHUP, we call callback_pollhup with
+ *     rc==FAIL    errnoval==-1   POLLHUP signalled
+ * or if callback_pollhup==0 this is treated as eof (if POLLIN|POLLHUP
+ * on the reading fd) or an internal failure (otherwise), as above.
+ * In all cases copier is killed before calling this callback */
+typedef void libxl__datacopier_callback(libxl__egc *egc,
+     libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
+
+struct libxl__datacopier_buf {
+    /* private to datacopier */
+    LIBXL_TAILQ_ENTRY(libxl__datacopier_buf) entry;
+    int used;
+    char buf[1000];
+};
+
+struct libxl__datacopier_state {
+    /* caller must fill these in, and they must all remain valid */
+    libxl__ao *ao;
+    int readfd, writefd;
+    ssize_t maxsz;
+    ssize_t bytes_to_read; /* set to -1 to read until EOF */
+    const char *copywhat, *readwhat, *writewhat; /* for error msgs */
+    FILE *log; /* gets a copy of everything */
+    libxl__datacopier_callback *callback;
+    libxl__datacopier_callback *callback_pollhup;
+    void *readbuf; /* Set this to read data into it without writing to an
+                      fd. The buffer should be at least as large as the
+                      bytes_to_read parameter, which should not be -1. */
+    /* remaining fields are private to datacopier */
+    libxl__ao_abortable abrt;
+    libxl__ev_fd toread, towrite;
+    ssize_t used;
+    LIBXL_TAILQ_HEAD(libxl__datacopier_bufs, libxl__datacopier_buf) bufs;
+};
+
+_hidden void libxl__datacopier_init(libxl__datacopier_state *dc);
+_hidden void libxl__datacopier_kill(libxl__datacopier_state *dc);
+_hidden int libxl__datacopier_start(libxl__datacopier_state *dc);
+
+/* Inserts literal data into the output stream.  The data is copied.
+ * May safely be used only immediately after libxl__datacopier_start
+ * (before the ctx is unlocked).  But may be called multiple times.
+ * NB exceeding maxsz will fail an assertion! */
+_hidden void libxl__datacopier_prefixdata(libxl__egc*, libxl__datacopier_state*,
+                                          const void *data, size_t len);
+
+/*----- Save/restore helper (used by creation and suspend) -----*/
+
+typedef struct libxl__srm_save_callbacks {
+    libxl__srm_save_autogen_callbacks a;
+} libxl__srm_save_callbacks;
+
+typedef struct libxl__srm_restore_callbacks {
+    libxl__srm_restore_autogen_callbacks a;
+} libxl__srm_restore_callbacks;
+
+/* a pointer to this struct is also passed as "user" to the
+ * save callout helper callback functions */
+typedef struct libxl__save_helper_state {
+    /* public, caller of run_helper initialises */
+    libxl__ao *ao;
+    uint32_t domid;
+    union {
+        libxl__srm_save_callbacks save;
+        libxl__srm_restore_callbacks restore;
+    } callbacks;
+    int (*recv_callback)(const unsigned char *msg, uint32_t len, void *user);
+    void (*completion_callback)(libxl__egc *egc, void *caller_state,
+                                int rc, int retval, int errnoval);
+    void *caller_state;
+    int need_results; /* set to 0 or 1 by caller of run_helper;
+                       * if set to 1 then the ultimate caller's
+                       * results function must set it to 0 */
+    /* private */
+    int rc;
+    int completed; /* retval/errnoval valid iff completed */
+    int retval, errnoval; /* from xc_domain_save / xc_domain_restore */
+    libxl__ao_abortable abrt;
+    libxl__carefd *pipes[2]; /* 0 = helper's stdin, 1 = helper's stdout */
+    libxl__ev_fd readable;
+    libxl__ev_child child;
+    const char *stdin_what, *stdout_what;
+
+    libxl__egc *egc; /* valid only for duration of each event callback;
+                      * is here in this struct for the benefit of the
+                      * marshalling and xc callback functions */
+} libxl__save_helper_state;
+
+/*----- checkpoint device related state structure -----*/
+/*
+ * The abstract checkpoint device layer exposes a common
+ * set of API to [external] libxl for manipulating devices attached to
+ * a guest protected by Remus/COLO. The device layer also exposes a set of
+ * [internal] interfaces that every device type must implement.
+ *
+ * The following API are exposed to libxl:
+ *
+ * One-time configuration operations:
+ *  +libxl__checkpoint_devices_setup
+ *    > Enable output buffering for NICs, setup disk replication, etc.
+ *  +libxl__checkpoint_devices_teardown
+ *    > Disable output buffering and disk replication; teardown any
+ *       associated external setups like qdiscs for NICs.
+ *
+ * Operations executed every checkpoint (in order of invocation):
+ *  +libxl__checkpoint_devices_postsuspend
+ *  +libxl__checkpoint_devices_preresume
+ *  +libxl__checkpoint_devices_commit
+ *
+ * Each device type needs to implement the interfaces specified in
+ * the libxl__checkpoint_device_instance_ops if it wishes to support Remus/COLO.
+ *
+ * The high-level control flow through the checkpoint device layer is shown
+ * below:
+ *
+ * xl remus
+ *  |->  libxl_domain_remus_start
+ *    |-> libxl__checkpoint_devices_setup
+ *      |-> Per-checkpoint libxl__checkpoint_devices_[postsuspend,preresume,commit]
+ *        ...
+ *        |-> On backup failure, network error or other internal errors:
+ *            libxl__checkpoint_devices_teardown
+ */
+
+typedef struct libxl__checkpoint_device libxl__checkpoint_device;
+typedef struct libxl__checkpoint_devices_state libxl__checkpoint_devices_state;
+typedef struct libxl__checkpoint_device_instance_ops libxl__checkpoint_device_instance_ops;
+
+/*
+ * Interfaces to be implemented by every device subkind that wishes to
+ * support Remus/COLO. Functions must be implemented unless otherwise
+ * stated. Many of these functions are asynchronous. They call
+ * dev->aodev.callback when done.  The actual implementations may be
+ * synchronous and call dev->aodev.callback directly (as the last
+ * thing they do).
+ */
+struct libxl__checkpoint_device_instance_ops {
+    /* the device kind this ops belongs to... */
+    libxl__device_kind kind;
+
+    /*
+     * Checkpoint operations. May be NULL, meaning the op is not
+     * implemented and the caller should treat them as a no-op (and do
+     * nothing when checkpointing).
+     * Asynchronous.
+     */
+
+    void (*postsuspend)(libxl__egc *egc, libxl__checkpoint_device *dev);
+    void (*preresume)(libxl__egc *egc, libxl__checkpoint_device *dev);
+    void (*commit)(libxl__egc *egc, libxl__checkpoint_device *dev);
+
+    /*
+     * setup() and teardown() are refer to the actual checkpoint device.
+     * Asynchronous.
+     * teardown is called even if setup fails.
+     */
+    /*
+     * setup() should first determines whether the subkind matches the specific
+     * device. If matched, the device will then be managed with this set of
+     * subkind operations.
+     * Yields 0 if the device successfully set up.
+     * CHECKPOINT_DEVOPS_DOES_NOT_MATCH if the ops does not match the device.
+     * any other rc indicates failure.
+     */
+    void (*setup)(libxl__egc *egc, libxl__checkpoint_device *dev);
+    void (*teardown)(libxl__egc *egc, libxl__checkpoint_device *dev);
+};
+
+int init_subkind_nic(libxl__checkpoint_devices_state *cds);
+void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds);
+int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
+void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
+
+typedef void libxl__checkpoint_callback(libxl__egc *,
+                                        libxl__checkpoint_devices_state *,
+                                        int rc);
+
+/*
+ * State associated with a checkpoint invocation, including parameters
+ * passed to the checkpoint abstract device layer by the remus
+ * save/restore machinery.
+ */
+struct libxl__checkpoint_devices_state {
+    /*-- must be set by caller of libxl__checkpoint_device_(setup|teardown) --*/
+
+    libxl__ao *ao;
+    uint32_t domid;
+    libxl__checkpoint_callback *callback;
+    void *concrete_data;
+    int device_kind_flags;
+    /* The ops must be pointer array, and the last ops must be NULL. */
+    const libxl__checkpoint_device_instance_ops **ops;
+
+    /*----- private for abstract layer only -----*/
+
+    int num_devices;
+    /*
+     * this array is allocated before setup the checkpoint devices by the
+     * checkpoint abstract layer.
+     * devs may be NULL, means there's no checkpoint devices that has been
+     * set up.
+     * the size of this array is 'num_devices', which is the total number
+     * of libxl nic devices and disk devices(num_nics + num_disks).
+     */
+    libxl__checkpoint_device **devs;
+
+    libxl_device_nic *nics;
+    int num_nics;
+    libxl_device_disk *disks;
+    int num_disks;
+
+    libxl__multidev multidev;
+};
+
+/*
+ * Information about a single device being handled by remus.
+ * Allocated by the checkpoint abstract layer.
+ */
+struct libxl__checkpoint_device {
+    /*----- shared between abstract and concrete layers -----*/
+    /*
+     * if this is true, that means the subkind ops match the device
+     */
+    bool matched;
+
+    /*----- set by checkpoint device abstruct layer -----*/
+    /* libxl__device_* which this checkpoint device related to */
+    const void *backend_dev;
+    libxl__device_kind kind;
+    libxl__checkpoint_devices_state *cds;
+    libxl__ao_device aodev;
+
+    /*----- private for abstract layer only -----*/
+
+    /*
+     * Control and state variables for the asynchronous callback
+     * based loops which iterate over device subkinds, and over
+     * individual devices.
+     */
+    int ops_index;
+    const libxl__checkpoint_device_instance_ops *ops;
+
+    /*----- private for concrete (device-specific) layer -----*/
+
+    /* concrete device's private data */
+    void *concrete_data;
+};
+
+/* the following 5 APIs are async ops, call cds->callback when done */
+_hidden void libxl__checkpoint_devices_setup(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_teardown(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_postsuspend(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_preresume(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds);
+_hidden void libxl__checkpoint_devices_commit(libxl__egc *egc,
+                                        libxl__checkpoint_devices_state *cds);
+
+/*----- Remus related state structure -----*/
+typedef struct libxl__remus_state libxl__remus_state;
+struct libxl__remus_state {
+    /* private */
+    libxl__ev_time checkpoint_timeout; /* used for Remus checkpoint */
+    int interval; /* checkpoint interval */
+
+    /*----- private for concrete (device-specific) layer only -----*/
+    /* private for nic device subkind ops */
+    char *netbufscript;
+    struct nl_sock *nlsock;
+    struct nl_cache *qdisc_cache;
+
+    /* private for drbd disk subkind ops */
+    char *drbd_probe_script;
+};
+_hidden int libxl__netbuffer_enabled(libxl__gc *gc);
+
+/*----- Legacy conversion helper -----*/
+typedef struct libxl__conversion_helper_state libxl__conversion_helper_state;
+
+struct libxl__conversion_helper_state {
+    /* Public - Must be filled by caller unless noted. */
+    libxl__ao *ao;
+    int legacy_fd;             /* fd to read the legacy stream from. */
+    bool hvm;                  /* pv or hvm domain? */
+    libxl__carefd *v2_carefd;  /* Filled by successful call to
+                                * libxl__convert_legacy_stream().  Caller
+                                * assumes ownership of the fd. */
+    void (*completion_callback)(
+        libxl__egc *egc, libxl__conversion_helper_state *chs, int rc);
+    /* private */
+    int rc;
+    libxl__ao_abortable abrt;
+    libxl__ev_child child;
+};
+
+_hidden void libxl__conversion_helper_init
+                    (libxl__conversion_helper_state *chs);
+_hidden int libxl__convert_legacy_stream(libxl__egc *egc,
+                    libxl__conversion_helper_state *chs);
+_hidden void libxl__conversion_helper_abort(libxl__egc *egc,
+                    libxl__conversion_helper_state *chs, int rc);
+static inline bool libxl__conversion_helper_inuse
+                    (const libxl__conversion_helper_state *chs)
+{ return libxl__ev_child_inuse(&chs->child); }
+
+/* State for reading a libxl migration v2 stream */
+typedef struct libxl__stream_read_state libxl__stream_read_state;
+
+typedef struct libxl__sr_record_buf {
+    /* private to stream read helper */
+    LIBXL_STAILQ_ENTRY(struct libxl__sr_record_buf) entry;
+    libxl__sr_rec_hdr hdr;
+    void *body; /* iff hdr.length != 0 */
+} libxl__sr_record_buf;
+
+struct libxl__stream_read_state {
+    /* filled by the user */
+    libxl__ao *ao;
+    libxl__domain_create_state *dcs;
+    int fd;
+    bool legacy;
+    bool back_channel;
+    void (*completion_callback)(libxl__egc *egc,
+                                libxl__stream_read_state *srs,
+                                int rc);
+    void (*checkpoint_callback)(libxl__egc *egc,
+                                libxl__stream_read_state *srs,
+                                int rc);
+    /* Private */
+    int rc;
+    bool running;
+    bool in_checkpoint;
+    bool sync_teardown; /* Only used to coordinate shutdown on error path. */
+    bool in_checkpoint_state;
+    libxl__save_helper_state shs;
+    libxl__conversion_helper_state chs;
+
+    /* Main stream-reading data. */
+    libxl__datacopier_state dc; /* Only used when reading a record */
+    libxl__sr_hdr hdr;
+    LIBXL_STAILQ_HEAD(, libxl__sr_record_buf) record_queue; /* NOGC */
+    enum {
+        SRS_PHASE_NORMAL,
+        SRS_PHASE_BUFFERING,
+        SRS_PHASE_UNBUFFERING,
+    } phase;
+    bool recursion_guard;
+
+    /* Only used while actively reading a record from the stream. */
+    libxl__sr_record_buf *incoming_record; /* NOGC */
+
+    /* Both only used when processing an EMULATOR record. */
+    libxl__datacopier_state emu_dc;
+    libxl__carefd *emu_carefd;
+};
+
+_hidden void libxl__stream_read_init(libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_start(libxl__egc *egc,
+                                      libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_start_checkpoint(libxl__egc *egc,
+                                                 libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_checkpoint_state(libxl__egc *egc,
+                                                 libxl__stream_read_state *stream);
+_hidden void libxl__stream_read_abort(libxl__egc *egc,
+                                      libxl__stream_read_state *stream, int rc);
+static inline bool
+libxl__stream_read_inuse(const libxl__stream_read_state *stream)
+{
+    return stream->running;
+}
+
+#include "libxl_colo.h"
+
+/*----- Domain suspend (save) state structure -----*/
+/*
+ * "suspend" refers to quiescing the VM, so pausing qemu, making a
+ * remote_shutdown(SHUTDOWN_suspend) hypercall etc.
+ *
+ * "save" refers to the actions involved in actually shuffling the
+ * state of the VM, so xc_domain_save() etc.
+ */
+
+typedef struct libxl__domain_suspend_state libxl__domain_suspend_state;
+typedef struct libxl__domain_save_state libxl__domain_save_state;
+
+typedef void libxl__domain_save_cb(libxl__egc*,
+                                   libxl__domain_save_state*, int rc);
+typedef void libxl__save_device_model_cb(libxl__egc*,
+                                         libxl__domain_save_state*, int rc);
+
+/* State for writing a libxl migration v2 stream */
+typedef struct libxl__stream_write_state libxl__stream_write_state;
+typedef void (*sws_record_done_cb)(libxl__egc *egc,
+                                   libxl__stream_write_state *sws);
+struct libxl__stream_write_state {
+    /* filled by the user */
+    libxl__ao *ao;
+    libxl__domain_save_state *dss;
+    int fd;
+    bool back_channel;
+    void (*completion_callback)(libxl__egc *egc,
+                                libxl__stream_write_state *sws,
+                                int rc);
+    void (*checkpoint_callback)(libxl__egc *egc,
+                                libxl__stream_write_state *sws,
+                                int rc);
+    /* Private */
+    int rc;
+    bool running;
+    bool in_checkpoint;
+    bool sync_teardown;  /* Only used to coordinate shutdown on error path. */
+    bool in_checkpoint_state;
+    libxl__save_helper_state shs;
+
+    /* Main stream-writing data. */
+    libxl__datacopier_state dc;
+    sws_record_done_cb record_done_callback;
+
+    /* Cache device model version. */
+    libxl_device_model_version device_model_version;
+
+    /* Only used when constructing EMULATOR records. */
+    libxl__datacopier_state emu_dc;
+    libxl__carefd *emu_carefd;
+    libxl__sr_rec_hdr emu_rec_hdr;
+    libxl__sr_emulator_hdr emu_sub_hdr;
+    void *emu_body;
+};
+
+_hidden void libxl__stream_write_init(libxl__stream_write_state *stream);
+_hidden void libxl__stream_write_start(libxl__egc *egc,
+                                       libxl__stream_write_state *stream);
+_hidden void
+libxl__stream_write_start_checkpoint(libxl__egc *egc,
+                                     libxl__stream_write_state *stream);
+_hidden void
+libxl__stream_write_checkpoint_state(libxl__egc *egc,
+                                     libxl__stream_write_state *stream,
+                                     libxl_sr_checkpoint_state *srcs);
+_hidden void libxl__stream_write_abort(libxl__egc *egc,
+                                       libxl__stream_write_state *stream,
+                                       int rc);
+static inline bool
+libxl__stream_write_inuse(const libxl__stream_write_state *stream)
+{
+    return stream->running;
+}
+
+typedef struct libxl__logdirty_switch {
+    /* Set by caller of libxl__domain_common_switch_qemu_logdirty */
+    libxl__ao *ao;
+    void (*callback)(libxl__egc *egc, struct libxl__logdirty_switch *lds,
+                     int rc);
+
+    const char *cmd;
+    const char *cmd_path;
+    const char *ret_path;
+    libxl__ev_xswatch watch;
+    libxl__ev_time timeout;
+    libxl__ev_qmp qmp;
+} libxl__logdirty_switch;
+
+_hidden void libxl__logdirty_init(libxl__logdirty_switch *lds);
+
+struct libxl__domain_suspend_state {
+    /* set by caller of libxl__domain_suspend_init */
+    libxl__ao *ao;
+    uint32_t domid;
+    bool live;
+
+    /* private */
+    libxl_domain_type type;
+
+    libxl__ev_evtchn guest_evtchn;
+    int guest_evtchn_lockfd;
+    int guest_responded;
+
+    libxl__xswait_state pvcontrol;
+    libxl__ev_xswatch guest_watch;
+    libxl__ev_time guest_timeout;
+    libxl__ev_qmp qmp;
+
+    const char *dm_savefile;
+    void (*callback_device_model_done)(libxl__egc*,
+                              struct libxl__domain_suspend_state*, int rc);
+    void (*callback_common_done)(libxl__egc*,
+                                 struct libxl__domain_suspend_state*, int ok);
+};
+int libxl__domain_suspend_init(libxl__egc *egc,
+                               libxl__domain_suspend_state *dsps,
+                               libxl_domain_type type);
+
+/* calls dsps->callback_device_model_done when done
+ * may synchronously calls this callback */
+_hidden void libxl__qmp_suspend_save(libxl__egc *egc,
+                                     libxl__domain_suspend_state *dsps);
+
+struct libxl__domain_save_state {
+    /* set by caller of libxl__domain_save */
+    libxl__ao *ao;
+    libxl__domain_save_cb *callback;
+
+    uint32_t domid;
+    int fd;
+    int fdfl; /* original flags on fd */
+    int recv_fd;
+    libxl_domain_type type;
+    int live;
+    int debug;
+    int checkpointed_stream;
+    const libxl_domain_remus_info *remus;
+    /* private */
+    int rc;
+    int xcflags;
+    libxl__domain_suspend_state dsps;
+    union {
+        /* for Remus */
+        libxl__remus_state rs;
+        /* for COLO */
+        libxl__colo_save_state css;
+    };
+    libxl__checkpoint_devices_state cds;
+    libxl__stream_write_state sws;
+    libxl__logdirty_switch logdirty;
+};
+
+
+/*----- openpty -----*/
+
+/*
+ * opens count (>0) ptys like count calls to openpty, and then
+ * calls back.  On entry, all op[].master and op[].slave must be
+ * 0.  On callback, either rc==0 and master and slave are non-0,
+ * or rc is a libxl error and they are both 0.  If libxl__openpty
+ * returns non-0 no callback will happen and everything is left
+ * cleaned up.
+ */
+
+typedef struct libxl__openpty_state libxl__openpty_state;
+typedef struct libxl__openpty_result libxl__openpty_result;
+typedef void libxl__openpty_callback(libxl__egc *egc, libxl__openpty_state *op);
+
+struct libxl__openpty_state {
+    /* caller must fill these in, and they must all remain valid */
+    libxl__ao *ao;
+    libxl__openpty_callback *callback;
+    int count;
+    libxl__openpty_result *results; /* actual size is count, out parameter */
+    /* public, result, caller may only read in callback */
+    int rc;
+    /* private for implementation */
+    libxl__ev_child child;
+};
+
+struct libxl__openpty_result {
+    libxl__carefd *master, *slave;
+};
+
+int libxl__openptys(libxl__openpty_state *op,
+                    struct termios *termp,
+                    struct winsize *winp);
+
+
+/*----- bootloader -----*/
+
+typedef struct libxl__bootloader_state libxl__bootloader_state;
+typedef void libxl__run_bootloader_callback(libxl__egc*,
+                                libxl__bootloader_state*, int rc);
+typedef void libxl__bootloader_console_callback(libxl__egc*,
+                                libxl__bootloader_state*);
+
+struct libxl__bootloader_state {
+    /* caller must fill these in, and they must all remain valid */
+    libxl__ao *ao;
+    libxl__run_bootloader_callback *callback;
+    libxl__bootloader_console_callback *console_available;
+    const libxl_domain_build_info *info;
+    libxl_device_disk *disk;
+    /* Should be zeroed by caller on entry.  Will be filled in by
+     * bootloader machinery; represents the local attachment of the
+     * disk for the benefit of the bootloader.  Must be detached by
+     * the caller using libxl__device_disk_local_initiate_detach.
+     * (This is safe to do after ->callback() has happened since
+     * the domain's kernel and initramfs will have been copied
+     * out of the guest's disk into a temporary directory, mapped
+     * as file references, and deleted. */
+    libxl__disk_local_state dls;
+    uint32_t domid;
+    /* outputs:
+     *  - caller must initialise kernel and ramdisk to point to file
+     *    references, these will be updated and mapped;
+     *  - caller must initialise cmdline to NULL, it will be updated with a
+     *    string allocated from the gc;
+     */
+    libxl__file_reference *kernel, *ramdisk;
+    const char *cmdline;
+    /* private to libxl__run_bootloader */
+    char *outputpath, *outputdir, *logfile;
+    libxl__openpty_state openpty;
+    libxl__openpty_result ptys[2];  /* [0] is for bootloader */
+    libxl__ev_child child;
+    libxl__domaindeathcheck deathcheck;
+    int nargs, argsspace;
+    const char **args;
+    libxl__datacopier_state keystrokes, display;
+    int rc, got_pollhup;
+};
+
+_hidden void libxl__bootloader_init(libxl__bootloader_state *bl);
+
+/* Will definitely call st->callback, perhaps reentrantly.
+ * If callback is passed rc==0, will have updated st->info appropriately */
+_hidden void libxl__bootloader_run(libxl__egc*, libxl__bootloader_state *st);
+
+/*----- Generic Device Handling -----*/
+#define LIBXL_DEFINE_DEVICE_ADD(type)                                   \
+    int libxl_device_##type##_add(libxl_ctx *ctx,                       \
+        uint32_t domid, libxl_device_##type *type,                      \
+        const libxl_asyncop_how *ao_how)                                \
+    {                                                                   \
+        AO_CREATE(ctx, domid, ao_how);                                  \
+        libxl__ao_device *aodev;                                        \
+                                                                        \
+        GCNEW(aodev);                                                   \
+        libxl__prepare_ao_device(ao, aodev);                            \
+        aodev->action = LIBXL__DEVICE_ACTION_ADD;                       \
+        aodev->callback = device_addrm_aocomplete;                      \
+        aodev->update_json = true;                                      \
+        libxl__device_##type##_add(egc, domid, type, aodev);            \
+                                                                        \
+        return AO_INPROGRESS;                                           \
+    }
+
+#define LIBXL_DEFINE_DEVICES_ADD(type)                                  \
+    void libxl__add_##type##s(libxl__egc *egc, libxl__ao *ao, uint32_t domid, \
+                              libxl_domain_config *d_config,            \
+                              libxl__multidev *multidev)                \
+    {                                                                   \
+        AO_GC;                                                          \
+        int i;                                                          \
+        for (i = 0; i < d_config->num_##type##s; i++) {                 \
+            libxl__ao_device *aodev = libxl__multidev_prepare(multidev);  \
+            libxl__device_##type##_add(egc, domid, &d_config->type##s[i], \
+                                       aodev);                          \
+        }                                                               \
+    }
+
+#define LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, remtype, removedestroy, f) \
+    int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \
+        uint32_t domid, libxl_device_##type *type,                      \
+        const libxl_asyncop_how *ao_how)                                \
+    {                                                                   \
+        AO_CREATE(ctx, domid, ao_how);                                  \
+        libxl__device *device;                                          \
+        libxl__ao_device *aodev;                                        \
+        int rc;                                                         \
+                                                                        \
+        GCNEW(device);                                                  \
+        rc = libxl__device_from_##type(gc, domid, type, device);        \
+        if (rc != 0) goto out;                                          \
+                                                                        \
+        GCNEW(aodev);                                                   \
+        libxl__prepare_ao_device(ao, aodev);                            \
+        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;                    \
+        aodev->dev = device;                                            \
+        aodev->callback = device_addrm_aocomplete;                      \
+        aodev->force.flag = f;                                          \
+        libxl__initiate_device_##remtype##_remove(egc, aodev);          \
+                                                                        \
+    out:                                                                \
+        if (rc) return AO_CREATE_FAIL(rc);                              \
+        return AO_INPROGRESS;                                           \
+    }
+
+#define LIBXL_DEFINE_UPDATE_DEVID(name)                                 \
+    int libxl__device_##name##_update_devid(libxl__gc *gc,              \
+                                            uint32_t domid,             \
+                                            libxl_device_##name *type)  \
+    {                                                                   \
+        if (type->devid == -1)                                          \
+            type->devid = libxl__device_nextid(gc, domid,               \
+                          libxl__##name##_devtype.type);                \
+        if (type->devid < 0)                                            \
+            return ERROR_FAIL;                                          \
+        return 0;                                                       \
+    }
+
+#define LIBXL_DEFINE_DEVICE_FROM_TYPE(name)                             \
+    int libxl__device_from_##name(libxl__gc *gc, uint32_t domid,        \
+                                  libxl_device_##name *type,            \
+                                  libxl__device *device)                \
+    {                                                                   \
+        device->backend_devid   = type->devid;                          \
+        device->backend_domid   = type->backend_domid;                  \
+        device->backend_kind    = libxl__##name##_devtype.type;         \
+        device->devid           = type->devid;                          \
+        device->domid           = domid;                                \
+        device->kind            = libxl__##name##_devtype.type;         \
+                                                                        \
+        return 0;                                                       \
+    }
+
+#define LIBXL_DEFINE_DEVID_TO_DEVICE(name)                              \
+    int libxl_devid_to_device_##name(libxl_ctx *ctx, uint32_t domid,    \
+                                     int devid,                         \
+                                     libxl_device_##name *type)         \
+    {                                                                   \
+        GC_INIT(ctx);                                                   \
+                                                                        \
+        char *device_path;                                              \
+        const char *tmp;                                                \
+        int rc;                                                         \
+                                                                        \
+        libxl_device_##name##_init(type);                               \
+                                                                        \
+        device_path = GCSPRINTF("%s/device/%s/%d",                      \
+                                libxl__xs_libxl_path(gc, domid),        \
+                                libxl__device_kind_to_string(           \
+                                libxl__##name##_devtype.type),          \
+                                devid);                                 \
+                                                                        \
+        if (libxl__xs_read_mandatory(gc, XBT_NULL, device_path, &tmp)) {\
+            rc = ERROR_NOTFOUND; goto out;                              \
+        }                                                               \
+                                                                        \
+        if (libxl__##name##_devtype.from_xenstore) {                    \
+            rc = libxl__##name##_devtype.from_xenstore(gc, device_path, \
+                                                       devid, type);    \
+            if (rc) goto out;                                           \
+        }                                                               \
+                                                                        \
+        rc = 0;                                                         \
+                                                                        \
+    out:                                                                \
+                                                                        \
+        GC_FREE;                                                        \
+        return rc;                                                      \
+    }
+
+
+#define LIBXL_DEFINE_DEVICE_REMOVE(type)                                \
+    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, remove,               \
+                                   LIBXL__FORCE_AUTO)                   \
+    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, destroy,              \
+                                   LIBXL__FORCE_ON)
+
+#define LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(type)                         \
+    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, remove,                  \
+                                   LIBXL__FORCE_AUTO)                   \
+    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, destroy,                 \
+                                   LIBXL__FORCE_ON)
+
+#define LIBXL_DEFINE_DEVICE_SAFE_REMOVE(type)                           \
+    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, safe_remove,          \
+                                   LIBXL__FORCE_OFF)
+
+#define LIBXL_DEFINE_DEVICE_LIST(type)                                  \
+    libxl_device_##type *libxl_device_##type##_list(libxl_ctx *ctx,     \
+                                                    uint32_t domid,     \
+                                                    int *num)           \
+    {                                                                   \
+        libxl_device_##type *r;                                         \
+        GC_INIT(ctx);                                                   \
+        r = libxl__device_list(gc, &libxl__##type##_devtype,            \
+                               domid, num);                             \
+        GC_FREE;                                                        \
+        return r;                                                       \
+    }                                                                   \
+                                                                        \
+    void libxl_device_##type##_list_free(libxl_device_##type *list,     \
+                                         int num)                       \
+    {                                                                   \
+        libxl__device_list_free(&libxl__##type##_devtype, list, num);   \
+    }
+
+typedef void (*device_add_fn_t)(libxl__egc *, libxl__ao *, uint32_t,
+                                libxl_domain_config *, libxl__multidev *);
+typedef int (*device_set_default_fn_t)(libxl__gc *, uint32_t, void *, bool);
+typedef int (*device_to_device_fn_t)(libxl__gc *, uint32_t, void *,
+                                     libxl__device *);
+typedef void (*device_init_fn_t)(void *);
+typedef void (*device_copy_fn_t)(libxl_ctx *, void *dst, const void *src);
+typedef void (*device_dispose_fn_t)(void *);
+typedef int (*device_compare_fn_t)(const void *, const void *);
+typedef void (*device_merge_fn_t)(libxl_ctx *, void *, void *);
+typedef int (*device_dm_needed_fn_t)(void *, unsigned);
+typedef void (*device_update_config_fn_t)(libxl__gc *, void *, void *);
+typedef int (*device_update_devid_fn_t)(libxl__gc *, uint32_t, void *);
+typedef int (*device_get_num_fn_t)(libxl__gc *, const char *, unsigned int *);
+typedef int (*device_from_xenstore_fn_t)(libxl__gc *, const char *,
+                                         libxl_devid, void *);
+typedef int (*device_set_xenstore_config_fn_t)(libxl__gc *, uint32_t, void *,
+                                               flexarray_t *, flexarray_t *,
+                                               flexarray_t *);
+
+struct libxl__device_type {
+    libxl__device_kind type;
+    int skip_attach;   /* Skip entry in domcreate_attach_devices() if 1 */
+    int ptr_offset;    /* Offset of device array ptr in libxl_domain_config */
+    int num_offset;    /* Offset of # of devices in libxl_domain_config */
+    int dev_elem_size; /* Size of one device element in array */
+    device_add_fn_t                 add;
+    device_set_default_fn_t         set_default;
+    device_to_device_fn_t           to_device;
+    device_init_fn_t                init;
+    device_copy_fn_t                copy;
+    device_dispose_fn_t             dispose;
+    device_compare_fn_t             compare;
+    device_merge_fn_t               merge;
+    device_dm_needed_fn_t           dm_needed;
+    device_update_config_fn_t       update_config;
+    device_update_devid_fn_t        update_devid;
+    device_get_num_fn_t             get_num;
+    device_from_xenstore_fn_t       from_xenstore;
+    device_set_xenstore_config_fn_t set_xenstore_config;
+};
+
+#define DEFINE_DEVICE_TYPE_STRUCT_X(name, sname, kind, ...)                    \
+    const libxl__device_type libxl__ ## name ## _devtype = {                   \
+        .type          = LIBXL__DEVICE_KIND_ ## kind,                       \
+        .ptr_offset    = offsetof(libxl_domain_config, name ## s),             \
+        .num_offset    = offsetof(libxl_domain_config, num_ ## name ## s),     \
+        .dev_elem_size = sizeof(libxl_device_ ## sname),                       \
+        .add           = libxl__add_ ## name ## s,                             \
+        .set_default   = (device_set_default_fn_t)                             \
+                         libxl__device_ ## sname ## _setdefault,               \
+        .to_device     = (device_to_device_fn_t)libxl__device_from_ ## name,   \
+        .init          = (device_init_fn_t)libxl_device_ ## sname ## _init,    \
+        .copy          = (device_copy_fn_t)libxl_device_ ## sname ## _copy,    \
+        .dispose       = (device_dispose_fn_t)                                 \
+                         libxl_device_ ## sname ## _dispose,                   \
+        .compare       = (device_compare_fn_t)                                 \
+                         libxl_device_ ## sname ## _compare,                   \
+        .update_devid  = (device_update_devid_fn_t)                            \
+                         libxl__device_ ## sname ## _update_devid,             \
+        __VA_ARGS__                                                            \
+    }
+
+#define DEFINE_DEVICE_TYPE_STRUCT(name, kind, ...)                             \
+    DEFINE_DEVICE_TYPE_STRUCT_X(name, name, kind, __VA_ARGS__)
+
+static inline void **libxl__device_type_get_ptr(
+    const libxl__device_type *dt, const libxl_domain_config *d_config)
+{
+    return (void **)((void *)d_config + dt->ptr_offset);
+}
+
+static inline void *libxl__device_type_get_elem(
+    const libxl__device_type *dt, const libxl_domain_config *d_config,
+    int e)
+{
+    return *libxl__device_type_get_ptr(dt, d_config) + dt->dev_elem_size * e;
+}
+
+static inline int *libxl__device_type_get_num(
+    const libxl__device_type *dt, const libxl_domain_config *d_config)
+{
+    return (int *)((void *)d_config + dt->num_offset);
+}
+
+extern const libxl__device_type libxl__vfb_devtype;
+extern const libxl__device_type libxl__vkb_devtype;
+extern const libxl__device_type libxl__disk_devtype;
+extern const libxl__device_type libxl__nic_devtype;
+extern const libxl__device_type libxl__vtpm_devtype;
+extern const libxl__device_type libxl__usbctrl_devtype;
+extern const libxl__device_type libxl__usbdev_devtype;
+extern const libxl__device_type libxl__pcidev_devtype;
+extern const libxl__device_type libxl__vdispl_devtype;
+extern const libxl__device_type libxl__p9_devtype;
+extern const libxl__device_type libxl__pvcallsif_devtype;
+extern const libxl__device_type libxl__vsnd_devtype;
+
+extern const libxl__device_type *device_type_tbl[];
+
+/*----- Domain destruction -----*/
+
+/* Domain destruction has been split into two functions:
+ *
+ * libxl__domain_destroy is the main destroy function, which detects
+ * stubdoms and calls libxl__destroy_domid on the domain and its
+ * stubdom if present, creating a different libxl__destroy_domid_state
+ * for each one of them.
+ *
+ * libxl__destroy_domid actually destroys the domain, but it
+ * doesn't check for stubdomains, since that would involve
+ * recursion, which we want to avoid.
+ */
+
+typedef struct libxl__domain_destroy_state libxl__domain_destroy_state;
+typedef struct libxl__destroy_domid_state libxl__destroy_domid_state;
+typedef struct libxl__destroy_devicemodel_state libxl__destroy_devicemodel_state;
+typedef struct libxl__devices_remove_state libxl__devices_remove_state;
+
+typedef void libxl__domain_destroy_cb(libxl__egc *egc,
+                                      libxl__domain_destroy_state *dds,
+                                      int rc);
+
+typedef void libxl__domid_destroy_cb(libxl__egc *egc,
+                                     libxl__destroy_domid_state *dis,
+                                     int rc);
+
+typedef void libxl__devicemodel_destroy_cb(libxl__egc *egc,
+                                     libxl__destroy_devicemodel_state *ddms,
+                                     int rc);
+
+typedef void libxl__devices_remove_callback(libxl__egc *egc,
+                                            libxl__devices_remove_state *drs,
+                                            int rc);
+
+struct libxl__devices_remove_state {
+    /* filled in by user */
+    libxl__ao *ao;
+    uint32_t domid;
+    libxl__devices_remove_callback *callback;
+    libxl__force force; /* libxl_device_TYPE_destroy rather than _remove */
+    /* private */
+    libxl__multidev multidev;
+    int num_devices;
+};
+
+struct libxl__destroy_devicemodel_state {
+    /* filled in by user */
+    libxl__ao *ao;
+    uint32_t domid;
+    libxl__devicemodel_destroy_cb *callback; /* May be called re-entrantly */
+    /* private to implementation */
+    libxl__ev_child destroyer;
+    int rc; /* Accumulated return value for the destroy operation */
+};
+
+struct libxl__destroy_domid_state {
+    /* filled in by user */
+    libxl__ao *ao;
+    uint32_t domid;
+    libxl__domid_destroy_cb *callback;
+    /* private to implementation */
+    libxl__devices_remove_state drs;
+    libxl__destroy_devicemodel_state ddms;
+    libxl__ev_child destroyer;
+    bool soft_reset;
+    libxl__multidev multidev;
+};
+
+struct libxl__domain_destroy_state {
+    /* filled by the user */
+    libxl__ao *ao;
+    uint32_t domid;
+    libxl__domain_destroy_cb *callback;
+    /* Private */
+    int rc;
+    uint32_t stubdomid;
+    libxl__destroy_domid_state stubdom;
+    int stubdom_finished;
+    libxl__destroy_domid_state domain;
+    int domain_finished;
+    bool soft_reset;
+};
+
+/*
+ * Entry point for domain destruction
+ * This function checks for stubdom presence and then calls
+ * libxl__destroy_domid on the passed domain and its stubdom if found.
+ */
+_hidden void libxl__domain_destroy(libxl__egc *egc,
+                                   libxl__domain_destroy_state *dds);
+
+/* Used to destroy a domain with the passed id (it doesn't check for stubs) */
+_hidden void libxl__destroy_domid(libxl__egc *egc,
+                                  libxl__destroy_domid_state *dis);
+
+/* Used to detroy the device model */
+_hidden void libxl__destroy_device_model(libxl__egc *egc,
+                                         libxl__destroy_devicemodel_state *ddms);
+
+/* Entry point for devices destruction */
+_hidden void libxl__devices_destroy(libxl__egc *egc,
+                                    libxl__devices_remove_state *drs);
+
+/* Helper function to add a bunch of disks. This should be used when
+ * the caller is inside an async op. "multidev" will NOT be prepared by
+ * this function, so the caller must make sure to call
+ * libxl__multidev_begin before calling this function.
+ *
+ * The "callback" will be called for each device, and the user is responsible
+ * for calling libxl__ao_device_check_last on the callback.
+ */
+_hidden void libxl__add_disks(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+                              libxl_domain_config *d_config,
+                              libxl__multidev *multidev);
+
+_hidden void libxl__add_nics(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+                             libxl_domain_config *d_config,
+                             libxl__multidev *multidev);
+
+/*----- device model creation -----*/
+
+/* First layer; wraps libxl__spawn_spawn. */
+
+typedef struct libxl__dm_spawn_state libxl__dm_spawn_state;
+
+typedef void libxl__dm_spawn_cb(libxl__egc *egc, libxl__dm_spawn_state*,
+                                int rc /* if !0, error was logged */);
+
+/* Call dmss_init and dmss_dispose to initialise and dispose of
+ * libxl__dm_spawn_state */
+struct libxl__dm_spawn_state {
+    /* mixed - spawn.ao must be initialised by user; rest is private: */
+    libxl__spawn_state spawn;
+    libxl__ev_qmp qmp;
+    libxl__ev_time timeout;
+    libxl__dm_resume_state dmrs;
+    /* filled in by user, must remain valid: */
+    uint32_t guest_domid; /* domain being served */
+    libxl_domain_config *guest_config;
+    libxl__domain_build_state *build_state; /* relates to guest_domid */
+    libxl__dm_spawn_cb *callback;
+};
+
+_hidden void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state*);
+
+/*
+ * Called after forking but before executing the local devicemodel.
+ */
+_hidden int libxl__local_dm_preexec_restrict(libxl__gc *gc);
+
+/* Stubdom device models. */
+
+typedef struct {
+    /* Mixed - user must fill in public parts EXCEPT callback,
+     * which may be undefined on entry.  (See above for details) */
+    libxl__dm_spawn_state dm; /* the stub domain device model */
+    /* filled in by user, must remain valid: */
+    libxl__dm_spawn_cb *callback; /* called as callback(,&sdss->dm,) */
+    /* private to libxl__spawn_stub_dm: */
+    libxl_domain_config dm_config;
+    libxl__domain_build_state dm_state;
+    libxl__dm_spawn_state pvqemu;
+    libxl__destroy_domid_state dis;
+    libxl__multidev multidev;
+    libxl__xswait_state xswait;
+    libxl__spawn_state qmp_proxy_spawn;
+} libxl__stub_dm_spawn_state;
+
+_hidden void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state*);
+
+_hidden char *libxl__stub_dm_name(libxl__gc *gc, const char * guest_name);
+
+/* Qdisk backend launch helpers */
+
+_hidden void libxl__spawn_qdisk_backend(libxl__egc *egc,
+                                        libxl__dm_spawn_state *dmss);
+_hidden int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid);
+
+/*----- Domain creation -----*/
+
+
+struct libxl__domain_create_state {
+    /* filled in by user */
+    libxl__ao *ao;
+    libxl_domain_config *guest_config;
+    libxl_domain_config guest_config_saved; /* vanilla config */
+    int restore_fd, libxc_fd;
+    int restore_fdfl; /* original flags of restore_fd */
+    int send_back_fd;
+    libxl_domain_restore_params restore_params;
+    uint32_t domid;
+    bool soft_reset;
+    libxl__domain_create_cb *callback;
+    libxl_asyncprogress_how aop_console_how;
+    /* private to domain_create */
+    int guest_domid;
+    int device_type_idx;
+    const char *colo_proxy_script;
+    libxl__domain_build_state build_state;
+    libxl__colo_restore_state crs;
+    libxl__checkpoint_devices_state cds;
+    libxl__bootloader_state bl;
+    libxl__stub_dm_spawn_state sdss;
+        /* If we're not doing stubdom, we use only sdss.dm,
+         * for the non-stubdom device model. */
+    libxl__stream_read_state srs;
+    /* necessary if the domain creation failed and we have to destroy it */
+    libxl__domain_destroy_state dds;
+    libxl__multidev multidev;
+    libxl__xswait_state console_xswait;
+};
+
+_hidden int libxl__device_nic_set_devids(libxl__gc *gc,
+                                         libxl_domain_config *d_config,
+                                         uint32_t domid);
+
+/*----- Domain suspend (save) functions -----*/
+
+/* calls dss->callback when done */
+_hidden void libxl__domain_save(libxl__egc *egc,
+                                libxl__domain_save_state *dss);
+
+
+/* calls libxl__xc_domain_suspend_done when done */
+_hidden void libxl__xc_domain_save(libxl__egc *egc,
+                                   libxl__domain_save_state *dss,
+                                   libxl__save_helper_state *shs);
+/* If rc==0 then retval is the return value from xc_domain_save
+ * and errnoval is the errno value it provided.
+ * If rc!=0, retval and errnoval are undefined. */
+_hidden void libxl__xc_domain_save_done(libxl__egc*, void *dss_void,
+                                        int rc, int retval, int errnoval);
+
+/* Used by asynchronous callbacks: ie ones which xc regards as
+ * returning a value, but which we want to handle asynchronously.
+ * Such functions' actual callback function return void in libxl
+ * When they are ready to indicate completion, they call this. */
+void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
+                           libxl__save_helper_state *shs, int return_value);
+
+
+_hidden void libxl__domain_suspend_common_switch_qemu_logdirty
+                               (uint32_t domid, unsigned int enable, void *data);
+_hidden void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
+                                               int domid, unsigned enable,
+                                               libxl__logdirty_switch *lds);
+_hidden int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
+                                               char **buf, uint32_t *len);
+_hidden int libxl__restore_emulator_xenstore_data
+    (libxl__domain_create_state *dcs, const char *ptr, uint32_t size);
+
+
+/* calls libxl__xc_domain_restore_done when done */
+_hidden void libxl__xc_domain_restore(libxl__egc *egc,
+                                      libxl__domain_create_state *dcs,
+                                      libxl__save_helper_state *shs);
+/* If rc==0 then retval is the return value from xc_domain_save
+ * and errnoval is the errno value it provided.
+ * If rc!=0, retval and errnoval are undefined. */
+_hidden void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
+                                           int rc, int retval, int errnoval);
+
+_hidden void libxl__save_helper_init(libxl__save_helper_state *shs);
+_hidden void libxl__save_helper_abort(libxl__egc *egc,
+                                      libxl__save_helper_state *shs);
+
+static inline bool libxl__save_helper_inuse(const libxl__save_helper_state *shs)
+{
+    return libxl__ev_child_inuse(&shs->child);
+}
+
+/* Each time the dm needs to be saved, we must call suspend and then save
+ * calls dsps->callback_device_model_done when done */
+_hidden void libxl__domain_suspend_device_model(libxl__egc *egc,
+                                           libxl__domain_suspend_state *dsps);
+
+_hidden const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid);
+
+/* calls dsps->callback_common_done when done */
+_hidden void libxl__domain_suspend(libxl__egc *egc,
+                                   libxl__domain_suspend_state *dsps);
+/* used by libxc to suspend the guest during migration */
+_hidden void libxl__domain_suspend_callback(void *data);
+
+/* Remus setup and teardown */
+_hidden void libxl__remus_setup(libxl__egc *egc,
+                                libxl__remus_state *rs);
+_hidden void libxl__remus_teardown(libxl__egc *egc,
+                                   libxl__remus_state *rs,
+                                   int rc);
+_hidden void libxl__remus_restore_setup(libxl__egc *egc,
+                                        libxl__domain_create_state *dcs);
+
+_hidden char *libxl__domid_history_path(libxl__gc *gc,
+                                        const char *suffix);
+
+/*
+ * Convenience macros.
+ */
+
+#define FILLZERO LIBXL_FILLZERO
+
+
+/*
+ * All of these assume (or define)
+ *    libxl__gc *gc;
+ * as a local variable.
+ */
+
+#define GC_INIT(ctx)  libxl__gc gc[1]; LIBXL_INIT_GC(gc[0],ctx)
+#define GC_FREE       libxl__free_all(gc)
+#define CTX           libxl__gc_owner(gc)
+#define NOGC          (&CTX->nogc_gc) /* pass only to consenting functions */
+
+/* Allocation macros all of which use the gc. */
+
+#define ARRAY_SIZE_OK(ptr, nmemb) ((nmemb) < INT_MAX / (sizeof(*(ptr)) * 2))
+
+/*
+ * Expression statement  <type> *GCNEW(<type> *var);
+ * Uses                  libxl__gc *gc;
+ *
+ * Allocates a new object of type <type> from the gc and zeroes it
+ * with memset.  Sets var to point to the new object or zero (setting
+ * errno).  Returns the new value of var.
+ */
+#define GCNEW(var)                                      \
+    (((var) = libxl__zalloc((gc),sizeof(*(var)))))
+
+/*
+ * Expression statement  <type> *GCNEW_ARRAY(<type> *var, ssize_t nmemb);
+ * Uses                  libxl__gc *gc;
+ *
+ * Like GCNEW but allocates an array of nmemb elements, as if from
+ * calloc.  Does check for integer overflow due to large nmemb.  If
+ * nmemb is 0 may succeed by returning 0.
+ */
+#define GCNEW_ARRAY(var, nmemb)                                 \
+    ((var) = libxl__calloc((gc), (nmemb), sizeof(*(var))))
+
+/*
+ * Expression statement  <type> *GCREALLOC_ARRAY(<type> *var, size_t nmemb);
+ * Uses                  libxl__gc *gc;
+ *
+ * Reallocates the array var to be of size nmemb elements.  Updates
+ * var and returns the new value of var.  Does check for integer
+ * overflow due to large nmemb.
+ *
+ * Do not pass nmemb==0.  old may be 0 on entry.
+ */
+#define GCREALLOC_ARRAY(var, nmemb)                                     \
+    (assert(nmemb > 0),                                                 \
+     assert(ARRAY_SIZE_OK((var), (nmemb))),                             \
+     (var) = libxl__realloc((gc), (var), (nmemb)*sizeof(*(var))))
+
+
+/*
+ * Expression            char *GCSPRINTF(const char *fmt, ...);
+ * Uses                  libxl__gc *gc;
+ *
+ * Trivial convenience wrapper for libxl__sprintf.
+ */
+#define GCSPRINTF(fmt, ...) (libxl__sprintf((gc), (fmt), __VA_ARGS__))
+
+
+/*
+ * Expression statements
+ *    void LOG(<xtl_level_suffix>, const char *fmt, ...);
+ *    void LOGE(<xtl_level_suffix>, const char *fmt, ...);
+ *    void LOGEV(<xtl_level_suffix>, int errnoval, const char *fmt, ...);
+ *
+ *    void LOGD(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
+ *    void LOGED(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
+ *    void LOGEVD(<xtl_level_suffix>, int errnoval, uint32_t domid, const char *fmt, ...);
+ * Use
+ *    libxl__gc *gc;
+ *
+ * Trivial convenience wrappers for LIBXL__LOG, LIBXL__LOG_ERRNO,
+ * LIBXL__LOG_ERRNOVAL, LIBXL__LOGD, LIBXL__LOGD_ERRNO and
+ * LIBXL__LOGD_ERRNOVAL respectively (and thus for libxl__log).
+ *
+ * XTL_<xtl_level_suffix> should exist and be an xentoollog.h log level
+ * So <xtl_level_suffix> should be one of
+ *   DEBUG VERBOSE DETAIL PROGRESS INFO NOTICE WARN ERROR ERROR CRITICAL
+ * Of these, most of libxl uses
+ *   DEBUG INFO WARN ERROR
+ *
+ * The LOG*D family will preprend the log message with a string formatted
+ * as follows: 'Domain %PRIu32:'. This should help better automatic sorting
+ * of log messages per domain.
+ */
+#define LOG(l,f, ...)     LIBXL__LOG(CTX,XTL_##l,(f),##__VA_ARGS__)
+#define LOGE(l,f, ...)    LIBXL__LOG_ERRNO(CTX,XTL_##l,(f),##__VA_ARGS__)
+#define LOGEV(l,e,f, ...) LIBXL__LOG_ERRNOVAL(CTX,XTL_##l,(e),(f),##__VA_ARGS__)
+
+#define LOGD(l,d,f, ...)     LIBXL__LOGD(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
+#define LOGED(l,d,f, ...)    LIBXL__LOGD_ERRNO(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
+#define LOGEVD(l,e,d,f, ...) LIBXL__LOGD_ERRNOVAL(CTX,XTL_##l,(e),(d),(f),##__VA_ARGS__)
+
+
+/* Locking functions.  See comment for "lock" member of libxl__ctx. */
+
+static inline void libxl__ctx_lock(libxl_ctx *ctx) {
+    int r = pthread_mutex_lock(&ctx->lock);
+    assert(!r);
+}
+
+static inline void libxl__ctx_unlock(libxl_ctx *ctx) {
+    int r = pthread_mutex_unlock(&ctx->lock);
+    assert(!r);
+}
+
+#define CTX_LOCK (libxl__ctx_lock(CTX))
+#define CTX_UNLOCK (libxl__ctx_unlock(CTX))
+
+/*
+ * Automatic NUMA placement
+ *
+ * These functions and data structures deal with the initial placement of a
+ * domain onto the host NUMA nodes.
+ *
+ * The key concept here is the one of "NUMA placement candidate", which is
+ * basically a set of nodes whose characteristics have been successfully
+ * checked against some specific requirements. More precisely, a candidate
+ * is the nodemap associated with one of the possible subset of the host
+ * NUMA nodes providing a certain amount of free memory, or a given number
+ * of cpus, or even both (depending in what the caller wants). For
+ * convenience of use, some of this information are stored within the
+ * candidate itself, instead of always being dynamically computed. A single
+ * node can be valid placement candidate, as well as it is possible for a
+ * candidate to contain all the nodes of the host. The fewer nodes there
+ * are in a candidate, the better performance a domain placed onto it
+ * should get (at least from a NUMA point of view). For instance, looking
+ * for a numa candidates with 2GB of free memory means we want the subsets
+ * of the host NUMA nodes with, cumulatively, at least 2GB of free memory.
+ * This condition can be satisfied by just one particular node, or it may
+ * require more nodes, depending on the characteristics of the host, on how
+ * many domains have been created already, on how big they are, etc.
+ *
+ * The intended usage is as follows:
+ *  1. first of all, call libxl__get_numa_candidates(), and specify the
+ *     proper constraints to it (e.g., the amount of memory a domain need
+ *     as the minimum amount of free memory for the candidates). If a
+ *     candidate comparison function is provided, the candidate with fewer
+ *     nodes that is found to be best according to what such fucntion says
+ *     is returned. If no comparison function is passed, the very first
+ *     candidate is.
+ *  2. The chosen candidate's nodemap should be utilized for computing the
+ *     actual affinity of the domain which, given the current NUMA support
+ *     in the hypervisor, is what determines the placement of the domain's
+ *     vcpus and memory.
+ */
+
+typedef struct {
+    int nr_cpus, nr_nodes;
+    int nr_vcpus;
+    uint64_t free_memkb;
+    libxl_bitmap nodemap;
+} libxl__numa_candidate;
+
+/* Signature for the comparison function between two candidates */
+typedef int (*libxl__numa_candidate_cmpf)(const libxl__numa_candidate *c1,
+                                          const libxl__numa_candidate *c2);
+
+/*
+ * This looks for the best NUMA placement candidate satisfying some
+ * specific conditions. If min_nodes and/or max_nodes are not 0, their
+ * value is used to determine the minimum and maximum number of nodes the
+ * candidate can have. If they are 0, it means the candidate can contain
+ * from 1 node (min_nodes=0) to the total number of nodes of the host
+ * (max_ndoes=0). If min_free_memkb and/or min_cpus are not 0, the caller
+ * only wants candidates with at least the amount of free memory and the
+ * number of cpus they specify, respectively. If they are 0, the
+ * candidates' free memory and/or number of cpus won't be checked at all.
+ *
+ * Candidates are compared among each others by calling numa_cmpf(), which
+ * is where the heuristics for determining which candidate is the best
+ * one is actually implemented. The only bit of it that is hardcoded in
+ * this function is the fact that candidates with fewer nodes are always
+ * preferrable.
+ *
+ * If at least one suitable candidate is found, it is returned in cndt_out,
+ * cndt_found is set to one, and the function returns successfully. On the
+ * other hand, if not even one single candidate can be found, the function
+ * still returns successfully but cndt_found will be zero.
+ *
+ * Finally, suitable_cpumap is useful for telling that only the cpus in that
+ * mask should be considered when generating placement candidates (for
+ * example because of cpupools).
+ *
+ * It is up to the function to properly allocate cndt_out (by calling
+ * libxl__numa_candidate_alloc()), while it is the caller that should init
+ * (libxl__numa_candidate_init()) and free (libxl__numa_candidate_dispose())
+ * it.
+ */
+_hidden int libxl__get_numa_candidate(libxl__gc *gc,
+                                      uint64_t min_free_memkb, int min_cpus,
+                                      int min_nodes, int max_nodes,
+                                      const libxl_bitmap *suitable_cpumap,
+                                      libxl__numa_candidate_cmpf numa_cmpf,
+                                      libxl__numa_candidate *cndt_out,
+                                      int *cndt_found);
+
+/* Initialization, allocation and deallocation for placement candidates */
+static inline void libxl__numa_candidate_init(libxl__numa_candidate *cndt)
+{
+    cndt->free_memkb = 0;
+    cndt->nr_cpus = cndt->nr_nodes = cndt->nr_vcpus = 0;
+    libxl_bitmap_init(&cndt->nodemap);
+}
+
+static inline int libxl__numa_candidate_alloc(libxl__gc *gc,
+                                              libxl__numa_candidate *cndt)
+{
+    return libxl_node_bitmap_alloc(CTX, &cndt->nodemap, 0);
+}
+static inline void libxl__numa_candidate_dispose(libxl__numa_candidate *cndt)
+{
+    libxl_bitmap_dispose(&cndt->nodemap);
+}
+
+/* Retrieve (in nodemap) the node map associated to placement candidate cndt */
+static inline
+void libxl__numa_candidate_get_nodemap(libxl__gc *gc,
+                                       const libxl__numa_candidate *cndt,
+                                       libxl_bitmap *nodemap)
+{
+    libxl_bitmap_copy(CTX, nodemap, &cndt->nodemap);
+}
+/* Set the node map of placement candidate cndt to match nodemap */
+static inline
+void libxl__numa_candidate_put_nodemap(libxl__gc *gc,
+                                       libxl__numa_candidate *cndt,
+                                       const libxl_bitmap *nodemap)
+{
+    libxl_bitmap_copy(CTX, &cndt->nodemap, nodemap);
+}
+
+/* Check if vNUMA config is valid. Returns 0 if valid,
+ * ERROR_VNUMA_CONFIG_INVALID otherwise.
+ */
+int libxl__vnuma_config_check(libxl__gc *gc,
+                              const libxl_domain_build_info *b_info,
+                              const libxl__domain_build_state *state);
+int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
+                                            uint32_t domid,
+                                            libxl_domain_build_info *b_info,
+                                            libxl__domain_build_state *state);
+int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
+                                    uint32_t domid,
+                                    libxl_domain_build_info *b_info,
+                                    libxl__domain_build_state *state);
+int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
+                                     uint32_t domid,
+                                     libxl_domain_build_info *b_info,
+                                     libxl__domain_build_state *state,
+                                     struct xc_dom_image *dom);
+bool libxl__vnuma_configured(const libxl_domain_build_info *b_info);
+
+_hidden int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
+                                   const libxl_ms_vm_genid *id);
+
+
+/* Som handy macros for defbool type. */
+#define LIBXL__DEFBOOL_DEFAULT     (0)
+#define LIBXL__DEFBOOL_FALSE       (-1)
+#define LIBXL__DEFBOOL_TRUE        (1)
+#define LIBXL__DEFBOOL_STR_DEFAULT "<default>"
+#define LIBXL__DEFBOOL_STR_FALSE   "False"
+#define LIBXL__DEFBOOL_STR_TRUE    "True"
+static inline int libxl__defbool_is_default(libxl_defbool *db)
+{
+    return !db->val;
+}
+
+/*
+ * Inserts "elm_new" into the sorted list "head".
+ *
+ * "elm_search" must be a loop search variable of the same type as
+ * "elm_new".  "new_after_search_p" must be an expression which is
+ * true iff the element "elm_new" sorts after the element
+ * "elm_search".
+ *
+ * "search_body" can be empty, or some declaration(s) and statement(s)
+ * needed for "new_after_search_p".
+ */
+#define LIBXL_TAILQ_INSERT_SORTED(head, entry, elm_new, elm_search,     \
+                                  search_body, new_after_search_p)      \
+    do {                                                                \
+        for ((elm_search) = LIBXL_TAILQ_FIRST((head));                  \
+             (elm_search);                                              \
+             (elm_search) = LIBXL_TAILQ_NEXT((elm_search), entry)) {    \
+            search_body;                                                \
+            if (!(new_after_search_p))                                  \
+                break;                                                  \
+        }                                                               \
+        /* now elm_search is either the element before which we want    \
+         * to place elm_new, or NULL meaning we want to put elm_new at  \
+         * the end */                                                   \
+        if ((elm_search))                                               \
+            LIBXL_TAILQ_INSERT_BEFORE((elm_search), (elm_new), entry);  \
+        else                                                            \
+            LIBXL_TAILQ_INSERT_TAIL((head), (elm_new), entry);          \
+    } while(0)
+
+
+/*
+ * int CTYPE(ISFOO, char c);
+ * int CTYPE(toupper, char c);
+ * int CTYPE(tolower, char c);
+ *
+ * This is necessary because passing a simple char to a ctype.h
+ * is forbidden.  ctype.h macros take ints derived from _unsigned_ chars.
+ *
+ * If you have a char which might be EOF then you should already have
+ * it in an int representing an unsigned char, and you can use the
+ * <ctype.h> macros directly.  This generally happens only with values
+ * from fgetc et al.
+ *
+ * For any value known to be a character (eg, anything that came from
+ * a char[]), use CTYPE.
+ */
+#define CTYPE(isfoo,c) (isfoo((unsigned char)(c)))
+
+int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                              libxl_defbool *p);
+int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                           bool *p);
+int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                          libxl_mac *p);
+int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                             libxl_bitmap *p);
+int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                           libxl_uuid *p);
+int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
+                                        const libxl__json_object *o,
+                                        libxl_cpuid_policy_list *p);
+int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                                  libxl_string_list *p);
+int libxl__key_value_list_parse_json(libxl__gc *gc,
+                                     const libxl__json_object *o,
+                                     libxl_key_value_list *p);
+int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                            libxl_hwcap *p);
+int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                                  libxl_ms_vm_genid *p);
+int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                          void *p);
+int libxl__uint8_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                            void *p);
+int libxl__uint16_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                             void *p);
+int libxl__uint32_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                             void *p);
+int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                             void *p);
+int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                             char **p);
+
+int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len);
+
+#include "_libxl_types_private.h"
+#include "_libxl_types_internal_private.h"
+
+/* This always return false, there's no "default value" for hw cap */
+static inline int libxl__hwcap_is_default(libxl_hwcap *hwcap)
+{
+    return 0;
+}
+
+static inline int libxl__string_list_is_empty(libxl_string_list *psl)
+{
+    return !libxl_string_list_length(psl);
+}
+
+static inline int libxl__key_value_list_is_empty(libxl_key_value_list *pkvl)
+{
+    return !libxl_key_value_list_length(pkvl);
+}
+
+int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl);
+
+/* Portability note: a proper flock(2) implementation is required */
+typedef struct {
+    libxl__carefd *carefd;
+    char *path; /* path of the lock file itself */
+} libxl__flock;
+/* The CTX_LOCK must be held around uses of this lock */
+
+libxl__flock *libxl__lock_file(libxl__gc *gc, const char *filename);
+void libxl__unlock_file(libxl__flock *lock);
+
+libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid);
+libxl__flock *libxl__lock_domid_history(libxl__gc *gc);
+
+/*
+ * Retrieve / store domain configuration from / to libxl private
+ * data store. The registry entry in libxl private data store
+ * is "libxl-json".
+ * Caller must hold user data lock.
+ *
+ * Other names used for this lock throughout the libxl code are json_lock,
+ * libxl__domain_userdata_lock, "libxl-json", data store lock.
+ *
+ * See the comment for libxl__ao_device, and "Algorithm for handling device
+ * removal", for information about using the libxl-json lock / json_lock.
+ */
+int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
+                                    libxl_domain_config *d_config);
+int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
+                                    libxl_domain_config *d_config);
+
+/* ------ Things related to updating domain configurations ----- */
+void libxl__update_domain_configuration(libxl__gc *gc,
+                                        libxl_domain_config *dst,
+                                        const libxl_domain_config *src);
+
+/* Target memory in xenstore is different from what user has
+ * asked for. The difference is video_memkb + (possible) fudge.
+ * See libxl_set_memory_target.
+ */
+static inline
+uint64_t libxl__get_targetmem_fudge(libxl__gc *gc,
+                                    const libxl_domain_build_info *info)
+{
+    int64_t mem_target_fudge = (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+                                info->max_memkb > info->target_memkb)
+                                ? LIBXL_MAXMEM_CONSTANT : 0;
+
+    return info->video_memkb + mem_target_fudge;
+}
+
+int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
+                             uint64_t *out_target_memkb,
+                             uint64_t *out_max_memkb);
+void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
+                          const xc_domaininfo_t *xcinfo,
+                          libxl_dominfo *xlinfo);
+
+/* Macros used to compare device identifier. Returns true if the two
+ * devices have same identifier. */
+#define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid)
+#define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev))
+#define COMPARE_PCI(a, b) ((a)->func == (b)->func &&    \
+                           (a)->bus == (b)->bus &&      \
+                           (a)->dev == (b)->dev)
+#define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \
+                           (a)->port == (b)->port)
+#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid)
+
+/* This function copies X bytes from source to destination bitmap,
+ * where X is the smaller of the two sizes.
+ *
+ * If destination's size is larger than source, the extra bytes are
+ * untouched.
+ *
+ * XXX This is introduced to fix a regression for 4.5. It shall
+ * be revisited in 4.6 time frame.
+ */
+void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
+                                    const libxl_bitmap *sptr);
+
+int libxl__count_physical_sockets(libxl__gc *gc, int *sockets);
+
+_hidden int libxl__read_sysfs_file_contents(libxl__gc *gc,
+                                            const char *filename,
+                                            void **data_r,
+                                            int *datalen_r);
+
+#define LIBXL_QEMU_USER_PREFIX "xen-qemuuser"
+#define LIBXL_QEMU_USER_SHARED LIBXL_QEMU_USER_PREFIX"-shared"
+#define LIBXL_QEMU_USER_RANGE_BASE LIBXL_QEMU_USER_PREFIX"-range-base"
+#define LIBXL_QEMU_USER_REAPER LIBXL_QEMU_USER_PREFIX"-reaper"
+
+static inline bool libxl__acpi_defbool_val(const libxl_domain_build_info *b_info)
+{
+    return libxl_defbool_val(b_info->acpi) &&
+           libxl_defbool_val(b_info->u.hvm.acpi);
+}
+
+/*
+ * Add a device in libxl_domain_config structure
+ *
+ * If there is already a device with the same identifier in d_config,
+ * that entry is updated.
+ *
+ * parameters:
+ *  d_config: pointer to template domain config
+ *  dt:       type of `dev'
+ *  dev:      the device that is to be added / removed / updated
+ *            (a copy of `dev' will be made)
+ */
+void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
+                              const libxl__device_type *dt,
+                              const void *dev);
+
+void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
+                             const libxl__device_type *dt, void *type,
+                             libxl__ao_device *aodev);
+int libxl__device_add(libxl__gc *gc, uint32_t domid,
+                      const libxl__device_type *dt, void *type);
+
+/* Caller is responsible for freeing the memory by calling
+ * libxl__device_list_free
+ */
+void* libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
+                         uint32_t domid, int *num);
+void libxl__device_list_free(const libxl__device_type *dt,
+                             void *list, int num);
+
+static inline bool libxl__timer_mode_is_default(libxl_timer_mode *tm)
+{
+    return *tm == LIBXL_TIMER_MODE_DEFAULT;
+}
+
+static inline bool libxl__string_is_default(char **s)
+{
+    return *s == NULL;
+}
+
+_hidden int libxl__prepare_sockaddr_un(libxl__gc *gc, struct sockaddr_un *un,
+                                       const char *path, const char *what);
+static inline const char *libxl__qemu_qmp_path(libxl__gc *gc, int domid)
+{
+    return GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
+}
+
+/* Send control commands over xenstore and wait for an Ack. */
+_hidden int libxl__domain_pvcontrol(libxl__egc *egc,
+                                    libxl__xswait_state *pvcontrol,
+                                    domid_t domid, const char *cmd);
+
+/*
+ * Maximum number of seconds after desctruction then a domid remains
+ * 'recent'. Recent domids are not allowed to be re-used. This can be
+ * overidden, for debugging purposes, by the environment variable of the
+ * same name.
+ */
+#define LIBXL_DOMID_REUSE_TIMEOUT 60
+
+/* Check whether a domid is recent */
+int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent);
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_json.c b/tools/libs/light/libxl_json.c
new file mode 100644 (file)
index 0000000..9b8ef2c
--- /dev/null
@@ -0,0 +1,1191 @@
+/*
+ * Copyright (C) 2011      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <math.h>
+
+#include <yajl/yajl_parse.h>
+#include <yajl/yajl_gen.h>
+
+#include "libxl_internal.h"
+
+/* #define DEBUG_ANSWER */
+
+typedef struct libxl__yajl_ctx {
+    libxl__gc *gc;
+    yajl_handle hand;
+    libxl__json_object *head;
+    libxl__json_object *current;
+#ifdef DEBUG_ANSWER
+    yajl_gen g;
+#endif
+} libxl__yajl_ctx;
+
+#ifdef DEBUG_ANSWER
+#if YAJL_VERSION < 20000
+#  define DEBUG_GEN_ALLOC(ctx)                  \
+    if ((ctx)->g == NULL) {                     \
+        yajl_gen_config conf = { 1, "  " };     \
+        (ctx)->g = yajl_gen_alloc(&conf, NULL); \
+    }
+#else  /* YAJL2 */
+#  define DEBUG_GEN_ALLOC(ctx)                                    \
+    if ((ctx)->g == NULL) {                                       \
+        (ctx)->g = yajl_gen_alloc(NULL);                          \
+        yajl_gen_config((ctx)->g, yajl_gen_beautify, 1);          \
+        yajl_gen_config((ctx)->g, yajl_gen_indent_string, "  ");  \
+    }
+#endif
+#  define DEBUG_GEN_FREE(ctx) \
+    if ((ctx)->g) yajl_gen_free((ctx)->g)
+#  define DEBUG_GEN(ctx, type)              yajl_gen_##type(ctx->g)
+#  define DEBUG_GEN_VALUE(ctx, type, value) yajl_gen_##type(ctx->g, value)
+#  define DEBUG_GEN_STRING(ctx, str, n)     yajl_gen_string(ctx->g, str, n)
+#  define DEBUG_GEN_NUMBER(ctx, str, n)     yajl_gen_number(ctx->g, str, n)
+#  define DEBUG_GEN_REPORT(yajl_ctx) \
+    do { \
+        const unsigned char *buf = NULL; \
+        size_t len = 0; \
+        yajl_gen_get_buf((yajl_ctx)->g, &buf, &len); \
+        LIBXL__LOG(libxl__gc_owner((yajl_ctx)->gc), XTL_DEBUG, \
+                  "response: %s\n", buf); \
+        yajl_gen_free((yajl_ctx)->g); \
+        (yajl_ctx)->g = NULL; \
+    } while (0)
+#else
+#  define DEBUG_GEN_ALLOC(ctx)                  ((void)0)
+#  define DEBUG_GEN_FREE(ctx)                   ((void)0)
+#  define DEBUG_GEN(ctx, type)                  ((void)0)
+#  define DEBUG_GEN_VALUE(ctx, type, value)     ((void)0)
+#  define DEBUG_GEN_STRING(ctx, value, length)  ((void)0)
+#  define DEBUG_GEN_NUMBER(ctx, value, length)  ((void)0)
+#  define DEBUG_GEN_REPORT(ctx)                 ((void)0)
+#endif
+
+/*
+ * YAJL Helper
+ */
+
+yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str)
+{
+    return yajl_gen_string(hand, (const unsigned char *)str, strlen(str));
+}
+
+yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str)
+{
+    if (str)
+        return libxl__yajl_gen_asciiz(hand, str);
+    else
+        return yajl_gen_null(hand);
+}
+
+/*
+ * YAJL generators for builtin libxl types.
+ */
+yajl_gen_status libxl_defbool_gen_json(yajl_gen hand,
+                                       libxl_defbool *db)
+{
+    return libxl__yajl_gen_asciiz(hand, libxl_defbool_to_string(*db));
+}
+
+int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                              libxl_defbool *p)
+{
+    const char *s;
+
+    if (!libxl__json_object_is_string(o))
+        return ERROR_FAIL;
+
+    s = libxl__json_object_get_string(o);
+
+    if (!strncmp(s, LIBXL__DEFBOOL_STR_DEFAULT,
+                 strlen(LIBXL__DEFBOOL_STR_DEFAULT)))
+        p->val = LIBXL__DEFBOOL_DEFAULT;
+    else if (!strncmp(s, LIBXL__DEFBOOL_STR_TRUE,
+                      strlen(LIBXL__DEFBOOL_STR_TRUE)))
+        p->val = LIBXL__DEFBOOL_TRUE;
+    else if (!strncmp(s, LIBXL__DEFBOOL_STR_FALSE,
+                      strlen(LIBXL__DEFBOOL_STR_FALSE)))
+        p->val = LIBXL__DEFBOOL_FALSE;
+    else
+        return ERROR_FAIL;
+
+    return 0;
+}
+
+int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                           bool *p)
+{
+    if (!libxl__json_object_is_bool(o))
+        return ERROR_FAIL;
+
+    *p = libxl__json_object_get_bool(o);
+
+    return 0;
+}
+
+yajl_gen_status libxl_uuid_gen_json(yajl_gen hand,
+                                    libxl_uuid *uuid)
+{
+    char buf[LIBXL_UUID_FMTLEN+1];
+    snprintf(buf, sizeof(buf), LIBXL_UUID_FMT, LIBXL_UUID_BYTES((*uuid)));
+    return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_UUID_FMTLEN);
+}
+
+int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                           libxl_uuid *p)
+{
+    if (!libxl__json_object_is_string(o))
+        return ERROR_FAIL;
+
+    return libxl_uuid_from_string(p, o->u.string);
+}
+
+yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand,
+                                      libxl_bitmap *bitmap)
+{
+    yajl_gen_status s;
+    int i;
+
+    s = yajl_gen_array_open(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    libxl_for_each_bit(i, *bitmap) {
+        if (libxl_bitmap_test(bitmap, i)) {
+            s = yajl_gen_integer(hand, i);
+            if (s != yajl_gen_status_ok) goto out;
+        }
+    }
+    s = yajl_gen_array_close(hand);
+out:
+    return s;
+}
+
+int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                            libxl_bitmap *p)
+{
+    int i;
+    int size;
+    const libxl__json_object *t;
+    flexarray_t *array;
+
+    if (!libxl__json_object_is_array(o))
+        return ERROR_FAIL;
+
+    array = libxl__json_object_get_array(o);
+    if (!array->count) {
+        libxl_bitmap_init(p);
+        return 0;
+    }
+
+    t = libxl__json_array_get(o, array->count - 1);
+    if (!libxl__json_object_is_integer(t))
+        return ERROR_FAIL;
+    size = libxl__json_object_get_integer(t) + 1;
+
+    libxl_bitmap_alloc(CTX, p, size);
+
+    for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
+        if (!libxl__json_object_is_integer(t))
+            return ERROR_FAIL;
+
+        libxl_bitmap_set(p, libxl__json_object_get_integer(t));
+    }
+
+    return 0;
+}
+
+yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
+                                              libxl_key_value_list *pkvl)
+{
+    libxl_key_value_list kvl = *pkvl;
+    yajl_gen_status s;
+    int i;
+
+    s = yajl_gen_map_open(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    if (!kvl) goto empty;
+
+    for (i = 0; kvl[i] != NULL; i += 2) {
+        s = libxl__yajl_gen_asciiz(hand, kvl[i]);
+        if (s != yajl_gen_status_ok) goto out;
+        if (kvl[i + 1])
+            s = libxl__yajl_gen_asciiz(hand, kvl[i+1]);
+        else
+            s = yajl_gen_null(hand);
+        if (s != yajl_gen_status_ok) goto out;
+    }
+empty:
+    s = yajl_gen_map_close(hand);
+out:
+    return s;
+}
+
+int libxl__key_value_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                                     libxl_key_value_list *p)
+{
+    libxl__json_map_node *node = NULL;
+    flexarray_t *maps = NULL;
+    int i, size;
+    libxl_key_value_list kvl;
+
+    if (!libxl__json_object_is_map(o))
+        return ERROR_FAIL;
+
+    maps = libxl__json_object_get_map(o);
+    size = maps->count * 2;
+    kvl = *p = libxl__calloc(NOGC, size+1, sizeof(char *));
+
+    for (i = 0; i < maps->count; i++) {
+        int idx = i * 2;
+        if (flexarray_get(maps, i, (void**)&node) != 0)
+            return ERROR_FAIL;
+
+        if (!libxl__json_object_is_string(node->obj) &&
+            !libxl__json_object_is_null(node->obj))
+            return ERROR_FAIL;
+
+        kvl[idx] = libxl__strdup(NOGC, node->map_key);
+        if (libxl__json_object_is_string(node->obj))
+            kvl[idx+1] =
+                libxl__strdup(NOGC, libxl__json_object_get_string(node->obj));
+        else
+            kvl[idx+1] = NULL;
+    }
+
+    return 0;
+}
+
+yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *pl)
+{
+    libxl_string_list l = *pl;
+    yajl_gen_status s;
+    int i;
+
+    s = yajl_gen_array_open(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    if (!l) goto empty;
+
+    for (i = 0; l[i] != NULL; i++) {
+        s = libxl__yajl_gen_asciiz(hand, l[i]);
+        if (s != yajl_gen_status_ok) goto out;
+    }
+empty:
+    s = yajl_gen_array_close(hand);
+out:
+    return s;
+}
+
+int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                                  libxl_string_list *p)
+{
+    const libxl__json_object *t;
+    libxl_string_list l;
+    flexarray_t *array = NULL;
+    int i, size;
+
+    if (!libxl__json_object_is_array(o))
+        return ERROR_FAIL;
+
+    array = libxl__json_object_get_array(o);
+    size = array->count;
+
+    if (size == 0) {
+        *p = NULL;
+        return 0;
+    }
+
+    /* need one extra slot as sentinel */
+    l = *p = libxl__calloc(NOGC, size + 1, sizeof(char *));
+
+    for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
+        if (!libxl__json_object_is_string(t))
+            return ERROR_FAIL;
+
+        l[i] = libxl__strdup(NOGC, libxl__json_object_get_string(t));
+    }
+
+    return 0;
+}
+
+yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *mac)
+{
+    char buf[LIBXL_MAC_FMTLEN+1];
+    snprintf(buf, sizeof(buf), LIBXL_MAC_FMT, LIBXL_MAC_BYTES((*mac)));
+    return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_MAC_FMTLEN);
+}
+
+int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                          libxl_mac *p)
+{
+    if (!libxl__json_object_is_string(o))
+        return ERROR_FAIL;
+
+    return libxl__parse_mac(libxl__json_object_get_string(o), *p);
+}
+
+yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand,
+                                     libxl_hwcap *p)
+{
+    yajl_gen_status s;
+    int i;
+
+    s = yajl_gen_array_open(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    for(i=0; i<4; i++) {
+        s = yajl_gen_integer(hand, (*p)[i]);
+        if (s != yajl_gen_status_ok) goto out;
+    }
+    s = yajl_gen_array_close(hand);
+out:
+    return s;
+}
+
+int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                            libxl_hwcap *p)
+{
+    int i;
+
+    if (!libxl__json_object_is_array(o))
+        return ERROR_FAIL;
+
+    for (i = 0; i<4; i++) {
+        const libxl__json_object *t;
+
+        t = libxl__json_array_get(o, i);
+        if (!t || !libxl__json_object_is_integer(t))
+            return ERROR_FAIL;
+
+        (*p)[i] = libxl__json_object_get_integer(t);
+    }
+
+    return 0;
+}
+
+yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p)
+{
+    yajl_gen_status s;
+    int i;
+
+    s = yajl_gen_array_open(hand);
+    if (s != yajl_gen_status_ok)
+        return s;
+
+    for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
+        s = yajl_gen_integer(hand, p->bytes[i]);
+        if (s != yajl_gen_status_ok)
+            return s;
+    }
+
+    return yajl_gen_array_close(hand);
+}
+
+int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                                  libxl_ms_vm_genid *p)
+{
+    unsigned int i;
+
+    if (!libxl__json_object_is_array(o))
+        return ERROR_FAIL;
+
+    for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
+        const libxl__json_object *t;
+
+        t = libxl__json_array_get(o, i);
+        if (!t || !libxl__json_object_is_integer(t))
+            return ERROR_FAIL;
+
+        p->bytes[i] = libxl__json_object_get_integer(t);
+    }
+
+    return 0;
+}
+
+yajl_gen_status libxl__string_gen_json(yajl_gen hand,
+                                       const char *p)
+{
+    if (p)
+        return libxl__yajl_gen_asciiz(hand, p);
+    else
+        return yajl_gen_null(hand);
+}
+
+int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                             char **p)
+{
+    if (!libxl__json_object_is_string(o) && !libxl__json_object_is_null(o))
+        return ERROR_FAIL;
+
+    if (libxl__json_object_is_null(o))
+        *p = NULL;
+    else
+        *p = libxl__strdup(NOGC, libxl__json_object_get_string(o));
+
+    return 0;
+}
+
+/*
+ * libxl__json_object helper functions
+ */
+
+libxl__json_object *libxl__json_object_alloc(libxl__gc *gc,
+                                             libxl__json_node_type type)
+{
+    libxl__json_object *obj;
+
+    obj = libxl__zalloc(gc, sizeof(*obj));
+
+    obj->type = type;
+
+    if (type == JSON_MAP || type == JSON_ARRAY) {
+        flexarray_t *array = flexarray_make(gc, 1, 1);
+        if (type == JSON_MAP)
+            obj->u.map = array;
+        else
+            obj->u.array = array;
+    }
+
+    return obj;
+}
+
+static int libxl__json_object_append_to(libxl__gc *gc,
+                                        libxl__json_object *obj,
+                                        libxl__yajl_ctx *ctx)
+{
+    libxl__json_object *dst = ctx->current;
+
+    if (dst) {
+        switch (dst->type) {
+        case JSON_MAP: {
+            libxl__json_map_node *last;
+
+            if (dst->u.map->count == 0) {
+                LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
+                           "Try to add a value to an empty map (with no key)");
+                return ERROR_FAIL;
+            }
+            flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last);
+            last->obj = obj;
+            break;
+        }
+        case JSON_ARRAY:
+            flexarray_append(dst->u.array, obj);
+            break;
+        default:
+            LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
+                       "Try append an object is not a map/array (%i)",
+                       dst->type);
+            return ERROR_FAIL;
+        }
+    }
+
+    obj->parent = dst;
+
+    if (libxl__json_object_is_map(obj) || libxl__json_object_is_array(obj))
+        ctx->current = obj;
+    if (ctx->head == NULL)
+        ctx->head = obj;
+
+    return 0;
+}
+
+void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj)
+{
+    int idx = 0;
+
+    if (obj == NULL)
+        return;
+    switch (obj->type) {
+    case JSON_STRING:
+    case JSON_NUMBER:
+        free(obj->u.string);
+        break;
+    case JSON_MAP: {
+        libxl__json_map_node *node = NULL;
+
+        for (idx = 0; idx < obj->u.map->count; idx++) {
+            if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
+                break;
+            libxl__json_object_free(gc, node->obj);
+            free(node->map_key);
+            free(node);
+            node = NULL;
+        }
+        flexarray_free(obj->u.map);
+        break;
+    }
+    case JSON_ARRAY: {
+        libxl__json_object *node = NULL;
+
+        for (idx = 0; idx < obj->u.array->count; idx++) {
+            if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
+                break;
+            libxl__json_object_free(gc, node);
+            node = NULL;
+        }
+        flexarray_free(obj->u.array);
+        break;
+    }
+    default:
+        break;
+    }
+    free(obj);
+}
+
+libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i)
+{
+    flexarray_t *array = NULL;
+    libxl__json_object *obj = NULL;
+
+    if ((array = libxl__json_object_get_array(o)) == NULL) {
+        return NULL;
+    }
+
+    if (i >= array->count)
+        return NULL;
+
+    if (flexarray_get(array, i, (void**)&obj) != 0)
+        return NULL;
+
+    return obj;
+}
+
+libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
+                                               int i)
+{
+    flexarray_t *array = NULL;
+    libxl__json_map_node *obj = NULL;
+
+    if ((array = libxl__json_object_get_map(o)) == NULL) {
+        return NULL;
+    }
+
+    if (i >= array->count)
+        return NULL;
+
+    if (flexarray_get(array, i, (void**)&obj) != 0)
+        return NULL;
+
+    return obj;
+}
+
+const libxl__json_object *libxl__json_map_get(const char *key,
+                                          const libxl__json_object *o,
+                                          libxl__json_node_type expected_type)
+{
+    flexarray_t *maps = NULL;
+    int idx = 0;
+
+    if (libxl__json_object_is_map(o)) {
+        libxl__json_map_node *node = NULL;
+
+        maps = o->u.map;
+        for (idx = 0; idx < maps->count; idx++) {
+            if (flexarray_get(maps, idx, (void**)&node) != 0)
+                return NULL;
+            if (strcmp(key, node->map_key) == 0) {
+                if (expected_type == JSON_ANY
+                    || (node->obj && (node->obj->type & expected_type))) {
+                    return node->obj;
+                } else {
+                    return NULL;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc,
+                                           yajl_gen hand,
+                                           const libxl__json_object *obj)
+{
+    int idx = 0;
+    yajl_status rc;
+
+#define CONVERT_YAJL_GEN_TO_STATUS(gen) \
+    ((gen) == yajl_gen_status_ok ? yajl_status_ok : yajl_status_error)
+
+    switch (obj->type) {
+    case JSON_NULL:
+        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_null(hand));
+    case JSON_BOOL:
+        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_bool(hand, obj->u.b));
+    case JSON_INTEGER:
+        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_integer(hand, obj->u.i));
+    case JSON_DOUBLE:
+        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_double(hand, obj->u.d));
+    case JSON_NUMBER:
+        return CONVERT_YAJL_GEN_TO_STATUS(
+                  yajl_gen_number(hand, obj->u.string, strlen(obj->u.string)));
+    case JSON_STRING:
+        return CONVERT_YAJL_GEN_TO_STATUS(
+                        libxl__yajl_gen_asciiz(hand, obj->u.string));
+    case JSON_MAP: {
+        libxl__json_map_node *node = NULL;
+
+        rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_open(hand));
+        if (rc != yajl_status_ok)
+            return rc;
+        for (idx = 0; idx < obj->u.map->count; idx++) {
+            if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
+                break;
+
+            rc = CONVERT_YAJL_GEN_TO_STATUS(
+                            libxl__yajl_gen_asciiz(hand, node->map_key));
+            if (rc != yajl_status_ok)
+                return rc;
+            rc = libxl__json_object_to_yajl_gen(gc, hand, node->obj);
+            if (rc != yajl_status_ok)
+                return rc;
+        }
+        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_close(hand));
+    }
+    case JSON_ARRAY: {
+        libxl__json_object *node = NULL;
+
+        rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_open(hand));
+        if (rc != yajl_status_ok)
+            return rc;
+        for (idx = 0; idx < obj->u.array->count; idx++) {
+            if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
+                break;
+            rc = libxl__json_object_to_yajl_gen(gc, hand, node);
+            if (rc != yajl_status_ok)
+                return rc;
+        }
+        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_close(hand));
+    }
+    case JSON_ANY:
+        /* JSON_ANY is not a valid value for obj->type. */
+        ;
+    }
+    abort();
+#undef CONVERT_YAJL_GEN_TO_STATUS
+}
+
+
+/*
+ * JSON callbacks
+ */
+
+static int json_callback_null(void *opaque)
+{
+    libxl__yajl_ctx *ctx = opaque;
+    libxl__json_object *obj;
+
+    DEBUG_GEN(ctx, null);
+
+    obj = libxl__json_object_alloc(ctx->gc, JSON_NULL);
+
+    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+        return 0;
+
+    return 1;
+}
+
+static int json_callback_boolean(void *opaque, int boolean)
+{
+    libxl__yajl_ctx *ctx = opaque;
+    libxl__json_object *obj;
+
+    DEBUG_GEN_VALUE(ctx, bool, boolean);
+
+    obj = libxl__json_object_alloc(ctx->gc, JSON_BOOL);
+    obj->u.b = boolean;
+
+    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+        return 0;
+
+    return 1;
+}
+
+static bool is_decimal(const char *s, unsigned len)
+{
+    const char *end = s + len;
+    for (; s < end; s++) {
+        if (*s == '.')
+            return true;
+    }
+    return false;
+}
+
+static int json_callback_number(void *opaque, const char *s, libxl_yajl_length len)
+{
+    libxl__yajl_ctx *ctx = opaque;
+    libxl__json_object *obj = NULL;
+    char *t = NULL;
+
+    DEBUG_GEN_NUMBER(ctx, s, len);
+
+    if (is_decimal(s, len)) {
+        double d = strtod(s, NULL);
+
+        if ((d == HUGE_VALF || d == HUGE_VALL) && errno == ERANGE) {
+            goto error;
+        }
+
+        obj = libxl__json_object_alloc(ctx->gc, JSON_DOUBLE);
+        obj->u.d = d;
+    } else {
+        long long i = strtoll(s, NULL, 10);
+
+        if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) {
+            goto error;
+        }
+
+        obj = libxl__json_object_alloc(ctx->gc, JSON_INTEGER);
+        obj->u.i = i;
+    }
+    goto out;
+
+error:
+    /* If the conversion fail, we just store the original string. */
+    obj = libxl__json_object_alloc(ctx->gc, JSON_NUMBER);
+
+    t = libxl__zalloc(ctx->gc, len + 1);
+    strncpy(t, s, len);
+    t[len] = 0;
+
+    obj->u.string = t;
+
+out:
+    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+        return 0;
+
+    return 1;
+}
+
+static int json_callback_string(void *opaque, const unsigned char *str,
+                                libxl_yajl_length len)
+{
+    libxl__yajl_ctx *ctx = opaque;
+    char *t = NULL;
+    libxl__json_object *obj = NULL;
+
+    t = libxl__zalloc(ctx->gc, len + 1);
+
+    DEBUG_GEN_STRING(ctx, str, len);
+
+    strncpy(t, (const char *) str, len);
+    t[len] = 0;
+
+    obj = libxl__json_object_alloc(ctx->gc, JSON_STRING);
+    obj->u.string = t;
+
+    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+        return 0;
+
+    return 1;
+}
+
+static int json_callback_map_key(void *opaque, const unsigned char *str,
+                                 libxl_yajl_length len)
+{
+    libxl__yajl_ctx *ctx = opaque;
+    char *t = NULL;
+    libxl__json_object *obj = ctx->current;
+    libxl__gc *gc = ctx->gc;
+
+    t = libxl__zalloc(gc, len + 1);
+
+    DEBUG_GEN_STRING(ctx, str, len);
+
+    strncpy(t, (const char *) str, len);
+    t[len] = 0;
+
+    if (libxl__json_object_is_map(obj)) {
+        libxl__json_map_node *node;
+
+        GCNEW(node);
+        node->map_key = t;
+        node->obj = NULL;
+
+        flexarray_append(obj->u.map, node);
+    } else {
+        LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
+                   "Current json object is not a map");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int json_callback_start_map(void *opaque)
+{
+    libxl__yajl_ctx *ctx = opaque;
+    libxl__json_object *obj = NULL;
+
+    DEBUG_GEN(ctx, map_open);
+
+    obj = libxl__json_object_alloc(ctx->gc, JSON_MAP);
+
+    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+        return 0;
+
+    return 1;
+}
+
+static int json_callback_end_map(void *opaque)
+{
+    libxl__yajl_ctx *ctx = opaque;
+
+    DEBUG_GEN(ctx, map_close);
+
+    if (ctx->current) {
+        ctx->current = ctx->current->parent;
+    } else {
+        LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
+                   "No current libxl__json_object, cannot use his parent.");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int json_callback_start_array(void *opaque)
+{
+    libxl__yajl_ctx *ctx = opaque;
+    libxl__json_object *obj = NULL;
+
+    DEBUG_GEN(ctx, array_open);
+
+    obj = libxl__json_object_alloc(ctx->gc, JSON_ARRAY);
+
+    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
+        return 0;
+
+    return 1;
+}
+
+static int json_callback_end_array(void *opaque)
+{
+    libxl__yajl_ctx *ctx = opaque;
+
+    DEBUG_GEN(ctx, array_close);
+
+    if (ctx->current) {
+        ctx->current = ctx->current->parent;
+    } else {
+        LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
+                   "No current libxl__json_object, cannot use his parent.");
+        return 0;
+    }
+
+    return 1;
+}
+
+static yajl_callbacks callbacks = {
+    json_callback_null,
+    json_callback_boolean,
+    NULL,
+    NULL,
+    json_callback_number,
+    json_callback_string,
+    json_callback_start_map,
+    json_callback_map_key,
+    json_callback_end_map,
+    json_callback_start_array,
+    json_callback_end_array
+};
+
+static void yajl_ctx_free(libxl__yajl_ctx *yajl_ctx)
+{
+    if (yajl_ctx->hand) {
+        yajl_free(yajl_ctx->hand);
+        yajl_ctx->hand = NULL;
+    }
+    DEBUG_GEN_FREE(yajl_ctx);
+}
+
+libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s)
+{
+    yajl_status status;
+    libxl__yajl_ctx yajl_ctx;
+    libxl__json_object *o = NULL;
+    unsigned char *str = NULL;
+
+    memset(&yajl_ctx, 0, sizeof (yajl_ctx));
+    yajl_ctx.gc = gc;
+
+    DEBUG_GEN_ALLOC(&yajl_ctx);
+
+    if (yajl_ctx.hand == NULL) {
+        yajl_ctx.hand = libxl__yajl_alloc(&callbacks, NULL, &yajl_ctx);
+    }
+    status = yajl_parse(yajl_ctx.hand, (const unsigned char *)s, strlen(s));
+    if (status != yajl_status_ok)
+        goto out;
+
+    status = yajl_complete_parse(yajl_ctx.hand);
+    if (status != yajl_status_ok)
+        goto out;
+
+    o = yajl_ctx.head;
+
+    DEBUG_GEN_REPORT(&yajl_ctx);
+
+    yajl_ctx.head = NULL;
+
+    yajl_ctx_free(&yajl_ctx);
+    return o;
+
+out:
+    str = yajl_get_error(yajl_ctx.hand, 1, (const unsigned char*)s, strlen(s));
+
+    LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "yajl error: %s", str);
+    yajl_free_error(yajl_ctx.hand, str);
+    yajl_ctx_free(&yajl_ctx);
+    return NULL;
+}
+
+static const char *yajl_gen_status_to_string(yajl_gen_status s)
+{
+        switch (s) {
+        case yajl_gen_status_ok: abort();
+        case yajl_gen_keys_must_be_strings:
+            return "keys must be strings";
+        case yajl_max_depth_exceeded:
+            return "max depth exceeded";
+        case yajl_gen_in_error_state:
+            return "in error state";
+        case yajl_gen_generation_complete:
+            return "generation complete";
+        case yajl_gen_invalid_number:
+            return "invalid number";
+#if 0 /* This is in the docs but not implemented in the version I am running. */
+        case yajl_gen_no_buf:
+            return "no buffer";
+        case yajl_gen_invalid_string:
+            return "invalid string";
+#endif
+        default:
+            return "unknown error";
+        }
+}
+
+char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
+                            libxl__gen_json_callback gen, void *p)
+{
+    const unsigned char *buf;
+    char *ret = NULL;
+    libxl_yajl_length len = 0;
+    yajl_gen_status s;
+    yajl_gen hand;
+
+    hand = libxl_yajl_gen_alloc(NULL);
+    if (!hand)
+        return NULL;
+
+    s = gen(hand, p);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_get_buf(hand, &buf, &len);
+    if (s != yajl_gen_status_ok)
+        goto out;
+    ret = strdup((const char *)buf);
+
+out:
+    yajl_gen_free(hand);
+
+    if (s != yajl_gen_status_ok) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
+                   "unable to convert %s to JSON representation. "
+                   "YAJL error code %d: %s", type,
+                   s, yajl_gen_status_to_string(s));
+    } else if (!ret) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
+                   "unable to allocate space for to JSON representation of %s",
+                   type);
+    }
+
+    return ret;
+}
+
+char *libxl__json_object_to_json(libxl__gc *gc,
+                                 const libxl__json_object *args)
+{
+    const unsigned char *buf;
+    libxl_yajl_length len;
+    yajl_gen_status s;
+    yajl_gen hand;
+    char *ret = NULL;
+    int rc;
+
+    if (!args)
+        return NULL;
+
+    hand = libxl_yajl_gen_alloc(NULL);
+    if (!hand)
+        return NULL;
+
+    rc = libxl__json_object_to_yajl_gen(gc, hand, args);
+    if (rc)
+        goto out;
+
+    s = yajl_gen_get_buf(hand, &buf, &len);
+    if (s)
+        goto out;
+
+    ret = libxl__strndup(gc, (const char *)buf, len);
+
+out:
+    yajl_gen_free(hand);
+    return ret;
+}
+
+yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val)
+{
+    char *num;
+    int len;
+    yajl_gen_status s;
+
+
+    len = asprintf(&num, "%"PRIu64, val);
+    if (len == -1) {
+        s = yajl_gen_in_error_state;
+        goto out;
+    }
+
+    s = yajl_gen_number(hand, num, len);
+
+    free(num);
+
+out:
+    return s;
+}
+
+int libxl__object_from_json(libxl_ctx *ctx, const char *type,
+                            libxl__json_parse_callback parse,
+                            void *p, const char *s)
+{
+    GC_INIT(ctx);
+    libxl__json_object *o;
+    int rc;
+
+    o = libxl__json_parse(gc, s);
+    if (!o) {
+        LOG(ERROR,
+            "unable to generate libxl__json_object from JSON representation of %s.",
+            type);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = parse(gc, o, p);
+    if (rc) {
+        LOG(ERROR, "unable to convert libxl__json_object to %s. (rc=%d)", type, rc);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                          void *p)
+{
+    long long i;
+
+    if (!libxl__json_object_is_integer(o))
+        return ERROR_FAIL;
+
+    i = libxl__json_object_get_integer(o);
+
+    if (i > INT_MAX || i < INT_MIN)
+        return ERROR_FAIL;
+
+    *((int *)p) = i;
+
+    return 0;
+}
+
+/* Macro to generate:
+ *  libxl__uint8_parse_json
+ *  libxl__uint16_parse_json
+ *  libxl__uint32_parse_json
+ */
+#define PARSE_UINT(width)                                               \
+    int libxl__uint ## width ## _parse_json(libxl__gc *gc,              \
+                                            const libxl__json_object *o,\
+                                            void *p)                    \
+    {                                                                   \
+        long long i;                                                    \
+                                                                        \
+        if (!libxl__json_object_is_integer(o))                          \
+            return ERROR_FAIL;                                          \
+                                                                        \
+        i = libxl__json_object_get_integer(o);                          \
+                                                                        \
+        if (i < 0 || i > UINT ## width ## _MAX)                         \
+            return ERROR_FAIL;                                          \
+                                                                        \
+        *((uint ## width ## _t *)p) = i;                                \
+                                                                        \
+        return 0;                                                       \
+    }
+
+PARSE_UINT(8);
+PARSE_UINT(16);
+PARSE_UINT(32);
+
+int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
+                             void *p)
+{
+    if (!libxl__json_object_is_integer(o) &&
+        !libxl__json_object_is_number(o))
+        return ERROR_FAIL;
+
+    if (libxl__json_object_is_integer(o)) {
+        long long i = libxl__json_object_get_integer(o);
+
+        if (i < 0)
+            return ERROR_FAIL;
+
+        *((uint64_t *)p) = i;
+    } else {
+        const char *s;
+        unsigned long long i;
+        int saved_errno = errno;
+
+        s = libxl__json_object_get_number(o);
+
+        errno = 0;
+        i = strtoull(s, NULL, 10);
+
+        if (i == ULLONG_MAX && errno == ERANGE)
+            return ERROR_FAIL;
+
+        errno = saved_errno;
+        *((uint64_t *)p) = i;
+    }
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_libfdt_compat.c b/tools/libs/light/libxl_libfdt_compat.c
new file mode 100644 (file)
index 0000000..02b8f74
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * This file is part of libxl, and was originally taken from libfdt.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ *
+ * Additionally, this particular file is dual licensed.  That is,
+ * alternatively, at your option:
+ *
+ *      Redistribution and use in source and binary forms, with or
+ *      without modification, are permitted provided that the following
+ *      conditions are met:
+ *
+ *      1. Redistributions of source code must retain the above
+ *         copyright notice, this list of conditions and the following
+ *         disclaimer.
+ *      2. Redistributions in binary form must reproduce the above
+ *         copyright notice, this list of conditions and the following
+ *         disclaimer in the documentation and/or other materials
+ *         provided with the distribution.
+ *
+ *      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ *      CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ *      INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *      MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *      DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ *      CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *      SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *      NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *      HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ *      OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ *      EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Note that this applies only to this file, and other files with a
+ * similar notice.  Also, note that when the same code is distributed
+ * along with the rest of libxl, you must comply with the terms of the
+ * LGPLv2.1 for the whole of libxl including this file.
+ *
+ * The intent is to permit, in particular, upstream libfdt to
+ * incorporate improvements to this file within upstream libfdt.  At
+ * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
+ * (as above) and GPLv2-or-later.  The 2-clause BSD licence is
+ * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
+ * copying in both directions, and the optional licence upgrade to a
+ * copyleft licence by libdft upstream or the Xen Project,
+ * respectively.
+ */
+
+#include <libfdt.h>
+
+#include "libxl_libfdt_compat.h"
+
+#ifndef HAVE_FDT_FIRST_SUBNODE
+_hidden int fdt_first_subnode(const void *fdt, int offset)
+{
+       int depth = 0;
+
+       offset = fdt_next_node(fdt, offset, &depth);
+       if (offset < 0 || depth != 1)
+               return -FDT_ERR_NOTFOUND;
+
+       return offset;
+}
+#endif
+
+#ifndef HAVE_FDT_NEXT_SUBNODE
+_hidden int fdt_next_subnode(const void *fdt, int offset)
+{
+       int depth = 1;
+
+       /*
+        * With respect to the parent, the depth of the next subnode will be
+        * the same as the last.
+        */
+       do {
+               offset = fdt_next_node(fdt, offset, &depth);
+               if (offset < 0 || depth < 1)
+                       return -FDT_ERR_NOTFOUND;
+       } while (depth > 1);
+
+       return offset;
+}
+#endif
diff --git a/tools/libs/light/libxl_libfdt_compat.h b/tools/libs/light/libxl_libfdt_compat.h
new file mode 100644 (file)
index 0000000..23230b5
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * This file is part of libxl, and was originally taken from libfdt.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ *
+ * Additionally, this particular file is dual licensed.  That is,
+ * alternatively, at your option:
+ *
+ *      Redistribution and use in source and binary forms, with or
+ *      without modification, are permitted provided that the following
+ *      conditions are met:
+ *
+ *      1. Redistributions of source code must retain the above
+ *         copyright notice, this list of conditions and the following
+ *         disclaimer.
+ *      2. Redistributions in binary form must reproduce the above
+ *         copyright notice, this list of conditions and the following
+ *         disclaimer in the documentation and/or other materials
+ *         provided with the distribution.
+ *
+ *      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ *      CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ *      INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *      MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *      DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ *      CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *      SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *      NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *      HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ *      OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ *      EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Note that this applies only to this file, and other files with a
+ * similar notice.  Also, note that when the same code is distributed
+ * along with the rest of libxl, you must comply with the terms of the
+ * LGPLv2.1 for the whole of libxl including this file.
+ *
+ * The intent is to permit, in particular, upstream libfdt to
+ * incorporate improvements to this file within upstream libfdt.  At
+ * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
+ * (as above) and GPLv2-or-later.  The 2-clause BSD licence is
+ * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
+ * copying in both directions, and the optional licence upgrade to a
+ * copyleft licence by libdft upstream or the Xen Project,
+ * respectively.
+ */
+
+#ifndef LIBXL_LIBFDT_COMPAT_H
+#define LIBXL_LIBFDT_COMPAT_H
+
+#include "libxl_internal.h"
+#include <libfdt.h>
+
+#if !HAVE_DECL_FDT_FIRST_SUBNODE
+_hidden int fdt_first_subnode(const void *fdt, int offset);
+#endif
+
+#if !HAVE_DECL_FDT_NEXT_SUBNODE
+_hidden int fdt_next_subnode(const void *fdt, int offset);
+#endif
+
+#if !HAVE_DECL_FDT_PROPERTY_U32
+static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
+{
+       uint32_t tmp = cpu_to_fdt32(val);
+       return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+#endif
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_linux.c b/tools/libs/light/libxl_linux.c
new file mode 100644 (file)
index 0000000..873b027
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2011
+ * Author Roger Pau Monne <roger.pau@entel.upc.edu>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <sys/resource.h>
+#include "libxl_internal.h"
+
+
+/* Workarounds for Linux-specific lacks can go here: */
+
+#ifndef CLONE_NEWIPC /* Available as of Linux 2.6.19 / glibc 2.8 */
+# define CLONE_NEWIPC 0x08000000
+#endif
+
+
+int libxl__try_phy_backend(mode_t st_mode)
+{
+    if (S_ISBLK(st_mode) || S_ISREG(st_mode)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
+{
+    return libxl__devid_to_vdev(gc, devid);
+}
+
+/* Hotplug scripts helpers */
+
+static char **get_hotplug_env(libxl__gc *gc,
+                              char *script, libxl__device *dev)
+{
+    const char *type = libxl__device_kind_to_string(dev->backend_kind);
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char **env;
+    int nr = 0;
+
+    const int arraysize = 15;
+    GCNEW_ARRAY(env, arraysize);
+    env[nr++] = "script";
+    env[nr++] = script;
+    env[nr++] = "XENBUS_TYPE";
+    env[nr++] = (char *) type;
+    env[nr++] = "XENBUS_PATH";
+    env[nr++] = GCSPRINTF("backend/%s/%u/%d", type, dev->domid, dev->devid);
+    env[nr++] = "XENBUS_BASE_PATH";
+    env[nr++] = "backend";
+    if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) {
+        libxl_nic_type nictype;
+        char *gatewaydev;
+
+        gatewaydev = libxl__xs_read(gc, XBT_NULL,
+                                    GCSPRINTF("%s/%s", be_path, "gatewaydev"));
+        env[nr++] = "netdev";
+        env[nr++] = gatewaydev ? : "";
+
+        if (libxl__nic_type(gc, dev, &nictype)) {
+            LOGD(ERROR, dev->domid, "unable to get nictype");
+            return NULL;
+        }
+        switch (nictype) {
+        case LIBXL_NIC_TYPE_VIF_IOEMU:
+            env[nr++] = "INTERFACE";
+            env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
+                                                    dev->devid,
+                                                    LIBXL_NIC_TYPE_VIF_IOEMU);
+            /*
+             * We need to fall through because for PV_IOEMU nic types we need
+             * to execute both the vif and the tap hotplug script, and we
+             * don't know which one we are executing in this call, so provide
+             * both env variables.
+             */
+        case LIBXL_NIC_TYPE_VIF:
+            env[nr++] = "vif";
+            env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
+                                                    dev->devid,
+                                                    LIBXL_NIC_TYPE_VIF);
+            break;
+        default:
+            return NULL;
+        }
+    }
+
+    env[nr++] = NULL;
+    assert(nr <= arraysize);
+
+    return env;
+}
+
+/* Hotplug scripts caller functions */
+
+static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
+                               char ***args, char ***env,
+                               libxl__device_action action, int num_exec)
+{
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *script;
+    int nr = 0, rc = 0;
+    libxl_nic_type nictype;
+
+    script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path,
+                                                             "script"));
+    if (!script) {
+        LOGED(ERROR, dev->domid,
+              "unable to read script from %s", be_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__nic_type(gc, dev, &nictype);
+    if (rc) {
+        LOGD(ERROR, dev->domid, "error when fetching nic type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
+        rc = 0;
+        goto out;
+    }
+
+    *env = get_hotplug_env(gc, script, dev);
+    if (!*env) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    const int arraysize = 4;
+    GCNEW_ARRAY(*args, arraysize);
+    (*args)[nr++] = script;
+
+    if (nictype == LIBXL_NIC_TYPE_VIF_IOEMU && num_exec) {
+        (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+        (*args)[nr++] = "type_if=tap";
+        (*args)[nr++] = NULL;
+    } else {
+        (*args)[nr++] = action == LIBXL__DEVICE_ACTION_ADD ? "online" :
+                                                             "offline";
+        (*args)[nr++] = "type_if=vif";
+        (*args)[nr++] = NULL;
+    }
+    assert(nr == arraysize);
+    rc = 1;
+
+out:
+    return rc;
+}
+
+static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
+                               char ***args, char ***env,
+                               libxl__device_action action)
+{
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *script;
+    int nr = 0, rc = 0;
+
+    script = libxl__xs_read(gc, XBT_NULL,
+                            GCSPRINTF("%s/%s", be_path, "script"));
+    if (!script) {
+        LOGEVD(ERROR, errno, dev->domid,
+               "unable to read script from %s", be_path);
+        rc = ERROR_FAIL;
+        goto error;
+    }
+
+    *env = get_hotplug_env(gc, script, dev);
+    if (!*env) {
+        LOGD(ERROR, dev->domid, "Failed to get hotplug environment");
+        rc = ERROR_FAIL;
+        goto error;
+    }
+
+    const int arraysize = 3;
+    GCNEW_ARRAY(*args, arraysize);
+    (*args)[nr++] = script;
+    (*args)[nr++] = (char *) libxl__device_action_to_string(action);
+    (*args)[nr++] = NULL;
+    assert(nr == arraysize);
+
+    LOGD(DEBUG, dev->domid, "Args and environment ready");
+    rc = 1;
+
+error:
+    return rc;
+}
+
+int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+                                   char ***args, char ***env,
+                                   libxl__device_action action,
+                                   int num_exec)
+{
+    int rc;
+
+    switch (dev->backend_kind) {
+    case LIBXL__DEVICE_KIND_VBD:
+        if (num_exec != 0) {
+            LOGD(DEBUG, dev->domid,
+                 "num_exec %d, not running hotplug scripts", num_exec);
+            rc = 0;
+            goto out;
+        }
+        rc = libxl__hotplug_disk(gc, dev, args, env, action);
+        break;
+    case LIBXL__DEVICE_KIND_VIF:
+        /*
+         * If domain has a stubdom we don't have to execute hotplug scripts
+         * for emulated interfaces
+         */
+        if ((num_exec > 1) ||
+            (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
+            LOGD(DEBUG, dev->domid,
+                 "num_exec %d, not running hotplug scripts", num_exec);
+            rc = 0;
+            goto out;
+        }
+        rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
+        break;
+    default:
+        /* No need to execute any hotplug scripts */
+        LOGD(DEBUG, dev->domid,
+             "backend_kind %d, no need to execute scripts", dev->backend_kind);
+        rc = 0;
+        break;
+    }
+
+out:
+    return rc;
+}
+
+libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
+{
+    return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
+}
+
+int libxl__pci_numdevs(libxl__gc *gc)
+{
+    DIR *dir;
+    struct dirent *entry;
+    int num_devs = 0;
+
+    dir = opendir("/sys/bus/pci/devices");
+    if (!dir) {
+        LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
+        return ERROR_FAIL;
+    }
+
+    while ((entry = readdir(dir))) {
+        if (entry->d_name[0] == '.')
+            continue;
+        num_devs++;
+    }
+    closedir(dir);
+
+    return num_devs;
+}
+
+int libxl__pci_topology_init(libxl__gc *gc,
+                             physdev_pci_device_t *devs,
+                             int num_devs)
+{
+
+    DIR *dir;
+    struct dirent *entry;
+    int i, err = 0;
+
+    dir = opendir("/sys/bus/pci/devices");
+    if (!dir) {
+        LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
+        return ERROR_FAIL;
+    }
+
+    i = 0;
+    while ((entry = readdir(dir))) {
+        unsigned int dom, bus, dev, func;
+
+        if (entry->d_name[0] == '.')
+            continue;
+
+        if (i == num_devs) {
+            LOG(ERROR, "Too many devices");
+            err = ERROR_FAIL;
+            errno = -ENOSPC;
+            goto out;
+        }
+
+        if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4) {
+            LOGE(ERROR, "Error processing /sys/bus/pci/devices");
+            err = ERROR_FAIL;
+            goto out;
+        }
+
+        devs[i].seg = dom;
+        devs[i].bus = bus;
+        devs[i].devfn = ((dev & 0x1f) << 3) | (func & 7);
+
+        i++;
+    }
+
+ out:
+    closedir(dir);
+
+    return err;
+}
+
+static struct {
+    int resource;
+    rlim_t limit;
+} rlimits[] = {
+#define RLIMIT_ENTRY(r, l) \
+    { .resource = r, .limit = l }
+    /* Big enough for log files, not big enough for a DoS */
+    RLIMIT_ENTRY(RLIMIT_FSIZE,    256*1024),
+
+    /* Shouldn't need any of these */
+    RLIMIT_ENTRY(RLIMIT_CORE,     0),
+    RLIMIT_ENTRY(RLIMIT_MSGQUEUE, 0),
+    RLIMIT_ENTRY(RLIMIT_LOCKS,    0),
+    RLIMIT_ENTRY(RLIMIT_MEMLOCK,  0),
+
+    /* End-of-list marker */
+    RLIMIT_ENTRY(RLIMIT_NLIMITS,  0),
+#undef RLIMIT_ENTRY
+};
+
+int libxl__local_dm_preexec_restrict(libxl__gc *gc)
+{
+    int r;
+    unsigned i;
+
+    /* Unshare mount and IPC namespaces.  These are unused by QEMU. */
+    r = unshare(CLONE_NEWNS | CLONE_NEWIPC);
+    if (r) {
+        LOGE(ERROR, "libxl: unshare Mount and IPC namespace failed");
+        return ERROR_FAIL;
+    }
+
+    /* Set various "easy" rlimits */
+    for (i = 0; rlimits[i].resource != RLIMIT_NLIMITS; i++) {
+        struct rlimit rlim;
+
+        rlim.rlim_cur = rlim.rlim_max = rlimits[i].limit;
+
+        r = setrlimit(rlimits[i].resource, &rlim);
+        if (r < 0) {
+            LOGE(ERROR, "Setting rlimit %d to %llu failed\n",
+                                  rlimits[i].resource,
+                                  (unsigned long long)rlimits[i].limit);
+            return ERROR_FAIL;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_mem.c b/tools/libs/light/libxl_mem.c
new file mode 100644 (file)
index 0000000..e52a962
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+/*
+ * Set the maximum memory size of the domain in the hypervisor. There is no
+ * change of the current memory size involved. The specified memory size can
+ * even be above the configured maxmem size of the domain, but the related
+ * Xenstore entry memory/static-max isn't modified!
+ */
+int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb)
+{
+    GC_INIT(ctx);
+    char *mem, *endptr;
+    uint64_t memorykb, size;
+    char *dompath = libxl__xs_get_dompath(gc, domid);
+    int rc = 1;
+    libxl__flock *lock = NULL;
+    libxl_domain_config d_config;
+
+    libxl_domain_config_init(&d_config);
+
+    CTX_LOCK;
+
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    mem = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath));
+    if (!mem) {
+        LOGED(ERROR, domid, "Cannot get memory info from %s/memory/target",
+              dompath);
+        goto out;
+    }
+    memorykb = strtoull(mem, &endptr, 10);
+    if (*endptr != '\0') {
+        LOGED(ERROR, domid, "Invalid memory %s from %s/memory/target\n",
+              mem, dompath);
+        goto out;
+    }
+
+    if (max_memkb < memorykb) {
+        LOGED(ERROR, domid,
+              "memory_static_max must be greater than or or equal to memory_dynamic_max");
+        goto out;
+    }
+
+    rc = libxl__get_domain_configuration(gc, domid, &d_config);
+    if (rc < 0) {
+        LOGE(ERROR, "unable to retrieve domain configuration");
+        goto out;
+    }
+
+    rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
+    if (rc < 0) {
+        LOGE(ERROR, "Couldn't get arch extra constant memory size");
+        goto out;
+    }
+
+    rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + size);
+    if (rc != 0) {
+        LOGED(ERROR, domid,
+              "xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n",
+              domid, max_memkb + size, rc);
+        goto out;
+    }
+
+    rc = 0;
+out:
+    libxl_domain_config_dispose(&d_config);
+    if (lock) libxl__unlock_file(lock);
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
+                                        uint64_t *max_memkb)
+{
+    int rc;
+    libxl_dominfo info;
+    libxl_physinfo physinfo;
+    char *target = NULL, *staticmax = NULL, *endptr = NULL;
+    char *target_path = "/local/domain/0/memory/target";
+    char *max_path = "/local/domain/0/memory/static-max";
+    xs_transaction_t t;
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+
+    libxl_dominfo_init(&info);
+
+retry_transaction:
+    t = xs_transaction_start(ctx->xsh);
+
+    target = libxl__xs_read(gc, t, target_path);
+    staticmax = libxl__xs_read(gc, t, max_path);
+    if (target && staticmax) {
+        rc = 0;
+        goto out;
+    }
+
+    if (target) {
+        *target_memkb = strtoull(target, &endptr, 10);
+        if (*endptr != '\0') {
+            LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target,
+                 target_path);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    if (staticmax) {
+        *max_memkb = strtoull(staticmax, &endptr, 10);
+        if (*endptr != '\0') {
+            LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n",
+                 staticmax,
+                 max_path);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    libxl_dominfo_dispose(&info);
+    libxl_dominfo_init(&info);
+    rc = libxl_domain_info(ctx, &info, 0);
+    if (rc < 0)
+        goto out;
+
+    rc = libxl_get_physinfo(ctx, &physinfo);
+    if (rc < 0)
+        goto out;
+
+    if (target == NULL) {
+        libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
+        *target_memkb = info.current_memkb;
+    }
+    if (staticmax == NULL) {
+        libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
+        *max_memkb = info.max_memkb;
+    }
+
+    rc = 0;
+
+out:
+    if (!xs_transaction_end(ctx->xsh, t, 0)) {
+        if (errno == EAGAIN)
+            goto retry_transaction;
+        else
+            rc = ERROR_FAIL;
+    }
+
+    libxl_dominfo_dispose(&info);
+    return rc;
+}
+
+int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid,
+        int64_t target_memkb, int relative, int enforce)
+{
+    GC_INIT(ctx);
+    int rc, r, lrc, abort_transaction = 0;
+    uint64_t memorykb, size;
+    uint64_t videoram = 0;
+    uint64_t current_target_memkb = 0, new_target_memkb = 0;
+    uint64_t current_max_memkb = 0;
+    char *memmax, *endptr, *videoram_s = NULL, *target = NULL;
+    char *dompath = libxl__xs_get_dompath(gc, domid);
+    xc_domaininfo_t info;
+    libxl_dominfo ptr;
+    char *uuid;
+    xs_transaction_t t;
+    libxl__flock *lock;
+    libxl_domain_config d_config;
+
+    libxl_domain_config_init(&d_config);
+
+    CTX_LOCK;
+
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out_no_transaction;
+    }
+
+    rc = libxl__get_domain_configuration(gc, domid, &d_config);
+    if (rc < 0) {
+        LOGE(ERROR, "unable to retrieve domain configuration");
+        goto out_no_transaction;
+    }
+
+    rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
+    if (rc < 0) {
+        LOGE(ERROR, "Couldn't get arch extra constant memory size");
+        goto out_no_transaction;
+    }
+
+retry_transaction:
+    t = xs_transaction_start(ctx->xsh);
+
+    target = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/target", dompath));
+    if (!target && !domid) {
+        if (!xs_transaction_end(ctx->xsh, t, 1)) {
+            rc = ERROR_FAIL;
+            goto out_no_transaction;
+        }
+        lrc = libxl__fill_dom0_memory_info(gc, &current_target_memkb,
+                                           &current_max_memkb);
+        if (lrc < 0) { rc = ERROR_FAIL; goto out_no_transaction; }
+        goto retry_transaction;
+    } else if (!target) {
+        LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
+              dompath);
+        abort_transaction = 1;
+        rc = ERROR_FAIL;
+        goto out;
+    } else {
+        current_target_memkb = strtoull(target, &endptr, 10);
+        if (*endptr != '\0') {
+            LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
+                  target, dompath);
+            abort_transaction = 1;
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+    memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath));
+    if (!memmax) {
+        LOGED(ERROR, domid, "Cannot get memory info from %s/memory/static-max",
+              dompath);
+        abort_transaction = 1;
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    memorykb = strtoull(memmax, &endptr, 10);
+    if (*endptr != '\0') {
+        LOGED(ERROR, domid, "Invalid max memory %s from %s/memory/static-max\n",
+             memmax, dompath);
+        abort_transaction = 1;
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    videoram_s = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/videoram",
+                                                 dompath));
+    videoram = videoram_s ? atoi(videoram_s) : 0;
+
+    if (relative) {
+        if (target_memkb < 0 && llabs(target_memkb) > current_target_memkb)
+            new_target_memkb = 0;
+        else
+            new_target_memkb = current_target_memkb + target_memkb;
+    } else
+        new_target_memkb = target_memkb - videoram;
+    if (new_target_memkb > memorykb) {
+        LOGD(ERROR, domid,
+             "memory_dynamic_max must be less than or equal to"
+             " memory_static_max\n");
+        abort_transaction = 1;
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
+        LOGD(ERROR, domid,
+             "New target %"PRIu64" for dom0 is below the minimum threshold",
+             new_target_memkb);
+        abort_transaction = 1;
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    if (enforce) {
+        memorykb = new_target_memkb + videoram;
+        r = xc_domain_setmaxmem(ctx->xch, domid, memorykb + size);
+        if (r != 0) {
+            LOGED(ERROR, domid,
+                  "xc_domain_setmaxmem memkb=%"PRIu64" failed ""rc=%d\n",
+                  memorykb + size,
+                  r);
+            abort_transaction = 1;
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    if (d_config.c_info.type != LIBXL_DOMAIN_TYPE_PV) {
+        r = xc_domain_set_pod_target(ctx->xch, domid,
+                (new_target_memkb + size) / 4, NULL, NULL, NULL);
+        if (r != 0) {
+            LOGED(ERROR, domid,
+                  "xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n",
+                  (new_target_memkb + size) / 4,
+                  r);
+            abort_transaction = 1;
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
+                     "%"PRIu64, new_target_memkb);
+
+    r = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
+    if (r != 1 || info.domain != domid) {
+        abort_transaction = 1;
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl_dominfo_init(&ptr);
+    libxl__xcinfo2xlinfo(ctx, &info, &ptr);
+    uuid = libxl__uuid2string(gc, ptr.uuid);
+    libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
+                     "%"PRIu64, new_target_memkb / 1024);
+    libxl_dominfo_dispose(&ptr);
+
+    rc = 0;
+out:
+    if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
+        && !abort_transaction)
+        if (errno == EAGAIN)
+            goto retry_transaction;
+
+out_no_transaction:
+    libxl_domain_config_dispose(&d_config);
+    if (lock) libxl__unlock_file(lock);
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+/* out_target_memkb and out_max_memkb can be NULL */
+int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
+                             uint64_t *out_target_memkb,
+                             uint64_t *out_max_memkb)
+{
+    int rc;
+    char *target = NULL, *static_max = NULL, *endptr = NULL;
+    char *dompath = libxl__xs_get_dompath(gc, domid);
+    uint64_t target_memkb, max_memkb;
+
+    target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
+                                                    dompath));
+    static_max = libxl__xs_read(gc, XBT_NULL,
+                    GCSPRINTF("%s/memory/static-max", dompath));
+
+    rc = ERROR_FAIL;
+    if ((!target || !static_max) && !domid) {
+        rc = libxl__fill_dom0_memory_info(gc, &target_memkb,
+                                          &max_memkb);
+        if (rc < 0)
+            goto out;
+    } else if (!target) {
+        LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
+              dompath);
+        goto out;
+    } else if (!static_max) {
+        LOGED(ERROR, domid,
+              "Cannot get target memory info from %s/memory/static-max",
+               dompath);
+        goto out;
+    } else {
+        target_memkb = strtoull(target, &endptr, 10);
+        if (*endptr != '\0') {
+            LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
+                  target, dompath);
+            goto out;
+        }
+        max_memkb = strtoull(static_max, &endptr, 10);
+        if (*endptr != '\0') {
+            LOGED(ERROR, domid,
+                  "Invalid memory target %s from %s/memory/static-max\n",
+                  static_max,
+                  dompath);
+            goto out;
+        }
+
+    }
+
+    if (out_target_memkb)
+        *out_target_memkb = target_memkb;
+
+    if (out_max_memkb)
+        *out_max_memkb = max_memkb;
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
+                               uint64_t val64, uint32_t *ptr32)
+{
+    GC_INIT(ctx);
+
+    if (rc)
+        goto out;
+
+    *ptr32 = val64;
+    if (*ptr32 == val64)
+        goto out;
+
+    LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
+    rc = ERROR_FAIL;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
+                            uint64_t *out_target)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = libxl__get_memory_target(gc, domid, out_target, NULL);
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_get_memory_target_0x040700(
+    libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
+{
+    uint64_t my_out_target;
+    int rc;
+
+    rc = libxl_get_memory_target(ctx, domid, &my_out_target);
+    return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
+}
+
+int libxl__domain_need_memory_calculate(libxl__gc *gc,
+                              libxl_domain_build_info *b_info,
+                              uint64_t *need_memkb)
+{
+    int rc;
+
+    *need_memkb = b_info->target_memkb;
+    *need_memkb += b_info->shadow_memkb + b_info->iommu_memkb;
+
+    switch (b_info->type) {
+    case LIBXL_DOMAIN_TYPE_PVH:
+    case LIBXL_DOMAIN_TYPE_HVM:
+        *need_memkb += LIBXL_HVM_EXTRA_MEMORY;
+        if (libxl_defbool_val(b_info->device_model_stubdomain)) {
+            *need_memkb += b_info->stubdomain_memkb;
+            *need_memkb += b_info->video_memkb;
+        }
+        break;
+    case LIBXL_DOMAIN_TYPE_PV:
+        *need_memkb += LIBXL_PV_EXTRA_MEMORY;
+        break;
+    default:
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    if (*need_memkb % (2 * 1024))
+        *need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024));
+    rc = 0;
+out:
+    return rc;
+}
+
+int libxl_domain_need_memory(libxl_ctx *ctx,
+                             libxl_domain_config *d_config,
+                             uint32_t domid_for_logging,
+                             uint64_t *need_memkb)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    ctx->libxl_domain_need_memory_called = 1;
+
+    rc = libxl__domain_config_setdefault(gc,
+                                         d_config,
+                                         domid_for_logging);
+    if (rc) goto out;
+
+    rc = libxl__domain_need_memory_calculate(gc,
+                                   &d_config->b_info,
+                                   need_memkb);
+    if (rc) goto out;
+
+    rc = 0;
+ out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
+                                      const libxl_domain_build_info *b_info_in,
+                                      uint64_t *need_memkb)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    ctx->libxl_domain_need_memory_0x041200_called = 1;
+
+    libxl_domain_build_info b_info[1];
+    libxl_domain_build_info_init(b_info);
+    libxl_domain_build_info_copy(ctx, b_info, b_info_in);
+
+    rc = libxl__domain_build_info_setdefault(gc, b_info);
+    if (rc) goto out;
+
+    rc = libxl__domain_need_memory_calculate(gc,
+                                   b_info,
+                                   need_memkb);
+    if (rc) goto out;
+
+    rc = 0;
+ out:
+    libxl_domain_build_info_dispose(b_info);
+    GC_FREE;
+    return rc;
+}
+
+int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
+                                      const libxl_domain_build_info *b_info_in,
+                                      uint32_t *need_memkb)
+{
+    uint64_t my_need_memkb;
+    int rc;
+
+    rc = libxl_domain_need_memory_0x041200(ctx, b_info_in, &my_need_memkb);
+    return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
+}
+
+int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
+{
+    int rc = 0;
+    libxl_physinfo info;
+    GC_INIT(ctx);
+
+    rc = libxl_get_physinfo(ctx, &info);
+    if (rc < 0)
+        goto out;
+
+    *memkb = (info.free_pages + info.scrub_pages) * 4;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
+{
+    uint64_t my_memkb;
+    int rc;
+
+    rc = libxl_get_free_memory(ctx, &my_memkb);
+    return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
+}
+
+int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
+                               uint64_t memory_kb, int wait_secs)
+{
+    int rc = 0;
+    libxl_physinfo info;
+    GC_INIT(ctx);
+
+    while (wait_secs > 0) {
+        rc = libxl_get_physinfo(ctx, &info);
+        if (rc < 0)
+            goto out;
+        if (info.free_pages * 4 >= memory_kb) {
+            rc = 0;
+            goto out;
+        }
+        wait_secs--;
+        sleep(1);
+    }
+    rc = ERROR_NOMEM;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
+{
+    int rc = 0;
+    uint64_t target_memkb = 0;
+    uint64_t current_memkb, prev_memkb;
+    libxl_dominfo info;
+
+    rc = libxl_get_memory_target(ctx, domid, &target_memkb);
+    if (rc < 0)
+        return rc;
+
+    libxl_dominfo_init(&info);
+    prev_memkb = UINT64_MAX;
+
+    do {
+        sleep(2);
+
+        libxl_dominfo_dispose(&info);
+        libxl_dominfo_init(&info);
+        rc = libxl_domain_info(ctx, &info, domid);
+        if (rc < 0)
+            goto out;
+
+        current_memkb = info.current_memkb + info.outstanding_memkb;
+
+        if (current_memkb > prev_memkb)
+        {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        else if (current_memkb == prev_memkb)
+            wait_secs -= 2;
+        /* if current_memkb < prev_memkb loop for free as progress has
+         * been made */
+
+        prev_memkb = current_memkb;
+    } while (wait_secs > 0 && current_memkb > target_memkb);
+
+    if (current_memkb <= target_memkb)
+        rc = 0;
+    else
+        rc = ERROR_FAIL;
+
+out:
+    libxl_dominfo_dispose(&info);
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_netbsd.c b/tools/libs/light/libxl_netbsd.c
new file mode 100644 (file)
index 0000000..e66a393
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011
+ * Author Roger Pau Monne <roger.pau@entel.upc.edu>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__try_phy_backend(mode_t st_mode)
+{
+    if (S_ISREG(st_mode) || S_ISBLK(st_mode))
+        return 1;
+
+    return 0;
+}
+
+char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
+{
+    /* TODO */
+    return NULL;
+}
+
+/* Hotplug scripts caller functions */
+static int libxl__hotplug(libxl__gc *gc, libxl__device *dev, char ***args,
+                          libxl__device_action action)
+{
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *script;
+    int nr = 0, rc = 0, arraysize = 4;
+
+    script = libxl__xs_read(gc, XBT_NULL,
+                            GCSPRINTF("%s/%s", be_path, "script"));
+    if (!script) {
+        LOGEVD(ERROR, errno, dev->domid,
+               "unable to read script from %s", be_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    GCNEW_ARRAY(*args, arraysize);
+    (*args)[nr++] = script;
+    (*args)[nr++] = be_path;
+    (*args)[nr++] = GCSPRINTF("%d", action == LIBXL__DEVICE_ACTION_ADD ?
+                                    XenbusStateInitWait : XenbusStateClosed);
+    (*args)[nr++] = NULL;
+    assert(nr == arraysize);
+
+out:
+    return rc;
+}
+
+int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
+                                   char ***args, char ***env,
+                                   libxl__device_action action,
+                                   int num_exec)
+{
+    int rc;
+
+    switch (dev->backend_kind) {
+    case LIBXL__DEVICE_KIND_VBD:
+        if (num_exec != 0) {
+            LOGD(DEBUG, dev->domid,
+                 "num_exec %d, not running hotplug scripts", num_exec);
+            rc = 0;
+            goto out;
+        }
+        rc = libxl__hotplug(gc, dev, args, action);
+        if (!rc) rc = 1;
+        break;
+    case LIBXL__DEVICE_KIND_VIF:
+        /*
+         * If domain has a stubdom we don't have to execute hotplug scripts
+         * for emulated interfaces
+         *
+         * NetBSD let QEMU call a script to plug emulated nic, so
+         * only test if num_exec == 0 in that case.
+         */
+        if ((num_exec != 0) ||
+            (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
+            LOGD(DEBUG, dev->domid,
+                 "num_exec %d, not running hotplug scripts", num_exec);
+            rc = 0;
+            goto out;
+        }
+        rc = libxl__hotplug(gc, dev, args, action);
+        if (!rc) rc = 1;
+        break;
+    default:
+        /* If no need to execute any hotplug scripts,
+         * call the callback manually
+         */
+        rc = 0;
+        break;
+    }
+
+out:
+    return rc;
+}
+
+libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
+{
+    return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
+}
+
+int libxl__pci_numdevs(libxl__gc *gc)
+{
+    return ERROR_NI;
+}
+
+int libxl__pci_topology_init(libxl__gc *gc,
+                             physdev_pci_device_t *devs,
+                             int num_devs)
+{
+    return ERROR_NI;
+}
+
+int libxl__local_dm_preexec_restrict(libxl__gc *gc)
+{
+    return 0;
+}
diff --git a/tools/libs/light/libxl_netbuffer.c b/tools/libs/light/libxl_netbuffer.c
new file mode 100644 (file)
index 0000000..4b21914
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2014
+ * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#include <netlink/cache.h>
+#include <netlink/socket.h>
+#include <netlink/attr.h>
+#include <netlink/route/link.h>
+#include <netlink/route/route.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc/plug.h>
+
+typedef struct libxl__remus_device_nic {
+    int devid;
+
+    const char *vif;
+    const char *ifb;
+    struct rtnl_qdisc *qdisc;
+} libxl__remus_device_nic;
+
+int libxl__netbuffer_enabled(libxl__gc *gc)
+{
+    return 1;
+}
+
+int init_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+    int rc, ret;
+    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+    libxl__remus_state *rs = cds->concrete_data;
+
+    STATE_AO_GC(cds->ao);
+
+    rs->nlsock = nl_socket_alloc();
+    if (!rs->nlsock) {
+        LOGD(ERROR, dss->domid, "cannot allocate nl socket");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    ret = nl_connect(rs->nlsock, NETLINK_ROUTE);
+    if (ret) {
+        LOGD(ERROR, dss->domid, "failed to open netlink socket: %s",
+             nl_geterror(ret));
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* get list of all qdiscs installed on network devs. */
+    ret = rtnl_qdisc_alloc_cache(rs->nlsock, &rs->qdisc_cache);
+    if (ret) {
+        LOGD(ERROR, dss->domid, "failed to allocate qdisc cache: %s",
+             nl_geterror(ret));
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (dss->remus->netbufscript) {
+        rs->netbufscript = libxl__strdup(gc, dss->remus->netbufscript);
+    } else {
+        rs->netbufscript = GCSPRINTF("%s/remus-netbuf-setup",
+                                     libxl__xen_script_dir_path());
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+    libxl__remus_state *rs = cds->concrete_data;
+
+    STATE_AO_GC(cds->ao);
+
+    /* free qdisc cache */
+    if (rs->qdisc_cache) {
+        nl_cache_clear(rs->qdisc_cache);
+        nl_cache_free(rs->qdisc_cache);
+        rs->qdisc_cache = NULL;
+    }
+
+    /* close & free nlsock */
+    if (rs->nlsock) {
+        nl_close(rs->nlsock);
+        nl_socket_free(rs->nlsock);
+        rs->nlsock = NULL;
+    }
+}
+
+/*----- setup() and teardown() -----*/
+
+/* helper functions */
+
+/*
+ * If the device has a vifname, then use that instead of
+ * the vifX.Y format.
+ * it must ONLY be used for remus because if driver domains
+ * were in use it would constitute a security vulnerability.
+ */
+static const char *get_vifname(libxl__checkpoint_device *dev,
+                               const libxl_device_nic *nic)
+{
+    const char *vifname = NULL;
+    const char *path;
+    int rc;
+
+    STATE_AO_GC(dev->cds->ao);
+
+    /* Convenience aliases */
+    const uint32_t domid = dev->cds->domid;
+
+    path = GCSPRINTF("%s/vifname",
+                     libxl__domain_device_backend_path(gc, 0, domid,
+                     nic->devid, LIBXL__DEVICE_KIND_VIF));
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
+    if (!rc && !vifname) {
+        vifname = libxl__device_nic_devname(gc, domid,
+                                            nic->devid,
+                                            nic->nictype);
+    }
+
+    return vifname;
+}
+
+static void free_qdisc(libxl__remus_device_nic *remus_nic)
+{
+    if (remus_nic->qdisc == NULL)
+        return;
+
+    nl_object_put((struct nl_object *)(remus_nic->qdisc));
+    remus_nic->qdisc = NULL;
+}
+
+static int init_qdisc(libxl__checkpoint_devices_state *cds,
+                      libxl__remus_device_nic *remus_nic)
+{
+    int rc, ret, ifindex;
+    struct rtnl_link *ifb = NULL;
+    struct rtnl_qdisc *qdisc = NULL;
+    libxl__remus_state *rs = cds->concrete_data;
+
+    STATE_AO_GC(cds->ao);
+
+    /* Now that we have brought up REMUS_IFB device with plug qdisc for
+     * this vif, so we need to refill the qdisc cache.
+     */
+    ret = nl_cache_refill(rs->nlsock, rs->qdisc_cache);
+    if (ret) {
+        LOGD(ERROR, cds->domid,
+             "cannot refill qdisc cache: %s", nl_geterror(ret));
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* get a handle to the REMUS_IFB interface */
+    ret = rtnl_link_get_kernel(rs->nlsock, 0, remus_nic->ifb, &ifb);
+    if (ret) {
+        LOGD(ERROR, cds->domid,
+             "cannot obtain handle for %s: %s", remus_nic->ifb,
+            nl_geterror(ret));
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    ifindex = rtnl_link_get_ifindex(ifb);
+    if (!ifindex) {
+        LOGD(ERROR, cds->domid,
+             "interface %s has no index", remus_nic->ifb);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* Get a reference to the root qdisc installed on the REMUS_IFB, by
+     * querying the qdisc list we obtained earlier. The netbufscript
+     * sets up the plug qdisc as the root qdisc, so we don't have to
+     * search the entire qdisc tree on the REMUS_IFB dev.
+
+     * There is no need to explicitly free this qdisc as its just a
+     * reference from the qdisc cache we allocated earlier.
+     */
+    qdisc = rtnl_qdisc_get_by_parent(rs->qdisc_cache, ifindex, TC_H_ROOT);
+    if (qdisc) {
+        const char *tc_kind = rtnl_tc_get_kind(TC_CAST(qdisc));
+        /* Sanity check: Ensure that the root qdisc is a plug qdisc. */
+        if (!tc_kind || strcmp(tc_kind, "plug")) {
+            LOGD(ERROR, cds->domid,
+                 "plug qdisc is not installed on %s", remus_nic->ifb);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        remus_nic->qdisc = qdisc;
+    } else {
+        LOGD(ERROR, cds->domid,
+             "Cannot get qdisc handle from ifb %s", remus_nic->ifb);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    if (ifb)
+        rtnl_link_put(ifb);
+
+    if (rc && qdisc)
+        nl_object_put((struct nl_object *)qdisc);
+
+    return rc;
+}
+
+/* callbacks */
+
+static void netbuf_setup_script_cb(libxl__egc *egc,
+                                   libxl__async_exec_state *aes,
+                                   int rc, int status);
+static void netbuf_teardown_script_cb(libxl__egc *egc,
+                                      libxl__async_exec_state *aes,
+                                      int rc, int status);
+
+/*
+ * the script needs the following env & args
+ * $vifname
+ * $XENBUS_PATH (/libxl/<domid>/remus/netbuf/<devid>/)
+ * $REMUS_IFB (for teardown)
+ * setup/teardown as command line arg.
+ */
+static void setup_async_exec(libxl__checkpoint_device *dev, char *op)
+{
+    int arraysize, nr = 0;
+    char **env = NULL, **args = NULL;
+    libxl__remus_device_nic *remus_nic = dev->concrete_data;
+    libxl__checkpoint_devices_state *cds = dev->cds;
+    libxl__async_exec_state *aes = &dev->aodev.aes;
+    libxl__remus_state *rs = cds->concrete_data;
+
+    STATE_AO_GC(cds->ao);
+
+    /* Convenience aliases */
+    char *const script = libxl__strdup(gc, rs->netbufscript);
+    const uint32_t domid = cds->domid;
+    const int dev_id = remus_nic->devid;
+    const char *const vif = remus_nic->vif;
+    const char *const ifb = remus_nic->ifb;
+
+    arraysize = 7;
+    GCNEW_ARRAY(env, arraysize);
+    env[nr++] = "vifname";
+    env[nr++] = libxl__strdup(gc, vif);
+    env[nr++] = "XENBUS_PATH";
+    env[nr++] = GCSPRINTF("%s/remus/netbuf/%d",
+                          libxl__xs_libxl_path(gc, domid), dev_id);
+    if (!strcmp(op, "teardown") && ifb) {
+        env[nr++] = "REMUS_IFB";
+        env[nr++] = libxl__strdup(gc, ifb);
+    }
+    env[nr++] = NULL;
+    assert(nr <= arraysize);
+
+    arraysize = 3; nr = 0;
+    GCNEW_ARRAY(args, arraysize);
+    args[nr++] = script;
+    args[nr++] = op;
+    args[nr++] = NULL;
+    assert(nr == arraysize);
+
+    aes->ao = dev->cds->ao;
+    aes->what = GCSPRINTF("%s %s", args[0], args[1]);
+    aes->env = env;
+    aes->args = args;
+    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+    aes->stdfds[0] = -1;
+    aes->stdfds[1] = -1;
+    aes->stdfds[2] = -1;
+
+    if (!strcmp(op, "teardown"))
+        aes->callback = netbuf_teardown_script_cb;
+    else
+        aes->callback = netbuf_setup_script_cb;
+}
+
+/* setup() and teardown() */
+
+static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    int rc;
+    libxl__remus_device_nic *remus_nic;
+    const libxl_device_nic *nic = dev->backend_dev;
+
+    STATE_AO_GC(dev->cds->ao);
+
+    /*
+     * thers's no subkind of nic devices, so nic ops is always matched
+     * with nic devices
+     */
+    dev->matched = true;
+
+    GCNEW(remus_nic);
+    dev->concrete_data = remus_nic;
+    remus_nic->devid = nic->devid;
+    remus_nic->vif = get_vifname(dev, nic);
+    if (!remus_nic->vif) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    setup_async_exec(dev, "setup");
+    rc = libxl__async_exec_start(&dev->aodev.aes);
+    if (rc)
+        goto out;
+
+    return;
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+/*
+ * In return, the script writes the name of REMUS_IFB device (during setup)
+ * to be used for output buffering into XENBUS_PATH/ifb
+ */
+static void netbuf_setup_script_cb(libxl__egc *egc,
+                                   libxl__async_exec_state *aes,
+                                   int rc, int status)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+    libxl__remus_device_nic *remus_nic = dev->concrete_data;
+    libxl__checkpoint_devices_state *cds = dev->cds;
+    libxl__remus_state *rs = cds->concrete_data;
+    const char *out_path_base, *hotplug_error = NULL;
+
+    STATE_AO_GC(cds->ao);
+
+    /* Convenience aliases */
+    const uint32_t domid = cds->domid;
+    const int devid = remus_nic->devid;
+    const char *const vif = remus_nic->vif;
+    const char **const ifb = &remus_nic->ifb;
+
+    if (status && !rc)
+        rc = ERROR_FAIL;
+    if (rc)
+        goto out;
+
+    /*
+     * we need to get ifb first because it's needed for teardown
+     */
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/remus/netbuf/%d/ifb",
+                                          libxl__xs_libxl_path(gc, domid),
+                                          devid),
+                                ifb);
+    if (rc)
+        goto out;
+
+    if (!(*ifb)) {
+        LOGD(ERROR, domid, "Cannot get ifb dev name for domain %u dev %s",
+             domid, vif);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    out_path_base = GCSPRINTF("%s/remus/netbuf/%d",
+                              libxl__xs_libxl_path(gc, domid), devid);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/hotplug-error", out_path_base),
+                                &hotplug_error);
+    if (rc)
+        goto out;
+
+    if (hotplug_error) {
+        LOGD(ERROR, domid, "netbuf script %s setup failed for vif %s: %s",
+             rs->netbufscript, vif, hotplug_error);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (status) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    LOGD(DEBUG, domid, "%s will buffer packets from vif %s", *ifb, vif);
+    rc = init_qdisc(cds, remus_nic);
+
+out:
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+static void nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    int rc;
+    STATE_AO_GC(dev->cds->ao);
+
+    setup_async_exec(dev, "teardown");
+
+    rc = libxl__async_exec_start(&dev->aodev.aes);
+    if (rc)
+        goto out;
+
+    return;
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void netbuf_teardown_script_cb(libxl__egc *egc,
+                                      libxl__async_exec_state *aes,
+                                      int rc, int status)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+    libxl__remus_device_nic *remus_nic = dev->concrete_data;
+
+    if (status && !rc)
+        rc = ERROR_FAIL;
+
+    free_qdisc(remus_nic);
+
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+/*----- checkpointing APIs -----*/
+
+/* The value of buffer_op, not the value passed to kernel */
+enum {
+    tc_buffer_start,
+    tc_buffer_release
+};
+
+/* API implementations */
+
+static int remus_netbuf_op(libxl__remus_device_nic *remus_nic,
+                           libxl__checkpoint_devices_state *cds,
+                           int buffer_op)
+{
+    int rc, ret;
+    libxl__remus_state *rs = cds->concrete_data;
+
+    STATE_AO_GC(cds->ao);
+
+    if (buffer_op == tc_buffer_start)
+        ret = rtnl_qdisc_plug_buffer(remus_nic->qdisc);
+    else
+        ret = rtnl_qdisc_plug_release_one(remus_nic->qdisc);
+
+    if (ret) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    ret = rtnl_qdisc_add(rs->nlsock, remus_nic->qdisc, NLM_F_REQUEST);
+    if (ret) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    if (rc)
+        LOGD(ERROR, cds-> domid, "Remus: cannot do netbuf op %s on %s:%s",
+             ((buffer_op == tc_buffer_start) ?
+             "start_new_epoch" : "release_prev_epoch"),
+             remus_nic->ifb, nl_geterror(ret));
+    return rc;
+}
+
+static void nic_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    int rc;
+    libxl__remus_device_nic *remus_nic = dev->concrete_data;
+
+    STATE_AO_GC(dev->cds->ao);
+
+    rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_start);
+
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void nic_commit(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    int rc;
+    libxl__remus_device_nic *remus_nic = dev->concrete_data;
+
+    STATE_AO_GC(dev->cds->ao);
+
+    rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_release);
+
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+const libxl__checkpoint_device_instance_ops remus_device_nic = {
+    .kind = LIBXL__DEVICE_KIND_VIF,
+    .setup = nic_setup,
+    .teardown = nic_teardown,
+    .postsuspend = nic_postsuspend,
+    .commit = nic_commit,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_nic.c b/tools/libs/light/libxl_nic.c
new file mode 100644 (file)
index 0000000..0e5d120
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2016      SUSE Linux GmbH
+ * Author Juergen Gross <jgross@suse.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
+                            const char *mac, libxl_device_nic *nic)
+{
+    GC_INIT(ctx);
+    libxl_device_nic *nics;
+    int nb, rc, i;
+    libxl_mac mac_n;
+
+    rc = libxl__parse_mac(mac, mac_n);
+    if (rc)
+        return rc;
+
+    nics = libxl__device_list(gc, &libxl__nic_devtype, domid, &nb);
+    if (!nics)
+        return ERROR_FAIL;
+
+    memset(nic, 0, sizeof (libxl_device_nic));
+
+    rc = ERROR_INVAL;
+    for (i = 0; i < nb; ++i) {
+        if (!libxl__compare_macs(&mac_n, &nics[i].mac)) {
+            *nic = nics[i];
+            rc = 0;
+            i++; /* Do not dispose this NIC on exit path */
+            break;
+        }
+        libxl_device_nic_dispose(&nics[i]);
+    }
+
+    for (; i<nb; i++)
+        libxl_device_nic_dispose(&nics[i]);
+
+    free(nics);
+    return rc;
+}
+
+#define LIBXL_DEVICE_NIC_MTU_DEFAULT 1500
+
+static int libxl__device_nic_setdefault(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_nic *nic, bool hotplug)
+{
+    int rc;
+
+    if (!nic->mtu)
+        nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
+    if (!nic->model) {
+        nic->model = strdup("rtl8139");
+        if (!nic->model) return ERROR_NOMEM;
+    }
+    if (libxl__mac_is_default(&nic->mac)) {
+        const uint8_t *r;
+        libxl_uuid uuid;
+
+        libxl_uuid_generate(&uuid);
+        r = libxl_uuid_bytearray(&uuid);
+
+        nic->mac[0] = 0x00;
+        nic->mac[1] = 0x16;
+        nic->mac[2] = 0x3e;
+        nic->mac[3] = r[0] & 0x7f;
+        nic->mac[4] = r[1];
+        nic->mac[5] = r[2];
+    }
+    if (!nic->bridge) {
+        nic->bridge = strdup("xenbr0");
+        if (!nic->bridge) return ERROR_NOMEM;
+    }
+    if ( !nic->script && asprintf(&nic->script, "%s/vif-bridge",
+                                  libxl__xen_script_dir_path()) < 0 )
+        return ERROR_FAIL;
+
+    rc = libxl__resolve_domid(gc, nic->backend_domname, &nic->backend_domid);
+    if (rc < 0) return rc;
+
+    switch (libxl__domain_type(gc, domid)) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        if (!nic->nictype) {
+            if (hotplug)
+                nic->nictype = LIBXL_NIC_TYPE_VIF;
+            else
+                nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
+        }
+        break;
+    case LIBXL_DOMAIN_TYPE_PVH:
+    case LIBXL_DOMAIN_TYPE_PV:
+        if (nic->nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
+            LOGD(ERROR, domid,
+            "trying to create PV or PVH guest with an emulated interface");
+            return ERROR_INVAL;
+        }
+        nic->nictype = LIBXL_NIC_TYPE_VIF;
+        break;
+    case LIBXL_DOMAIN_TYPE_INVALID:
+        return ERROR_FAIL;
+    default:
+        abort();
+    }
+
+    return rc;
+}
+
+static void libxl__update_config_nic(libxl__gc *gc, libxl_device_nic *dst,
+                                     const libxl_device_nic *src)
+{
+    dst->devid = src->devid;
+    dst->nictype = src->nictype;
+    libxl_mac_copy(CTX, &dst->mac, &src->mac);
+}
+
+static int libxl__set_xenstore_nic(libxl__gc *gc, uint32_t domid,
+                                   libxl_device_nic *nic,
+                                   flexarray_t *back, flexarray_t *front,
+                                   flexarray_t *ro_front)
+{
+    flexarray_grow(back, 2);
+
+    if (nic->script)
+        flexarray_append_pair(back, "script",
+                              libxl__abs_path(gc, nic->script,
+                                              libxl__xen_script_dir_path()));
+
+    if (nic->ifname) {
+        flexarray_append(back, "vifname");
+        flexarray_append(back, nic->ifname);
+    }
+
+    if (nic->coloft_forwarddev) {
+        flexarray_append(back, "forwarddev");
+        flexarray_append(back, nic->coloft_forwarddev);
+    }
+
+#define MAYBE_ADD_COLO_ARGS(arg) ({                                       \
+    if (nic->colo_##arg) {                                                \
+        flexarray_append(back, "colo_"#arg);                              \
+        flexarray_append(back, nic->colo_##arg);                          \
+    }                                                                     \
+})
+
+    MAYBE_ADD_COLO_ARGS(sock_mirror_id);
+    MAYBE_ADD_COLO_ARGS(sock_mirror_ip);
+    MAYBE_ADD_COLO_ARGS(sock_mirror_port);
+    MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_id);
+    MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_ip);
+    MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_port);
+    MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_id);
+    MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_ip);
+    MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_port);
+    MAYBE_ADD_COLO_ARGS(sock_compare_notify_id);
+    MAYBE_ADD_COLO_ARGS(sock_compare_notify_ip);
+    MAYBE_ADD_COLO_ARGS(sock_compare_notify_port);
+    MAYBE_ADD_COLO_ARGS(sock_redirector0_id);
+    MAYBE_ADD_COLO_ARGS(sock_redirector0_ip);
+    MAYBE_ADD_COLO_ARGS(sock_redirector0_port);
+    MAYBE_ADD_COLO_ARGS(sock_redirector1_id);
+    MAYBE_ADD_COLO_ARGS(sock_redirector1_ip);
+    MAYBE_ADD_COLO_ARGS(sock_redirector1_port);
+    MAYBE_ADD_COLO_ARGS(sock_redirector2_id);
+    MAYBE_ADD_COLO_ARGS(sock_redirector2_ip);
+    MAYBE_ADD_COLO_ARGS(sock_redirector2_port);
+    MAYBE_ADD_COLO_ARGS(filter_mirror_queue);
+    MAYBE_ADD_COLO_ARGS(filter_mirror_outdev);
+    MAYBE_ADD_COLO_ARGS(filter_redirector0_queue);
+    MAYBE_ADD_COLO_ARGS(filter_redirector0_indev);
+    MAYBE_ADD_COLO_ARGS(filter_redirector0_outdev);
+    MAYBE_ADD_COLO_ARGS(filter_redirector1_queue);
+    MAYBE_ADD_COLO_ARGS(filter_redirector1_indev);
+    MAYBE_ADD_COLO_ARGS(filter_redirector1_outdev);
+    MAYBE_ADD_COLO_ARGS(compare_pri_in);
+    MAYBE_ADD_COLO_ARGS(compare_sec_in);
+    MAYBE_ADD_COLO_ARGS(compare_out);
+    MAYBE_ADD_COLO_ARGS(compare_notify_dev);
+
+    MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_id);
+    MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_ip);
+    MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_port);
+    MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_id);
+    MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_ip);
+    MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_port);
+    MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_queue);
+    MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_indev);
+    MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_outdev);
+    MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_queue);
+    MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_indev);
+    MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_outdev);
+    MAYBE_ADD_COLO_ARGS(filter_sec_rewriter0_queue);
+    MAYBE_ADD_COLO_ARGS(checkpoint_host);
+    MAYBE_ADD_COLO_ARGS(checkpoint_port);
+
+#undef MAYBE_ADD_COLO_ARGS
+
+    flexarray_append(back, "mac");
+    flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
+    if (nic->ip) {
+        flexarray_append(back, "ip");
+        flexarray_append(back, libxl__strdup(gc, nic->ip));
+    }
+    if (nic->gatewaydev) {
+        flexarray_append(back, "gatewaydev");
+        flexarray_append(back, libxl__strdup(gc, nic->gatewaydev));
+    }
+
+    if (nic->rate_interval_usecs > 0) {
+        flexarray_append(back, "rate");
+        flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"",
+                            nic->rate_bytes_per_interval,
+                            nic->rate_interval_usecs));
+    }
+
+    if (nic->mtu != LIBXL_DEVICE_NIC_MTU_DEFAULT) {
+        flexarray_append(back, "mtu");
+        flexarray_append(back, GCSPRINTF("%u", nic->mtu));
+    }
+    
+    flexarray_append(back, "bridge");
+    flexarray_append(back, libxl__strdup(gc, nic->bridge));
+    flexarray_append(back, "handle");
+    flexarray_append(back, GCSPRINTF("%d", nic->devid));
+    flexarray_append(back, "type");
+    flexarray_append(back, libxl__strdup(gc,
+                                     libxl_nic_type_to_string(nic->nictype)));
+
+    flexarray_append(front, "handle");
+    flexarray_append(front, GCSPRINTF("%d", nic->devid));
+    flexarray_append(front, "mac");
+    flexarray_append(front, GCSPRINTF(
+                                    LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
+
+    flexarray_append(ro_front, "mtu");
+    flexarray_append(ro_front, GCSPRINTF("%u", nic->mtu));
+
+    return 0;
+}
+
+static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
+                                  libxl_device_nic *nic,
+                                  libxl__ao_device *aodev)
+{
+    libxl__device_add_async(egc, domid, &libxl__nic_devtype, nic, aodev);
+}
+
+static int libxl__nic_from_xenstore(libxl__gc *gc, const char *libxl_path,
+                                    libxl_devid devid, libxl_device_nic *nic)
+{
+    const char *tmp;
+    int rc;
+
+    libxl_device_nic_init(nic);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/handle", libxl_path), &tmp);
+    if (rc) goto out;
+    if (tmp)
+        nic->devid = atoi(tmp);
+    else
+        nic->devid = 0;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/backend", libxl_path), &tmp);
+    if (rc) goto out;
+
+    if (!tmp) {
+        LOG(ERROR, "nic %s does not exist (no backend path)", libxl_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = libxl__backendpath_parse_domid(gc, tmp, &nic->backend_domid);
+    if (rc) goto out;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/mtu", libxl_path), &tmp);
+    if (rc) goto out;
+    if (tmp) {
+        char *endptr;
+
+        nic->mtu = strtol(tmp, &endptr, 10);
+        if (*endptr != '\0') {
+            rc = ERROR_INVAL;
+            goto out;
+        }
+    } else {
+        nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/mac", libxl_path), &tmp);
+    if (rc) goto out;
+    if (tmp) {
+        rc = libxl__parse_mac(tmp, nic->mac);
+        if (rc) goto out;
+    } else {
+        memset(nic->mac, 0, sizeof(nic->mac));
+    }
+
+    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+                                GCSPRINTF("%s/ip", libxl_path),
+                                (const char **)(&nic->ip));
+    if (rc) goto out;
+    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+                                GCSPRINTF("%s/bridge", libxl_path),
+                                (const char **)(&nic->bridge));
+    if (rc) goto out;
+    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+                                GCSPRINTF("%s/script", libxl_path),
+                                (const char **)(&nic->script));
+    if (rc) goto out;
+    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+                                GCSPRINTF("%s/forwarddev", libxl_path),
+                                (const char **)(&nic->coloft_forwarddev));
+    if (rc) goto out;
+
+#define CHECK_COLO_ARGS(arg) ({                                           \
+    rc = libxl__xs_read_checked(NOGC, XBT_NULL,                           \
+                                GCSPRINTF("%s/colo_"#arg, libxl_path),    \
+                                (const char **)(&nic->colo_##arg));       \
+    if (rc) goto out;                                                     \
+})
+
+    CHECK_COLO_ARGS(sock_mirror_id);
+    CHECK_COLO_ARGS(sock_mirror_ip);
+    CHECK_COLO_ARGS(sock_mirror_port);
+    CHECK_COLO_ARGS(sock_compare_pri_in_id);
+    CHECK_COLO_ARGS(sock_compare_pri_in_ip);
+    CHECK_COLO_ARGS(sock_compare_pri_in_port);
+    CHECK_COLO_ARGS(sock_compare_sec_in_id);
+    CHECK_COLO_ARGS(sock_compare_sec_in_ip);
+    CHECK_COLO_ARGS(sock_compare_sec_in_port);
+    CHECK_COLO_ARGS(sock_compare_notify_id);
+    CHECK_COLO_ARGS(sock_compare_notify_ip);
+    CHECK_COLO_ARGS(sock_compare_notify_port);
+    CHECK_COLO_ARGS(sock_redirector0_id);
+    CHECK_COLO_ARGS(sock_redirector0_ip);
+    CHECK_COLO_ARGS(sock_redirector0_port);
+    CHECK_COLO_ARGS(sock_redirector1_id);
+    CHECK_COLO_ARGS(sock_redirector1_ip);
+    CHECK_COLO_ARGS(sock_redirector1_port);
+    CHECK_COLO_ARGS(sock_redirector2_id);
+    CHECK_COLO_ARGS(sock_redirector2_ip);
+    CHECK_COLO_ARGS(sock_redirector2_port);
+    CHECK_COLO_ARGS(filter_mirror_queue);
+    CHECK_COLO_ARGS(filter_mirror_outdev);
+    CHECK_COLO_ARGS(filter_redirector0_queue);
+    CHECK_COLO_ARGS(filter_redirector0_indev);
+    CHECK_COLO_ARGS(filter_redirector0_outdev);
+    CHECK_COLO_ARGS(filter_redirector1_queue);
+    CHECK_COLO_ARGS(filter_redirector1_indev);
+    CHECK_COLO_ARGS(filter_redirector1_outdev);
+    CHECK_COLO_ARGS(compare_pri_in);
+    CHECK_COLO_ARGS(compare_sec_in);
+    CHECK_COLO_ARGS(compare_out);
+    CHECK_COLO_ARGS(compare_notify_dev);
+    CHECK_COLO_ARGS(sock_sec_redirector0_id);
+    CHECK_COLO_ARGS(sock_sec_redirector0_ip);
+    CHECK_COLO_ARGS(sock_sec_redirector0_port);
+    CHECK_COLO_ARGS(sock_sec_redirector1_id);
+    CHECK_COLO_ARGS(sock_sec_redirector1_ip);
+    CHECK_COLO_ARGS(sock_sec_redirector1_port);
+    CHECK_COLO_ARGS(filter_sec_redirector0_queue);
+    CHECK_COLO_ARGS(filter_sec_redirector0_indev);
+    CHECK_COLO_ARGS(filter_sec_redirector0_outdev);
+    CHECK_COLO_ARGS(filter_sec_redirector1_queue);
+    CHECK_COLO_ARGS(filter_sec_redirector1_indev);
+    CHECK_COLO_ARGS(filter_sec_redirector1_outdev);
+    CHECK_COLO_ARGS(filter_sec_rewriter0_queue);
+    CHECK_COLO_ARGS(checkpoint_host);
+    CHECK_COLO_ARGS(checkpoint_port);
+
+#undef CHECK_COLO_ARGS
+
+    /* vif_ioemu nics use the same xenstore entries as vif interfaces */
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/type", libxl_path), &tmp);
+    if (rc) goto out;
+    if (tmp) {
+        rc = libxl_nic_type_from_string(tmp, &nic->nictype);
+        if (rc) goto out;
+    } else {
+        nic->nictype = LIBXL_NIC_TYPE_VIF;
+    }
+    nic->model = NULL; /* XXX Only for TYPE_IOEMU */
+    nic->ifname = NULL; /* XXX Only for TYPE_IOEMU */
+
+    rc = 0;
+ out:
+    return rc;
+}
+
+libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    libxl_device_nic *r;
+
+    GC_INIT(ctx);
+
+    r = libxl__device_list(gc, &libxl__nic_devtype, domid, num);
+
+    GC_FREE;
+
+    return r;
+}
+
+void libxl_device_nic_list_free(libxl_device_nic* list, int num)
+{
+    libxl__device_list_free(&libxl__nic_devtype, list, num);
+}
+
+int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
+                              const libxl_device_nic *nic,
+                              libxl_nicinfo *nicinfo)
+{
+    GC_INIT(ctx);
+    char *nicpath, *libxl_path;
+    char *val;
+    int rc;
+
+    nicinfo->devid = nic->devid;
+
+    nicpath = libxl__domain_device_frontend_path(gc, domid, nicinfo->devid,
+                                                 LIBXL__DEVICE_KIND_VIF);
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, nicinfo->devid,
+                                                 LIBXL__DEVICE_KIND_VIF);
+    nicinfo->backend = xs_read(ctx->xsh, XBT_NULL,
+                                GCSPRINTF("%s/backend", libxl_path), NULL);
+    if (!nicinfo->backend) {
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    rc = libxl__backendpath_parse_domid(gc, nicinfo->backend,
+                                        &nicinfo->backend_id);
+    if (rc) goto out;
+
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath));
+    nicinfo->state = val ? strtoul(val, NULL, 10) : -1;
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath));
+    nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath));
+    nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1;
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath));
+    nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1;
+    nicinfo->frontend = libxl__strdup(NOGC, nicpath);
+    nicinfo->frontend_id = domid;
+
+    rc = 0;
+ out:
+    GC_FREE;
+    return rc;
+}
+
+const char *libxl__device_nic_devname(libxl__gc *gc,
+                                      uint32_t domid,
+                                      uint32_t devid,
+                                      libxl_nic_type type)
+{
+    switch (type) {
+    case LIBXL_NIC_TYPE_VIF:
+        return GCSPRINTF(NETBACK_NIC_NAME, domid, devid);
+    case LIBXL_NIC_TYPE_VIF_IOEMU:
+        return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid);
+    default:
+        abort();
+    }
+}
+
+static int libxl_device_nic_compare(const libxl_device_nic *d1,
+                                    const libxl_device_nic *d2)
+{
+    return COMPARE_DEVID(d1, d2);
+}
+
+static void libxl_device_nic_update_config(libxl__gc *gc, void *d, void *s)
+{
+    libxl__update_config_nic(gc, d, s);
+}
+
+int libxl__device_nic_set_devids(libxl__gc *gc, libxl_domain_config *d_config,
+                                 uint32_t domid)
+{
+    int ret = 0;
+    int i;
+    size_t last_devid = -1;
+
+    for (i = 0; i < d_config->num_nics; i++) {
+        /* We have to init the nic here, because we still haven't
+         * called libxl_device_nic_add when domcreate_launch_dm gets called,
+         * but qemu needs the nic information to be complete.
+         */
+        ret = libxl__device_nic_setdefault(gc, domid, &d_config->nics[i],
+                                           false);
+        if (ret) {
+            LOGD(ERROR, domid, "Unable to set nic defaults for nic %d", i);
+            goto out;
+        }
+
+        if (d_config->nics[i].devid > last_devid)
+            last_devid = d_config->nics[i].devid;
+    }
+    for (i = 0; i < d_config->num_nics; i++) {
+        if (d_config->nics[i].devid < 0)
+            d_config->nics[i].devid = ++last_devid;
+    }
+
+out:
+    return ret;
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(nic)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(nic)
+
+LIBXL_DEFINE_DEVID_TO_DEVICE(nic)
+LIBXL_DEFINE_DEVICE_ADD(nic)
+LIBXL_DEFINE_DEVICES_ADD(nic)
+LIBXL_DEFINE_DEVICE_REMOVE(nic)
+
+DEFINE_DEVICE_TYPE_STRUCT(nic, VIF,
+    .update_config = libxl_device_nic_update_config,
+    .from_xenstore = (device_from_xenstore_fn_t)libxl__nic_from_xenstore,
+    .set_xenstore_config = (device_set_xenstore_config_fn_t)
+                           libxl__set_xenstore_nic,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_no_colo.c b/tools/libs/light/libxl_no_colo.c
new file mode 100644 (file)
index 0000000..2e1315c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016
+ * Author Wei Liu <wei.liu2@citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+void libxl__colo_restore_setup(libxl__egc *egc,
+                               libxl__colo_restore_state *crs)
+{
+    STATE_AO_GC(crs->ao);
+
+    LOGD(ERROR, crs->domid, "COLO is not supported");
+
+    crs->callback(egc, crs, ERROR_FAIL);
+}
+
+void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
+                                  int ret, int retval, int errnoval)
+{
+    /* Shouldn't be here because setup already failed */
+    abort();
+}
+
+void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
+    STATE_AO_GC(dss->ao);
+
+    LOGD(ERROR, dss->domid, "COLO is not supported");
+
+    dss->callback(egc, dss, ERROR_FAIL);
+}
+
+void libxl__colo_save_teardown(libxl__egc *egc,
+                               libxl__colo_save_state *css,
+                               int rc)
+{
+    /* Shouldn't be here because setup already failed */
+    abort();
+}
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_no_convert_callout.c b/tools/libs/light/libxl_no_convert_callout.c
new file mode 100644 (file)
index 0000000..6ba4d92
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
+{
+    libxl__ev_child_init(&chs->child);
+}
+
+int libxl__convert_legacy_stream(libxl__egc *egc,
+                                 libxl__conversion_helper_state *chs)
+{
+    return ERROR_FAIL;
+}
+
+void libxl__conversion_helper_abort(libxl__egc *egc,
+                                    libxl__conversion_helper_state *chs,
+                                    int rc)
+{
+    /* no op */
+}
diff --git a/tools/libs/light/libxl_nocpuid.c b/tools/libs/light/libxl_nocpuid.c
new file mode 100644 (file)
index 0000000..f473365
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
+{
+    return 1;
+}
+
+void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
+{
+}
+
+int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
+{
+    return 0;
+}
+
+int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
+                                  const char* str)
+{
+    return 0;
+}
+
+void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
+                         libxl_domain_build_info *info)
+{
+}
+
+yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
+                                libxl_cpuid_policy_list *pcpuid)
+{
+    return 0;
+}
+
+int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
+                                        const libxl__json_object *o,
+                                        libxl_cpuid_policy_list *p)
+{
+    return 0;
+}
+
+void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
+                                  libxl_cpuid_policy_list *dst,
+                                  const libxl_cpuid_policy_list *src)
+{
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_nonetbuffer.c b/tools/libs/light/libxl_nonetbuffer.c
new file mode 100644 (file)
index 0000000..4b68152
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014
+ * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+int libxl__netbuffer_enabled(libxl__gc *gc)
+{
+    return 0;
+}
+
+int init_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+    return 0;
+}
+
+void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
+{
+    return;
+}
+
+static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    STATE_AO_GC(dev->cds->ao);
+
+    dev->aodev.rc = ERROR_FAIL;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+const libxl__checkpoint_device_instance_ops remus_device_nic = {
+    .kind = LIBXL__DEVICE_KIND_VIF,
+    .setup = nic_setup,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_numa.c b/tools/libs/light/libxl_numa.c
new file mode 100644 (file)
index 0000000..a8a75f8
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2012      Citrix Ltd.
+ * Author Dario Faggioli <dario.faggioli@citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <glob.h>
+
+#include "libxl_internal.h"
+
+/*
+ * What follows are helpers for generating all the k-combinations
+ * without repetitions of a set S with n elements in it. Formally
+ * speaking, they are subsets of k distinct elements of S and, if
+ * S is n elements big, the number of k-combinations is equal to the
+ * binomial coefficient C(n k)=n!/(k! * (n - k)!).
+ *
+ * The various subset are generated one after the other by calling
+ * comb_init() first, and, after that, comb_next()
+ * C(n k)-1 times. An iterator is used to store the current status
+ * of the whole generation operation (i.e., basically, the last
+ * combination that has been generated). As soon as all combinations
+ * have been generated, comb_next() will start returning 0 instead of
+ * 1. The same instance of the iterator and the same values for
+ * n and k _must_ be used for each call (if that doesn't happen, the
+ * result is unspecified).
+ *
+ * The algorithm is a well known one (see, for example, D. Knuth's "The
+ * Art of Computer Programming - Volume 4, Fascicle 3" and it produces
+ * the combinations in such a way that they (well, more precisely, their
+ * indexes it the array/map representing the set) come with lexicographic
+ * ordering.
+ *
+ * For example, with n = 5 and k = 3, calling comb_init()
+ * will generate { 0, 1, 2 }, while subsequent valid calls to
+ * comb_next() will produce the following:
+ * { { 0, 1, 3 }, { 0, 1, 4 },
+ *   { 0, 2, 3 }, { 0, 2, 4 }, { 0, 3, 4 },
+ *   { 1, 2, 3 }, { 1, 2, 4 }, { 1, 3, 4 },
+ *   { 2, 3, 4 } }
+ *
+ * This is used by the automatic NUMA placement logic below.
+ */
+typedef int* comb_iter_t;
+
+static int comb_init(libxl__gc *gc, comb_iter_t *it, int n, int k)
+{
+    comb_iter_t new_iter;
+    int i;
+
+    if (n < k)
+        return 0;
+
+    /* First set is always { 0, 1, 2, ..., k-1 } */
+    GCNEW_ARRAY(new_iter, k);
+    for (i = 0; i < k; i++)
+        new_iter[i] = i;
+
+    *it = new_iter;
+    return 1;
+}
+
+static int comb_next(comb_iter_t it, int n, int k)
+{
+    int i;
+
+    /*
+     * The idea here is to find the leftmost element from where
+     * we should start incrementing the indexes of the iterator.
+     * This means looking for the highest index that can be increased
+     * while still producing value smaller than n-1. In the example
+     * above, when dealing with { 0, 1, 4 }, such an element is the
+     * second one, as the third is already equal to 4 (which actually
+     * is n-1).
+     * Once we found from where to start, we increment that element
+     * and override the right-hand rest of the iterator with its
+     * successors, thus achieving lexicographic ordering.
+     *
+     * Regarding the termination of the generation process, when we
+     * manage in bringing n-k at the very first position of the iterator,
+     * we know that is the last valid combination ( { 2, 3, 4 }, with
+     * n - k = 5 - 2 = 2, in the example above), and thus we start
+     * returning 0 as soon as we cross that border.
+     */
+    for (i = k - 1; it[i] == n - k + i; i--) {
+        if (i <= 0)
+            return 0;
+    }
+    for (it[i]++, i++; i < k; i++)
+        it[i] = it[i - 1] + 1;
+    return 1;
+}
+
+/* NUMA automatic placement (see libxl_internal.h for details) */
+
+/*
+ * This function turns a k-combination iterator into a node map,
+ * given another map, telling us which nodes should be considered.
+ *
+ * This means the bits that are set in suitable_nodemap and that
+ * corresponds to the indexes of the given combination are the ones
+ * that will be set in nodemap.
+ *
+ * For example, given a fully set suitable_nodemap, if the iterator
+ * represents the combination { 0, 2, 4}, nodmeap will have bits #0,
+ * #2 and #4 set.
+ * On the other hand, if, say,  suitable_nodemap=01011011, the same
+ * iterator will cause bits #1, #4 and #7 of nodemap to be set.
+ */
+static void comb_get_nodemap(comb_iter_t it, libxl_bitmap *suitable_nodemap,
+                             libxl_bitmap *nodemap, int k)
+{
+    int i, m = 0, n = 0;
+
+    libxl_bitmap_set_none(nodemap);
+    libxl_for_each_set_bit(i, *suitable_nodemap) {
+        /* Check wether the n-th set bit of suitable_nodemap
+         * matches with the m-th element of the iterator (and,
+         * only if it does, advance to the next one) */
+        if (m < k && n == it[m]) {
+            libxl_bitmap_set(nodemap, i);
+            m++;
+        }
+        n++;
+    }
+}
+
+/* Retrieve the number of cpus that the nodes that are part of the nodemap
+ * span and are also set in suitable_cpumap. */
+static int nodemap_to_nr_cpus(libxl_cputopology *tinfo, int nr_cpus,
+                              const libxl_bitmap *suitable_cpumap,
+                              const libxl_bitmap *nodemap)
+{
+    int i, nodes_cpus = 0;
+
+    for (i = 0; i < nr_cpus; i++) {
+        if (libxl_bitmap_test(suitable_cpumap, i) &&
+            libxl_bitmap_test(nodemap, tinfo[i].node))
+            nodes_cpus++;
+    }
+    return nodes_cpus;
+}
+
+/* Retrieve the amount of free memory within the nodemap */
+static uint32_t nodemap_to_free_memkb(libxl_numainfo *ninfo,
+                                      libxl_bitmap *nodemap)
+{
+    uint32_t free_memkb = 0;
+    int i;
+
+    libxl_for_each_set_bit(i, *nodemap)
+        free_memkb += ninfo[i].free / 1024;
+
+    return free_memkb;
+}
+
+/* Retrieve the number of vcpus able to run on the nodes in nodemap */
+static int nodemap_to_nr_vcpus(libxl__gc *gc, int vcpus_on_node[],
+                               const libxl_bitmap *nodemap)
+{
+    int i, nr_vcpus = 0;
+
+    libxl_for_each_set_bit(i, *nodemap)
+        nr_vcpus += vcpus_on_node[i];
+
+    return nr_vcpus;
+}
+
+/* Number of vcpus able to run on the cpus of the various nodes
+ * (reported by filling the array vcpus_on_node[]). */
+static int nr_vcpus_on_nodes(libxl__gc *gc, libxl_cputopology *tinfo,
+                             size_t tinfo_elements,
+                             const libxl_bitmap *suitable_cpumap,
+                             int vcpus_on_node[])
+{
+    libxl_dominfo *dinfo = NULL;
+    libxl_bitmap dom_nodemap, nodes_counted;
+    int nr_doms, nr_cpus;
+    int i, j, k;
+
+    dinfo = libxl_list_domain(CTX, &nr_doms);
+    if (dinfo == NULL)
+        return ERROR_FAIL;
+
+    if (libxl_node_bitmap_alloc(CTX, &nodes_counted, 0) < 0) {
+        libxl_dominfo_list_free(dinfo, nr_doms);
+        return ERROR_FAIL;
+    }
+
+    if (libxl_node_bitmap_alloc(CTX, &dom_nodemap, 0) < 0) {
+        libxl_bitmap_dispose(&nodes_counted);
+        libxl_dominfo_list_free(dinfo, nr_doms);
+        return ERROR_FAIL;
+    }
+
+    for (i = 0; i < nr_doms; i++) {
+        libxl_vcpuinfo *vinfo = NULL;
+        int nr_dom_vcpus = 0;
+        libxl_cpupoolinfo cpupool_info;
+        int cpupool;
+
+        libxl_cpupoolinfo_init(&cpupool_info);
+
+        cpupool = libxl__domain_cpupool(gc, dinfo[i].domid);
+        if (cpupool < 0)
+            goto next;
+        if (libxl_cpupool_info(CTX, &cpupool_info, cpupool))
+            goto next;
+
+        vinfo = libxl_list_vcpu(CTX, dinfo[i].domid, &nr_dom_vcpus, &nr_cpus);
+        if (vinfo == NULL)
+            goto next;
+
+        /* Retrieve the domain's node-affinity map */
+        libxl_domain_get_nodeaffinity(CTX, dinfo[i].domid, &dom_nodemap);
+
+        for (j = 0; j < nr_dom_vcpus; j++) {
+            /*
+             * For each vcpu of each domain, it must have both vcpu-affinity
+             * and node-affinity to (a pcpu belonging to) a certain node to
+             * cause an increment in the corresponding element of the array.
+             *
+             * Note that we also need to check whether the cpu actually
+             * belongs to the domain's cpupool (the cpupool of the domain
+             * being checked). In fact, it could be that the vcpu has affinity
+             * with cpus in suitable_cpumask, but that are not in its own
+             * cpupool, and we don't want to consider those!
+             */
+            libxl_bitmap_set_none(&nodes_counted);
+            libxl_for_each_set_bit(k, vinfo[j].cpumap) {
+                if (k >= tinfo_elements)
+                    break;
+                int node = tinfo[k].node;
+
+                if (libxl_bitmap_test(suitable_cpumap, k) &&
+                    libxl_bitmap_test(&cpupool_info.cpumap, k) &&
+                    libxl_bitmap_test(&dom_nodemap, node) &&
+                    !libxl_bitmap_test(&nodes_counted, node)) {
+                    libxl_bitmap_set(&nodes_counted, node);
+                    vcpus_on_node[node]++;
+                }
+            }
+        }
+
+ next:
+        libxl_cpupoolinfo_dispose(&cpupool_info);
+        libxl_vcpuinfo_list_free(vinfo, nr_dom_vcpus);
+    }
+
+    libxl_bitmap_dispose(&dom_nodemap);
+    libxl_bitmap_dispose(&nodes_counted);
+    libxl_dominfo_list_free(dinfo, nr_doms);
+    return 0;
+}
+
+/*
+ * This function tries to figure out if the host has a consistent number
+ * of cpus along all its NUMA nodes. In fact, if that is the case, we can
+ * calculate the minimum number of nodes needed for a domain by just
+ * dividing its total number of vcpus by this value computed here.
+ * However, we are not allowed to assume that all the nodes have the
+ * same number of cpus. Therefore, in case discrepancies among different
+ * nodes are found, this function just returns 0, for the caller to know
+ * it shouldn't rely on this 'optimization', and sort out things in some
+ * other way (by doing something basic, like starting trying with
+ * candidates with just one node).
+ */
+static int count_cpus_per_node(libxl_cputopology *tinfo, int nr_cpus,
+                               int nr_nodes)
+{
+    int cpus_per_node = 0;
+    int j, i;
+
+    /* This makes sense iff # of PCPUs is the same for all nodes */
+    for (j = 0; j < nr_nodes; j++) {
+        int curr_cpus = 0;
+
+        for (i = 0; i < nr_cpus; i++) {
+            if (tinfo[i].node == j)
+                curr_cpus++;
+        }
+        /* So, if the above does not hold, turn the whole thing off! */
+        cpus_per_node = cpus_per_node == 0 ? curr_cpus : cpus_per_node;
+        if (cpus_per_node != curr_cpus)
+            return 0;
+    }
+    return cpus_per_node;
+}
+
+/*
+ * Looks for the placement candidates that satisfyies some specific
+ * conditions and return the best one according to the provided
+ * comparison function.
+ */
+int libxl__get_numa_candidate(libxl__gc *gc,
+                              uint64_t min_free_memkb, int min_cpus,
+                              int min_nodes, int max_nodes,
+                              const libxl_bitmap *suitable_cpumap,
+                              libxl__numa_candidate_cmpf numa_cmpf,
+                              libxl__numa_candidate *cndt_out,
+                              int *cndt_found)
+{
+    libxl__numa_candidate new_cndt;
+    libxl_cputopology *tinfo = NULL;
+    libxl_numainfo *ninfo = NULL;
+    int nr_nodes = 0, nr_suit_nodes, nr_cpus = 0;
+    libxl_bitmap suitable_nodemap, nodemap;
+    int *vcpus_on_node, rc = 0;
+
+    libxl_bitmap_init(&nodemap);
+    libxl_bitmap_init(&suitable_nodemap);
+    libxl__numa_candidate_init(&new_cndt);
+
+    /* Get platform info and prepare the map for testing the combinations */
+    ninfo = libxl_get_numainfo(CTX, &nr_nodes);
+    if (ninfo == NULL)
+        return ERROR_FAIL;
+
+    if (nr_nodes <= 1) {
+        *cndt_found = 0;
+        goto out;
+    }
+
+    GCNEW_ARRAY(vcpus_on_node, nr_nodes);
+
+    /*
+     * The good thing about this solution is that it is based on heuristics
+     * (implemented in numa_cmpf() ), but we at least can evaluate it on
+     * all the possible placement candidates. That can happen because the
+     * number of nodes present in current NUMA systems is quite small.
+     * In fact, even if a sum of binomials is involved, if the system has
+     * up to 16 nodes it "only" takes 65535 steps. This is fine, as the
+     * number of nodes the biggest NUMA systems provide at the time of this
+     * writing is 8 (and it will probably continue to be so for a while).
+     * However, computanional complexity would explode on systems bigger
+     * than that, and it's really important we avoid trying to run this
+     * on monsters with 32, 64 or more nodes (if they ever pop into being).
+     * Therefore, here it comes a safety catch that disables the algorithm
+     * for the cases when it wouldn't work well.
+     */
+    if (nr_nodes > 16) {
+        /* Log we did nothing and return 0, as no real error occurred */
+        LOG(WARN, "System has %d NUMA nodes, which is too big for the "
+                  "placement algorithm to work effectively: skipping it. "
+                  "Consider manually pinning the vCPUs and/or looking at "
+                  "cpupools for manually partitioning the system.",
+                  nr_nodes);
+        *cndt_found = 0;
+        goto out;
+    }
+
+    tinfo = libxl_get_cpu_topology(CTX, &nr_cpus);
+    if (tinfo == NULL) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
+    if (rc)
+        goto out;
+    rc = libxl__numa_candidate_alloc(gc, &new_cndt);
+    if (rc)
+        goto out;
+
+    /* Allocate and prepare the map of the node that can be utilized for
+     * placement, basing on the map of suitable cpus. */
+    rc = libxl_node_bitmap_alloc(CTX, &suitable_nodemap, 0);
+    if (rc)
+        goto out;
+    rc = libxl_cpumap_to_nodemap(CTX, suitable_cpumap, &suitable_nodemap);
+    if (rc)
+        goto out;
+
+    /*
+     * Later on, we will try to figure out how many vcpus are runnable on
+     * each candidate (as a part of choosing the best one of them). That
+     * requires going through all the vcpus of all the domains and check
+     * their affinities. So, instead of doing that for each candidate,
+     * let's count here the number of vcpus runnable on each node, so that
+     * all we have to do later is summing up the right elements of the
+     * vcpus_on_node array.
+     */
+    rc = nr_vcpus_on_nodes(gc, tinfo, nr_cpus, suitable_cpumap, vcpus_on_node);
+    if (rc)
+        goto out;
+
+    /*
+     * If the minimum number of NUMA nodes is not explicitly specified
+     * (i.e., min_nodes == 0), we try to figure out a sensible number of nodes
+     * from where to start generating candidates, if possible (or just start
+     * from 1 otherwise). The maximum number of nodes should not exceed the
+     * number of existent NUMA nodes on the host, or the candidate generation
+     * won't work properly.
+     */
+    if (!min_nodes) {
+        int cpus_per_node;
+
+        cpus_per_node = count_cpus_per_node(tinfo, nr_cpus, nr_nodes);
+        if (cpus_per_node == 0)
+            min_nodes = 1;
+        else
+            min_nodes = (min_cpus + cpus_per_node - 1) / cpus_per_node;
+    }
+    /* We also need to be sure we do not exceed the number of
+     * nodes we are allowed to use. */
+    nr_suit_nodes = libxl_bitmap_count_set(&suitable_nodemap);
+
+    if (min_nodes > nr_suit_nodes)
+        min_nodes = nr_suit_nodes;
+    if (!max_nodes || max_nodes > nr_suit_nodes)
+        max_nodes = nr_suit_nodes;
+    if (min_nodes > max_nodes) {
+        LOG(ERROR, "Inconsistent minimum or maximum number of guest nodes");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    /* This is up to the caller to be disposed */
+    rc = libxl__numa_candidate_alloc(gc, cndt_out);
+    if (rc)
+        goto out;
+
+    /*
+     * Consider all the combinations with sizes in [min_nodes, max_nodes]
+     * (see comb_init() and comb_next()). Note that, since the fewer the
+     * number of nodes the better, it is guaranteed that any candidate
+     * found during the i-eth step will be better than any other one we
+     * could find during the (i+1)-eth and all the subsequent steps (they
+     * all will have more nodes). It's thus pointless to keep going if
+     * we already found something.
+     */
+    *cndt_found = 0;
+    while (min_nodes <= max_nodes && *cndt_found == 0) {
+        comb_iter_t comb_iter;
+        int comb_ok;
+
+        /*
+         * And here it is. Each step of this cycle generates a combination of
+         * nodes as big as min_nodes mandates.  Each of these combinations is
+         * checked against the constraints provided by the caller (namely,
+         * amount of free memory and number of cpus) and it can concur to
+         * become our best placement iff it passes the check.
+         */
+        for (comb_ok = comb_init(gc, &comb_iter, nr_suit_nodes, min_nodes);
+             comb_ok;
+             comb_ok = comb_next(comb_iter, nr_suit_nodes, min_nodes)) {
+            uint64_t nodes_free_memkb;
+            int nodes_cpus;
+
+            /* Get the nodemap for the combination, only considering
+             * suitable nodes. */
+            comb_get_nodemap(comb_iter, &suitable_nodemap,
+                             &nodemap, min_nodes);
+
+            /* If there is not enough memory in this combination, skip it
+             * and go generating the next one... */
+            nodes_free_memkb = nodemap_to_free_memkb(ninfo, &nodemap);
+            if (min_free_memkb && nodes_free_memkb < min_free_memkb)
+                continue;
+
+            /* And the same applies if this combination is short in cpus */
+            nodes_cpus = nodemap_to_nr_cpus(tinfo, nr_cpus, suitable_cpumap,
+                                            &nodemap);
+            if (min_cpus && nodes_cpus < min_cpus)
+                continue;
+
+            /*
+             * Conditions are met, we can compare this candidate with the
+             * current best one (if any).
+             */
+            libxl__numa_candidate_put_nodemap(gc, &new_cndt, &nodemap);
+            new_cndt.nr_vcpus = nodemap_to_nr_vcpus(gc, vcpus_on_node,
+                                                    &nodemap);
+            new_cndt.free_memkb = nodes_free_memkb;
+            new_cndt.nr_nodes = libxl_bitmap_count_set(&nodemap);
+            new_cndt.nr_cpus = nodes_cpus;
+
+            /*
+             * Check if the new candidate we is better the what we found up
+             * to now by means of the comparison function. If no comparison
+             * function is provided, just return as soon as we find our first
+             * candidate.
+             */
+            if (*cndt_found == 0 || numa_cmpf(&new_cndt, cndt_out) < 0) {
+                *cndt_found = 1;
+
+                LOG(DEBUG, "New best NUMA placement candidate found: "
+                           "nr_nodes=%d, nr_cpus=%d, nr_vcpus=%d, "
+                           "free_memkb=%"PRIu64"", new_cndt.nr_nodes,
+                           new_cndt.nr_cpus, new_cndt.nr_vcpus,
+                           new_cndt.free_memkb / 1024);
+
+                libxl__numa_candidate_put_nodemap(gc, cndt_out, &nodemap);
+                cndt_out->nr_vcpus = new_cndt.nr_vcpus;
+                cndt_out->free_memkb = new_cndt.free_memkb;
+                cndt_out->nr_nodes = new_cndt.nr_nodes;
+                cndt_out->nr_cpus = new_cndt.nr_cpus;
+
+                if (numa_cmpf == NULL)
+                    break;
+            }
+        }
+        min_nodes++;
+    }
+
+    if (*cndt_found == 0)
+        LOG(NOTICE, "NUMA placement failed, performance might be affected");
+
+ out:
+    libxl_bitmap_dispose(&nodemap);
+    libxl_bitmap_dispose(&suitable_nodemap);
+    libxl__numa_candidate_dispose(&new_cndt);
+    libxl_numainfo_list_free(ninfo, nr_nodes);
+    libxl_cputopology_list_free(tinfo, nr_cpus);
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_osdeps.h b/tools/libs/light/libxl_osdeps.h
new file mode 100644 (file)
index 0000000..de1d24e
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+/*
+ * This header must be included first, before any system headers,
+ * so that _GNU_SOURCE takes effect properly.
+ */
+
+#ifndef LIBXL_OSDEP
+#define LIBXL_OSDEP
+
+#define _GNU_SOURCE
+
+#if defined(__NetBSD__)
+#define SYSFS_USB_DEV          "/sys/bus/usb/devices"
+#define SYSFS_USBBACK_DRIVER   "/kern/xen/usb"
+#define SYSFS_PCI_DEV          "/sys/bus/pci/devices"
+#define SYSFS_PCIBACK_DRIVER   "/kern/xen/pci"
+#define NETBACK_NIC_NAME       "xvif%ui%d"
+#include <util.h>
+#include <uuid.h>
+#elif defined(__OpenBSD__)
+#include <util.h>
+#elif defined(__linux__)
+#define SYSFS_USB_DEV          "/sys/bus/usb/devices"
+#define SYSFS_USBBACK_DRIVER   "/sys/bus/usb/drivers/usbback"
+#define SYSFS_PCI_DEV          "/sys/bus/pci/devices"
+#define SYSFS_PCIBACK_DRIVER   "/sys/bus/pci/drivers/pciback"
+#define NETBACK_NIC_NAME       "vif%u.%d"
+#include <sys/sysmacros.h>
+#include <pty.h>
+#include <uuid/uuid.h>
+#elif defined(__sun__)
+#include <stropts.h>
+#elif defined(__FreeBSD__)
+#define SYSFS_USB_DEV          "/dev/null"
+#define SYSFS_USBBACK_DRIVER   "/dev/null"
+#define SYSFS_PCI_DEV          "/dev/null"
+#define SYSFS_PCIBACK_DRIVER   "/dev/null"
+#define NETBACK_NIC_NAME       "xnb%u.%d"
+#include <libutil.h>
+#include <sys/endian.h>
+#include <uuid.h>
+/*
+ * FreeBSD doesn't have ENODATA errno ATM, so privcmd always translates
+ * ENODATA into ENOENT.
+ */
+#ifndef ENODATA
+#define ENODATA ENOENT
+#endif
+#endif
+
+#ifndef SYSFS_USBBACK_DRIVER
+#error define SYSFS_USBBACK_DRIVER for your platform
+#endif
+#ifndef SYSFS_USB_DEV
+#error define SYSFS_USB_DEV for your platform
+#endif
+
+#ifndef SYSFS_PCIBACK_DRIVER
+#error define SYSFS_PCIBACK_DRIVER for your platform
+#endif
+#ifndef SYSFS_PCI_DEV
+#error define SYSFS_PCI_DEV for your platform
+#endif
+
+#ifdef NEED_OWN_ASPRINTF
+#include <stdarg.h>
+
+int asprintf(char **buffer, char *fmt, ...);
+int vasprintf(char **buffer, const char *fmt, va_list ap);
+#endif /*NEED_OWN_ASPRINTF*/
+
+#ifndef htobe32 /* glibc < 2.9 */
+# include <byteswap.h>
+
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#  define htobe16(x) __bswap_16(x)
+#  define htole16(x) (x)
+#  define be16toh(x) __bswap_16(x)
+#  define le16toh(x) (x)
+
+#  define htobe32(x) __bswap_32(x)
+#  define htole32(x) (x)
+#  define be32toh(x) __bswap_32(x)
+#  define le32toh(x) (x)
+
+#  define htobe64(x) __bswap_64(x)
+#  define htole64(x) (x)
+#  define be64toh(x) __bswap_64(x)
+#  define le64toh(x) (x)
+# else
+#  define htobe16(x) (x)
+#  define htole16(x) __bswap_16(x)
+#  define be16toh(x) (x)
+#  define le16toh(x) __bswap_16(x)
+
+#  define htobe32(x) (x)
+#  define htole32(x) __bswap_32(x)
+#  define be32toh(x) (x)
+#  define le32toh(x) __bswap_32(x)
+
+#  define htobe64(x) (x)
+#  define htole64(x) __bswap_64(x)
+#  define be64toh(x) (x)
+#  define le64toh(x) __bswap_64(x)
+# endif
+#endif
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_paths.c b/tools/libs/light/libxl_paths.c
new file mode 100644 (file)
index 0000000..3f6a336
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+#include "libxl_internal.h"
+
+const char *libxl__private_bindir_path(void)
+{
+    return LIBEXEC_BIN;
+}
+
+const char *libxl__xenfirmwaredir_path(void)
+{
+    return XENFIRMWAREDIR;
+}
+
+const char *libxl__xen_script_dir_path(void)
+{
+    return XEN_SCRIPT_DIR;
+}
+
+const char *libxl__run_dir_path(void)
+{
+    return XEN_RUN_DIR;
+}
+
+const char *libxl__seabios_path(void)
+{
+#ifdef SEABIOS_PATH
+    return SEABIOS_PATH;
+#else
+    return NULL;
+#endif
+}
+
+const char *libxl__ovmf_path(void)
+{
+#ifdef OVMF_PATH
+    return OVMF_PATH;
+#else
+    return NULL;
+#endif
+}
+
+const char *libxl__ipxe_path(void)
+{
+#ifdef IPXE_PATH
+    return IPXE_PATH;
+#else
+    return NULL;
+#endif
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_pci.c b/tools/libs/light/libxl_pci.c
new file mode 100644 (file)
index 0000000..bc5843b
--- /dev/null
@@ -0,0 +1,2508 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#define PCI_BDF                "%04x:%02x:%02x.%01x"
+#define PCI_BDF_SHORT          "%02x:%02x.%01x"
+#define PCI_BDF_VDEVFN         "%04x:%02x:%02x.%01x@%02x"
+#define PCI_OPTIONS            "msitranslate=%d,power_mgmt=%d"
+#define PCI_BDF_XSPATH         "%04x-%02x-%02x-%01x"
+#define PCI_PT_QDEV_ID         "pci-pt-%02x_%02x.%01x"
+
+static unsigned int pcidev_encode_bdf(libxl_device_pci *pcidev)
+{
+    unsigned int value;
+
+    value = pcidev->domain << 16;
+    value |= (pcidev->bus & 0xff) << 8;
+    value |= (pcidev->dev & 0x1f) << 3;
+    value |= (pcidev->func & 0x7);
+
+    return value;
+}
+
+static void pcidev_struct_fill(libxl_device_pci *pcidev, unsigned int domain,
+                               unsigned int bus, unsigned int dev,
+                               unsigned int func, unsigned int vdevfn)
+{
+    pcidev->domain = domain;
+    pcidev->bus = bus;
+    pcidev->dev = dev;
+    pcidev->func = func;
+    pcidev->vdevfn = vdevfn;
+}
+
+static void libxl_create_pci_backend_device(libxl__gc *gc,
+                                            flexarray_t *back,
+                                            int num,
+                                            const libxl_device_pci *pcidev)
+{
+    flexarray_append(back, GCSPRINTF("key-%d", num));
+    flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
+    flexarray_append(back, GCSPRINTF("dev-%d", num));
+    flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
+    if (pcidev->vdevfn)
+        flexarray_append_pair(back, GCSPRINTF("vdevfn-%d", num), GCSPRINTF("%x", pcidev->vdevfn));
+    flexarray_append(back, GCSPRINTF("opts-%d", num));
+    flexarray_append(back,
+              GCSPRINTF("msitranslate=%d,power_mgmt=%d,permissive=%d",
+                             pcidev->msitranslate, pcidev->power_mgmt,
+                             pcidev->permissive));
+    flexarray_append_pair(back, GCSPRINTF("state-%d", num), GCSPRINTF("%d", XenbusStateInitialising));
+}
+
+static void libxl__device_from_pcidev(libxl__gc *gc, uint32_t domid,
+                                      const libxl_device_pci *pcidev,
+                                      libxl__device *device)
+{
+    device->backend_devid = 0;
+    device->backend_domid = 0;
+    device->backend_kind = LIBXL__DEVICE_KIND_PCI;
+    device->devid = 0;
+    device->domid = domid;
+    device->kind = LIBXL__DEVICE_KIND_PCI;
+}
+
+static int libxl__create_pci_backend(libxl__gc *gc, uint32_t domid,
+                                     const libxl_device_pci *pcidev,
+                                     int num)
+{
+    flexarray_t *front = NULL;
+    flexarray_t *back = NULL;
+    libxl__device device;
+    int i;
+
+    front = flexarray_make(gc, 16, 1);
+    back = flexarray_make(gc, 16, 1);
+
+    LOGD(DEBUG, domid, "Creating pci backend");
+
+    /* add pci device */
+    libxl__device_from_pcidev(gc, domid, pcidev, &device);
+
+    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+    flexarray_append_pair(back, "online", "1");
+    flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising));
+    flexarray_append_pair(back, "domain", libxl__domid_to_name(gc, domid));
+
+    for (i = 0; i < num; i++, pcidev++)
+        libxl_create_pci_backend_device(gc, back, i, pcidev);
+
+    flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num));
+    flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", 0));
+    flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising));
+
+    return libxl__device_generic_add(gc, XBT_NULL, &device,
+                                     libxl__xs_kvs_of_flexarray(gc, back),
+                                     libxl__xs_kvs_of_flexarray(gc, front),
+                                     NULL);
+}
+
+static int libxl__device_pci_add_xenstore(libxl__gc *gc,
+                                          uint32_t domid,
+                                          const libxl_device_pci *pcidev,
+                                          bool starting)
+{
+    flexarray_t *back;
+    char *num_devs, *be_path;
+    int num = 0;
+    xs_transaction_t t = XBT_NULL;
+    int rc;
+    libxl_domain_config d_config;
+    libxl__flock *lock = NULL;
+    bool is_stubdomain = libxl_is_stubdom(CTX, domid, NULL);
+
+    /* Stubdomain doesn't have own config. */
+    if (!is_stubdomain)
+        libxl_domain_config_init(&d_config);
+
+    be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
+                                                LIBXL__DEVICE_KIND_PCI);
+    num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
+    if (!num_devs)
+        return libxl__create_pci_backend(gc, domid, pcidev, 1);
+
+    libxl_domain_type domtype = libxl__domain_type(gc, domid);
+    if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
+        return ERROR_FAIL;
+
+    if (!starting && domtype == LIBXL_DOMAIN_TYPE_PV) {
+        if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0)
+            return ERROR_FAIL;
+    }
+
+    back = flexarray_make(gc, 16, 1);
+
+    LOGD(DEBUG, domid, "Adding new pci device to xenstore");
+    num = atoi(num_devs);
+    libxl_create_pci_backend_device(gc, back, num, pcidev);
+    flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num + 1));
+    if (!starting)
+        flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring));
+
+    /*
+     * Stubdomin config is derived from its target domain, it doesn't have
+     * its own file.
+     */
+    if (!is_stubdomain) {
+        lock = libxl__lock_domain_userdata(gc, domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+            goto out;
+        }
+
+        rc = libxl__get_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        device_add_domain_config(gc, &d_config, &libxl__pcidev_devtype,
+                                 pcidev);
+
+        rc = libxl__dm_check_start(gc, &d_config, domid);
+        if (rc) goto out;
+    }
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        if (lock) {
+            rc = libxl__set_domain_configuration(gc, domid, &d_config);
+            if (rc) goto out;
+        }
+
+        libxl__xs_writev(gc, t, be_path, libxl__xs_kvs_of_flexarray(gc, back));
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    if (lock) libxl__unlock_file(lock);
+    if (!is_stubdomain)
+        libxl_domain_config_dispose(&d_config);
+    return rc;
+}
+
+static int libxl__device_pci_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath;
+    int num, i, j;
+    xs_transaction_t t;
+
+    be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
+                                                LIBXL__DEVICE_KIND_PCI);
+    num_devs_path = GCSPRINTF("%s/num_devs", be_path);
+    num_devs = libxl__xs_read(gc, XBT_NULL, num_devs_path);
+    if (!num_devs)
+        return ERROR_INVAL;
+    num = atoi(num_devs);
+
+    libxl_domain_type domtype = libxl__domain_type(gc, domid);
+    if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
+        return ERROR_FAIL;
+
+    if (domtype == LIBXL_DOMAIN_TYPE_PV) {
+        if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
+            LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
+            return ERROR_FAIL;
+        }
+    }
+
+    for (i = 0; i < num; i++) {
+        unsigned int domain = 0, bus = 0, dev = 0, func = 0;
+        xsdev = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, i));
+        sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
+        if (domain == pcidev->domain && bus == pcidev->bus &&
+            pcidev->dev == dev && pcidev->func == func) {
+            break;
+        }
+    }
+    if (i == num) {
+        LOGD(ERROR, domid, "Couldn't find the device on xenstore");
+        return ERROR_INVAL;
+    }
+
+retry_transaction:
+    t = xs_transaction_start(ctx->xsh);
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i), GCSPRINTF("%d", XenbusStateClosing), 1);
+    xs_write(ctx->xsh, t, GCSPRINTF("%s/state", be_path), GCSPRINTF("%d", XenbusStateReconfiguring), 1);
+    if (!xs_transaction_end(ctx->xsh, t, 0))
+        if (errno == EAGAIN)
+            goto retry_transaction;
+
+    if (domtype == LIBXL_DOMAIN_TYPE_PV) {
+        if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
+            LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
+            return ERROR_FAIL;
+        }
+    }
+
+retry_transaction2:
+    t = xs_transaction_start(ctx->xsh);
+    xs_rm(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i));
+    xs_rm(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, i));
+    xs_rm(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, i));
+    xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, i));
+    xs_rm(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, i));
+    xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, i));
+    libxl__xs_printf(gc, t, num_devs_path, "%d", num - 1);
+    for (j = i + 1; j < num; j++) {
+        tmppath = GCSPRINTF("%s/state-%d", be_path, j);
+        tmp = libxl__xs_read(gc, t, tmppath);
+        xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, j - 1), tmp, strlen(tmp));
+        xs_rm(ctx->xsh, t, tmppath);
+        tmppath = GCSPRINTF("%s/dev-%d", be_path, j);
+        tmp = libxl__xs_read(gc, t, tmppath);
+        xs_write(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, j - 1), tmp, strlen(tmp));
+        xs_rm(ctx->xsh, t, tmppath);
+        tmppath = GCSPRINTF("%s/key-%d", be_path, j);
+        tmp = libxl__xs_read(gc, t, tmppath);
+        xs_write(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, j - 1), tmp, strlen(tmp));
+        xs_rm(ctx->xsh, t, tmppath);
+        tmppath = GCSPRINTF("%s/vdev-%d", be_path, j);
+        tmp = libxl__xs_read(gc, t, tmppath);
+        if (tmp) {
+            xs_write(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp));
+            xs_rm(ctx->xsh, t, tmppath);
+        }
+        tmppath = GCSPRINTF("%s/opts-%d", be_path, j);
+        tmp = libxl__xs_read(gc, t, tmppath);
+        if (tmp) {
+            xs_write(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, j - 1), tmp, strlen(tmp));
+            xs_rm(ctx->xsh, t, tmppath);
+        }
+        tmppath = GCSPRINTF("%s/vdevfn-%d", be_path, j);
+        tmp = libxl__xs_read(gc, t, tmppath);
+        if (tmp) {
+            xs_write(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp));
+            xs_rm(ctx->xsh, t, tmppath);
+        }
+    }
+    if (!xs_transaction_end(ctx->xsh, t, 0))
+        if (errno == EAGAIN)
+            goto retry_transaction2;
+
+    if (num == 1) {
+        libxl__device dev;
+        if (libxl__parse_backend_path(gc, be_path, &dev) != 0)
+            return ERROR_FAIL;
+
+        dev.domid = domid;
+        dev.kind = LIBXL__DEVICE_KIND_PCI;
+        dev.devid = 0;
+
+        libxl__device_destroy(gc, &dev);
+        return 0;
+    }
+
+    return 0;
+}
+
+static int get_all_assigned_devices(libxl__gc *gc, libxl_device_pci **list, int *num)
+{
+    char **domlist;
+    unsigned int nd = 0, i;
+
+    *list = NULL;
+    *num = 0;
+
+    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &nd);
+    for(i = 0; i < nd; i++) {
+        char *path, *num_devs;
+
+        path = GCSPRINTF("/local/domain/0/backend/%s/%s/0/num_devs",
+                         libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
+                         domlist[i]);
+        num_devs = libxl__xs_read(gc, XBT_NULL, path);
+        if ( num_devs ) {
+            int ndev = atoi(num_devs), j;
+            char *devpath, *bdf;
+
+            for(j = 0; j < ndev; j++) {
+                devpath = GCSPRINTF("/local/domain/0/backend/%s/%s/0/dev-%u",
+                                    libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
+                                    domlist[i], j);
+                bdf = libxl__xs_read(gc, XBT_NULL, devpath);
+                if ( bdf ) {
+                    unsigned dom, bus, dev, func;
+                    if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
+                        continue;
+
+                    *list = realloc(*list, sizeof(libxl_device_pci) * ((*num) + 1));
+                    if (*list == NULL)
+                        return ERROR_NOMEM;
+                    pcidev_struct_fill(*list + *num, dom, bus, dev, func, 0);
+                    (*num)++;
+                }
+            }
+        }
+    }
+    libxl__ptr_add(gc, *list);
+
+    return 0;
+}
+
+static int is_pcidev_in_array(libxl_device_pci *assigned, int num_assigned,
+                       int dom, int bus, int dev, int func)
+{
+    int i;
+
+    for(i = 0; i < num_assigned; i++) {
+        if ( assigned[i].domain != dom )
+            continue;
+        if ( assigned[i].bus != bus )
+            continue;
+        if ( assigned[i].dev != dev )
+            continue;
+        if ( assigned[i].func != func )
+            continue;
+        return 1;
+    }
+
+    return 0;
+}
+
+/* Write the standard BDF into the sysfs path given by sysfs_path. */
+static int sysfs_write_bdf(libxl__gc *gc, const char * sysfs_path,
+                           libxl_device_pci *pcidev)
+{
+    int rc, fd;
+    char *buf;
+
+    fd = open(sysfs_path, O_WRONLY);
+    if (fd < 0) {
+        LOGE(ERROR, "Couldn't open %s", sysfs_path);
+        return ERROR_FAIL;
+    }
+
+    buf = GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus,
+                    pcidev->dev, pcidev->func);
+    rc = write(fd, buf, strlen(buf));
+    /* Annoying to have two if's, but we need the errno */
+    if (rc < 0)
+        LOGE(ERROR, "write to %s returned %d", sysfs_path, rc);
+    close(fd);
+
+    if (rc < 0)
+        return ERROR_FAIL;
+
+    return 0;
+}
+
+libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_pci *pcidevs = NULL, *new, *assigned;
+    struct dirent *de;
+    DIR *dir;
+    int r, num_assigned;
+
+    *num = 0;
+
+    r = get_all_assigned_devices(gc, &assigned, &num_assigned);
+    if (r) goto out;
+
+    dir = opendir(SYSFS_PCIBACK_DRIVER);
+    if (NULL == dir) {
+        if (errno == ENOENT) {
+            LOG(ERROR, "Looks like pciback driver not loaded");
+        } else {
+            LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER);
+        }
+        goto out;
+    }
+
+    while((de = readdir(dir))) {
+        unsigned dom, bus, dev, func;
+        if (sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4)
+            continue;
+
+        if (is_pcidev_in_array(assigned, num_assigned, dom, bus, dev, func))
+            continue;
+
+        new = realloc(pcidevs, ((*num) + 1) * sizeof(*new));
+        if (NULL == new)
+            continue;
+
+        pcidevs = new;
+        new = pcidevs + *num;
+
+        memset(new, 0, sizeof(*new));
+        pcidev_struct_fill(new, dom, bus, dev, func, 0);
+        (*num)++;
+    }
+
+    closedir(dir);
+out:
+    GC_FREE;
+    return pcidevs;
+}
+
+/* Unbind device from its current driver, if any.  If driver_path is non-NULL,
+ * store the path to the original driver in it. */
+static int sysfs_dev_unbind(libxl__gc *gc, libxl_device_pci *pcidev,
+                            char **driver_path)
+{
+    char * spath, *dp = NULL;
+    struct stat st;
+
+    spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/driver",
+                           pcidev->domain,
+                           pcidev->bus,
+                           pcidev->dev,
+                           pcidev->func);
+    if ( !lstat(spath, &st) ) {
+        /* Find the canonical path to the driver. */
+        dp = libxl__zalloc(gc, PATH_MAX);
+        dp = realpath(spath, dp);
+        if ( !dp ) {
+            LOGE(ERROR, "realpath() failed");
+            return -1;
+        }
+
+        LOG(DEBUG, "Driver re-plug path: %s", dp);
+
+        /* Unbind from the old driver */
+        spath = GCSPRINTF("%s/unbind", dp);
+        if ( sysfs_write_bdf(gc, spath, pcidev) < 0 ) {
+            LOGE(ERROR, "Couldn't unbind device");
+            return -1;
+        }
+    }
+
+    if ( driver_path )
+        *driver_path = dp;
+
+    return 0;
+}
+
+static uint16_t sysfs_dev_get_vendor(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+    char *pci_device_vendor_path =
+            GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/vendor",
+                      pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+    uint16_t read_items;
+    uint16_t pci_device_vendor;
+
+    FILE *f = fopen(pci_device_vendor_path, "r");
+    if (!f) {
+        LOGE(ERROR,
+             "pci device "PCI_BDF" does not have vendor attribute",
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+        return 0xffff;
+    }
+    read_items = fscanf(f, "0x%hx\n", &pci_device_vendor);
+    fclose(f);
+    if (read_items != 1) {
+        LOGE(ERROR,
+             "cannot read vendor of pci device "PCI_BDF,
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+        return 0xffff;
+    }
+
+    return pci_device_vendor;
+}
+
+static uint16_t sysfs_dev_get_device(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+    char *pci_device_device_path =
+            GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/device",
+                      pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+    uint16_t read_items;
+    uint16_t pci_device_device;
+
+    FILE *f = fopen(pci_device_device_path, "r");
+    if (!f) {
+        LOGE(ERROR,
+             "pci device "PCI_BDF" does not have device attribute",
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+        return 0xffff;
+    }
+    read_items = fscanf(f, "0x%hx\n", &pci_device_device);
+    fclose(f);
+    if (read_items != 1) {
+        LOGE(ERROR,
+             "cannot read device of pci device "PCI_BDF,
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+        return 0xffff;
+    }
+
+    return pci_device_device;
+}
+
+static int sysfs_dev_get_class(libxl__gc *gc, libxl_device_pci *pcidev,
+                               unsigned long *class)
+{
+    char *pci_device_class_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/class",
+                     pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+    int read_items, ret = 0;
+
+    FILE *f = fopen(pci_device_class_path, "r");
+    if (!f) {
+        LOGE(ERROR,
+             "pci device "PCI_BDF" does not have class attribute",
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+        ret = ERROR_FAIL;
+        goto out;
+    }
+    read_items = fscanf(f, "0x%lx\n", class);
+    fclose(f);
+    if (read_items != 1) {
+        LOGE(ERROR,
+             "cannot read class of pci device "PCI_BDF,
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+        ret = ERROR_FAIL;
+    }
+
+out:
+    return ret;
+}
+
+/*
+ * Some devices may need some ways to work well. Here like IGD,
+ * we have to pass a specific option to qemu.
+ */
+bool libxl__is_igd_vga_passthru(libxl__gc *gc,
+                                const libxl_domain_config *d_config)
+{
+    unsigned int i;
+    uint16_t pt_vendor, pt_device;
+    unsigned long class;
+
+    for (i = 0 ; i < d_config->num_pcidevs ; i++) {
+        libxl_device_pci *pcidev = &d_config->pcidevs[i];
+        pt_vendor = sysfs_dev_get_vendor(gc, pcidev);
+        pt_device = sysfs_dev_get_device(gc, pcidev);
+
+        if (pt_vendor == 0xffff || pt_device == 0xffff ||
+            pt_vendor != 0x8086)
+            continue;
+
+        if (sysfs_dev_get_class(gc, pcidev, &class))
+            continue;
+        if (class == 0x030000)
+            return true;
+    }
+
+    return false;
+}
+
+/*
+ * A brief comment about slots.  I don't know what slots are for; however,
+ * I have by experimentation determined:
+ * - Before a device can be bound to pciback, its BDF must first be listed
+ *   in pciback/slots
+ * - The way to get the BDF listed there is to write BDF to
+ *   pciback/new_slot
+ * - Writing the same BDF to pciback/new_slot is not idempotent; it results
+ *   in two entries of the BDF in pciback/slots
+ * It's not clear whether having two entries in pciback/slots is a problem
+ * or not.  Just to be safe, this code does the conservative thing, and
+ * first checks to see if there is a slot, adding one only if one does not
+ * already exist.
+ */
+
+/* Scan through /sys/.../pciback/slots looking for pcidev's BDF */
+static int pciback_dev_has_slot(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+    FILE *f;
+    int rc = 0;
+    unsigned dom, bus, dev, func;
+
+    f = fopen(SYSFS_PCIBACK_DRIVER"/slots", "r");
+
+    if (f == NULL) {
+        LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER"/slots");
+        return ERROR_FAIL;
+    }
+
+    while(fscanf(f, "%x:%x:%x.%d\n", &dom, &bus, &dev, &func)==4) {
+        if(dom == pcidev->domain
+           && bus == pcidev->bus
+           && dev == pcidev->dev
+           && func == pcidev->func) {
+            rc = 1;
+            goto out;
+        }
+    }
+out:
+    fclose(f);
+    return rc;
+}
+
+static int pciback_dev_is_assigned(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+    char * spath;
+    int rc;
+    struct stat st;
+
+    if ( access(SYSFS_PCIBACK_DRIVER, F_OK) < 0 ) {
+        if ( errno == ENOENT ) {
+            LOG(ERROR, "Looks like pciback driver is not loaded");
+        } else {
+            LOGE(ERROR, "Can't access "SYSFS_PCIBACK_DRIVER);
+        }
+        return -1;
+    }
+
+    spath = GCSPRINTF(SYSFS_PCIBACK_DRIVER"/"PCI_BDF,
+                      pcidev->domain, pcidev->bus,
+                      pcidev->dev, pcidev->func);
+    rc = lstat(spath, &st);
+
+    if( rc == 0 )
+        return 1;
+    if ( rc < 0 && errno == ENOENT )
+        return 0;
+    LOGE(ERROR, "Accessing %s", spath);
+    return -1;
+}
+
+static int pciback_dev_assign(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+    int rc;
+
+    if ( (rc=pciback_dev_has_slot(gc, pcidev)) < 0 ) {
+        LOGE(ERROR, "Error checking for pciback slot");
+        return ERROR_FAIL;
+    } else if (rc == 0) {
+        if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/new_slot",
+                             pcidev) < 0 ) {
+            LOGE(ERROR, "Couldn't bind device to pciback!");
+            return ERROR_FAIL;
+        }
+    }
+
+    if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/bind", pcidev) < 0 ) {
+        LOGE(ERROR, "Couldn't bind device to pciback!");
+        return ERROR_FAIL;
+    }
+    return 0;
+}
+
+static int pciback_dev_unassign(libxl__gc *gc, libxl_device_pci *pcidev)
+{
+    /* Remove from pciback */
+    if ( sysfs_dev_unbind(gc, pcidev, NULL) < 0 ) {
+        LOG(ERROR, "Couldn't unbind device!");
+        return ERROR_FAIL;
+    }
+
+    /* Remove slot if necessary */
+    if ( pciback_dev_has_slot(gc, pcidev) > 0 ) {
+        if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/remove_slot",
+                             pcidev) < 0 ) {
+            LOGE(ERROR, "Couldn't remove pciback slot");
+            return ERROR_FAIL;
+        }
+    }
+    return 0;
+}
+
+#define PCIBACK_INFO_PATH "/libxl/pciback"
+
+static void pci_assignable_driver_path_write(libxl__gc *gc,
+                                            libxl_device_pci *pcidev,
+                                            char *driver_path)
+{
+    char *path;
+
+    path = GCSPRINTF(PCIBACK_INFO_PATH"/"PCI_BDF_XSPATH"/driver_path",
+                     pcidev->domain,
+                     pcidev->bus,
+                     pcidev->dev,
+                     pcidev->func);
+    if ( libxl__xs_printf(gc, XBT_NULL, path, "%s", driver_path) < 0 ) {
+        LOGE(WARN, "Write of %s to node %s failed.", driver_path, path);
+    }
+}
+
+static char * pci_assignable_driver_path_read(libxl__gc *gc,
+                                              libxl_device_pci *pcidev)
+{
+    return libxl__xs_read(gc, XBT_NULL,
+                          GCSPRINTF(
+                           PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH "/driver_path",
+                           pcidev->domain,
+                           pcidev->bus,
+                           pcidev->dev,
+                           pcidev->func));
+}
+
+static void pci_assignable_driver_path_remove(libxl__gc *gc,
+                                              libxl_device_pci *pcidev)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+
+    /* Remove the xenstore entry */
+    xs_rm(ctx->xsh, XBT_NULL,
+          GCSPRINTF(PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH,
+                    pcidev->domain,
+                    pcidev->bus,
+                    pcidev->dev,
+                    pcidev->func) );
+}
+
+static int libxl__device_pci_assignable_add(libxl__gc *gc,
+                                            libxl_device_pci *pcidev,
+                                            int rebind)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    unsigned dom, bus, dev, func;
+    char *spath, *driver_path = NULL;
+    int rc;
+    struct stat st;
+
+    /* Local copy for convenience */
+    dom = pcidev->domain;
+    bus = pcidev->bus;
+    dev = pcidev->dev;
+    func = pcidev->func;
+
+    /* See if the device exists */
+    spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF, dom, bus, dev, func);
+    if ( lstat(spath, &st) ) {
+        LOGE(ERROR, "Couldn't lstat %s", spath);
+        return ERROR_FAIL;
+    }
+
+    /* Check to see if it's already assigned to pciback */
+    rc = pciback_dev_is_assigned(gc, pcidev);
+    if ( rc < 0 ) {
+        return ERROR_FAIL;
+    }
+    if ( rc ) {
+        LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func);
+        goto quarantine;
+    }
+
+    /* Check to see if there's already a driver that we need to unbind from */
+    if ( sysfs_dev_unbind(gc, pcidev, &driver_path ) ) {
+        LOG(ERROR, "Couldn't unbind "PCI_BDF" from driver",
+            dom, bus, dev, func);
+        return ERROR_FAIL;
+    }
+
+    /* Store driver_path for rebinding to dom0 */
+    if ( rebind ) {
+        if ( driver_path ) {
+            pci_assignable_driver_path_write(gc, pcidev, driver_path);
+        } else if ( (driver_path =
+                     pci_assignable_driver_path_read(gc, pcidev)) != NULL ) {
+            LOG(INFO, PCI_BDF" not bound to a driver, will be rebound to %s",
+                dom, bus, dev, func, driver_path);
+        } else {
+            LOG(WARN, PCI_BDF" not bound to a driver, will not be rebound.",
+                dom, bus, dev, func);
+        }
+    } else {
+        pci_assignable_driver_path_remove(gc, pcidev);
+    }
+
+    if ( pciback_dev_assign(gc, pcidev) ) {
+        LOG(ERROR, "Couldn't bind device to pciback!");
+        return ERROR_FAIL;
+    }
+
+quarantine:
+    /*
+     * DOMID_IO is just a sentinel domain, without any actual mappings,
+     * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being
+     * unnecessarily denied.
+     */
+    rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev),
+                          XEN_DOMCTL_DEV_RDM_RELAXED);
+    if ( rc < 0 ) {
+        LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func);
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+static int libxl__device_pci_assignable_remove(libxl__gc *gc,
+                                               libxl_device_pci *pcidev,
+                                               int rebind)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int rc;
+    char *driver_path;
+
+    /* De-quarantine */
+    rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev));
+    if ( rc < 0 ) {
+        LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus,
+            pcidev->dev, pcidev->func);
+        return ERROR_FAIL;
+    }
+
+    /* Unbind from pciback */
+    if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) {
+        return ERROR_FAIL;
+    } else if ( rc ) {
+        pciback_dev_unassign(gc, pcidev);
+    } else {
+        LOG(WARN, "Not bound to pciback");
+    }
+
+    /* Rebind if necessary */
+    driver_path = pci_assignable_driver_path_read(gc, pcidev);
+
+    if ( driver_path ) {
+        if ( rebind ) {
+            LOG(INFO, "Rebinding to driver at %s", driver_path);
+
+            if ( sysfs_write_bdf(gc,
+                                 GCSPRINTF("%s/bind", driver_path),
+                                 pcidev) < 0 ) {
+                LOGE(ERROR, "Couldn't bind device to %s", driver_path);
+                return -1;
+            }
+
+            pci_assignable_driver_path_remove(gc, pcidev);
+        }
+    } else {
+        if ( rebind ) {
+            LOG(WARN,
+                "Couldn't find path for original driver; not rebinding");
+        }
+    }
+
+    return 0;
+}
+
+int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev,
+                                    int rebind)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = libxl__device_pci_assignable_add(gc, pcidev, rebind);
+
+    GC_FREE;
+    return rc;
+}
+
+
+int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev,
+                                       int rebind)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = libxl__device_pci_assignable_remove(gc, pcidev, rebind);
+
+    GC_FREE;
+    return rc;
+}
+
+/*
+ * This function checks that all functions of a device are bound to pciback
+ * driver. It also initialises a bit-mask of which function numbers are present
+ * on that device.
+*/
+static int pci_multifunction_check(libxl__gc *gc, libxl_device_pci *pcidev, unsigned int *func_mask)
+{
+    struct dirent *de;
+    DIR *dir;
+
+    *func_mask = 0;
+
+    dir = opendir(SYSFS_PCI_DEV);
+    if ( NULL == dir ) {
+        LOGE(ERROR, "Couldn't open %s", SYSFS_PCI_DEV);
+        return -1;
+    }
+
+    while( (de = readdir(dir)) ) {
+        unsigned dom, bus, dev, func;
+        struct stat st;
+        char *path;
+
+        if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
+            continue;
+        if ( pcidev->domain != dom )
+            continue;
+        if ( pcidev->bus != bus )
+            continue;
+        if ( pcidev->dev != dev )
+            continue;
+
+        path = GCSPRINTF("%s/" PCI_BDF, SYSFS_PCIBACK_DRIVER, dom, bus, dev, func);
+        if ( lstat(path, &st) ) {
+            if ( errno == ENOENT )
+                LOG(ERROR, PCI_BDF " is not assigned to pciback driver",
+                    dom, bus, dev, func);
+            else
+                LOGE(ERROR, "Couldn't lstat %s", path);
+            closedir(dir);
+            return -1;
+        }
+        (*func_mask) |= (1 << func);
+    }
+
+    closedir(dir);
+    return 0;
+}
+
+static int pci_ins_check(libxl__gc *gc, uint32_t domid, const char *state, void *priv)
+{
+    char *orig_state = priv;
+
+    if ( !strcmp(state, "pci-insert-failed") )
+        return -1;
+    if ( !strcmp(state, "pci-inserted") )
+        return 0;
+    if ( !strcmp(state, orig_state) )
+        return 1;
+
+    return 1;
+}
+
+static int qemu_pci_add_xenstore(libxl__gc *gc, uint32_t domid,
+                                 libxl_device_pci *pcidev)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int rc = 0;
+    char *path;
+    char *state, *vdevfn;
+    uint32_t dm_domid;
+
+    dm_domid = libxl_get_stubdom_id(CTX, domid);
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+    state = libxl__xs_read(gc, XBT_NULL, path);
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
+    if (pcidev->vdevfn) {
+        libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF_VDEVFN","PCI_OPTIONS,
+                         pcidev->domain, pcidev->bus, pcidev->dev,
+                         pcidev->func, pcidev->vdevfn, pcidev->msitranslate,
+                         pcidev->power_mgmt);
+    } else {
+        libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF","PCI_OPTIONS,
+                         pcidev->domain,  pcidev->bus, pcidev->dev,
+                         pcidev->func, pcidev->msitranslate, pcidev->power_mgmt);
+    }
+
+    libxl__qemu_traditional_cmd(gc, domid, "pci-ins");
+    rc = libxl__wait_for_device_model_deprecated(gc, domid, NULL, NULL,
+                                      pci_ins_check, state);
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
+    vdevfn = libxl__xs_read(gc, XBT_NULL, path);
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+    if ( rc < 0 )
+        LOGD(ERROR, domid, "qemu refused to add device: %s", vdevfn);
+    else if ( sscanf(vdevfn, "0x%x", &pcidev->vdevfn) != 1 ) {
+        LOGD(ERROR, domid, "wrong format for the vdevfn: '%s'", vdevfn);
+        rc = -1;
+    }
+    xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
+
+    return rc;
+}
+
+static int check_qemu_running(libxl__gc *gc,
+                              libxl_domid domid,
+                              libxl__xswait_state *xswa,
+                              int rc,
+                              const char *state)
+{
+    if (rc) {
+        if (rc == ERROR_TIMEDOUT) {
+            LOGD(ERROR, domid, "%s not ready", xswa->what);
+        }
+        goto out;
+    }
+
+    if (!state || strcmp(state, "running"))
+        return ERROR_NOT_READY;
+
+out:
+    libxl__xswait_stop(gc, xswa);
+    return rc;
+}
+
+typedef struct pci_add_state {
+    /* filled by user of do_pci_add */
+    libxl__ao_device *aodev;
+    libxl_domid domid;
+    bool starting;
+    void (*callback)(libxl__egc *, struct pci_add_state *, int rc);
+
+    /* private to device_pci_add_stubdom_wait */
+    libxl__ev_devstate pciback_ds;
+
+    /* private to do_pci_add */
+    libxl__xswait_state xswait;
+    libxl__ev_qmp qmp;
+    libxl__ev_time timeout;
+    libxl_device_pci *pcidev;
+    int pci_domid;
+} pci_add_state;
+
+static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
+    libxl__xswait_state *xswa, int rc, const char *state);
+static void pci_add_qmp_device_add(libxl__egc *, pci_add_state *);
+static void pci_add_qmp_device_add_cb(libxl__egc *,
+    libxl__ev_qmp *, const libxl__json_object *, int rc);
+static void pci_add_qmp_query_pci_cb(libxl__egc *,
+    libxl__ev_qmp *, const libxl__json_object *, int rc);
+static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
+    const struct timeval *requested_abs, int rc);
+static void pci_add_dm_done(libxl__egc *,
+    pci_add_state *, int rc);
+
+static void do_pci_add(libxl__egc *egc,
+                       libxl_domid domid,
+                       libxl_device_pci *pcidev,
+                       pci_add_state *pas)
+{
+    STATE_AO_GC(pas->aodev->ao);
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    int rc;
+
+    /* init pci_add_state */
+    libxl__xswait_init(&pas->xswait);
+    libxl__ev_qmp_init(&pas->qmp);
+    pas->pcidev = pcidev;
+    pas->pci_domid = domid;
+    libxl__ev_time_init(&pas->timeout);
+
+    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (type == LIBXL_DOMAIN_TYPE_HVM) {
+        switch (libxl__device_model_version_running(gc, domid)) {
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+                pas->xswait.ao = ao;
+                pas->xswait.what = "Device Model";
+                pas->xswait.path = DEVICE_MODEL_XS_PATH(gc,
+                    libxl_get_stubdom_id(CTX, domid), domid, "/state");
+                pas->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+                pas->xswait.callback = pci_add_qemu_trad_watch_state_cb;
+                rc = libxl__xswait_start(gc, &pas->xswait);
+                if (rc) goto out;
+                return;
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+                pci_add_qmp_device_add(egc, pas); /* must be last */
+                return;
+            default:
+                rc = ERROR_INVAL;
+                break;
+        }
+    }
+
+    rc = 0;
+
+out:
+    pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
+                                             libxl__xswait_state *xswa,
+                                             int rc,
+                                             const char *state)
+{
+    pci_add_state *pas = CONTAINER_OF(xswa, *pas, xswait);
+    STATE_AO_GC(pas->aodev->ao);
+
+    /* Convenience aliases */
+    libxl_domid domid = pas->domid;
+    libxl_device_pci *pcidev = pas->pcidev;
+
+    rc = check_qemu_running(gc, domid, xswa, rc, state);
+    if (rc == ERROR_NOT_READY)
+        return;
+    if (rc)
+        goto out;
+
+    rc = qemu_pci_add_xenstore(gc, domid, pcidev);
+out:
+    pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qmp_device_add(libxl__egc *egc, pci_add_state *pas)
+{
+    STATE_AO_GC(pas->aodev->ao);
+    libxl__json_object *args = NULL;
+    int rc;
+
+    /* Convenience aliases */
+    libxl_domid domid = pas->domid;
+    libxl_device_pci *pcidev = pas->pcidev;
+    libxl__ev_qmp *const qmp = &pas->qmp;
+
+    rc = libxl__ev_time_register_rel(ao, &pas->timeout,
+                                     pci_add_timeout,
+                                     LIBXL_QMP_CMD_TIMEOUT * 1000);
+    if (rc) goto out;
+
+    libxl__qmp_param_add_string(gc, &args, "driver",
+                                "xen-pci-passthrough");
+    QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
+                           pcidev->bus, pcidev->dev, pcidev->func);
+    QMP_PARAMETERS_SPRINTF(&args, "hostaddr",
+                           "%04x:%02x:%02x.%01x", pcidev->domain,
+                           pcidev->bus, pcidev->dev, pcidev->func);
+    if (pcidev->vdevfn) {
+        QMP_PARAMETERS_SPRINTF(&args, "addr", "%x.%x",
+                               PCI_SLOT(pcidev->vdevfn),
+                               PCI_FUNC(pcidev->vdevfn));
+    }
+    /*
+     * Version of QEMU prior to the XSA-131 fix did not support
+     * this property and were effectively always in permissive
+     * mode. The fix for XSA-131 switched the default to be
+     * restricted by default and added the permissive property.
+     *
+     * Therefore in order to support both old and new QEMU we only
+     * set the permissive flag if it is true. Users of older QEMU
+     * have no reason to set the flag so this is ok.
+     */
+    if (pcidev->permissive)
+        libxl__qmp_param_add_bool(gc, &args, "permissive", true);
+
+    qmp->ao = pas->aodev->ao;
+    qmp->domid = domid;
+    qmp->payload_fd = -1;
+    qmp->callback = pci_add_qmp_device_add_cb;
+    rc = libxl__ev_qmp_send(egc, qmp, "device_add", args);
+    if (rc) goto out;
+    return;
+
+out:
+    pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qmp_device_add_cb(libxl__egc *egc,
+                                      libxl__ev_qmp *qmp,
+                                      const libxl__json_object *response,
+                                      int rc)
+{
+    EGC_GC;
+    pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
+
+    if (rc) goto out;
+
+    qmp->callback = pci_add_qmp_query_pci_cb;
+    rc = libxl__ev_qmp_send(egc, qmp, "query-pci", NULL);
+    if (rc) goto out;
+    return;
+
+out:
+    pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_qmp_query_pci_cb(libxl__egc *egc,
+                                     libxl__ev_qmp *qmp,
+                                     const libxl__json_object *response,
+                                     int rc)
+{
+    EGC_GC;
+    pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
+    const libxl__json_object *bus = NULL;
+    char *asked_id;
+    int i, j;
+    const libxl__json_object *devices = NULL;
+    const libxl__json_object *device = NULL;
+    const libxl__json_object *o = NULL;
+    const char *id = NULL;
+    int dev_slot, dev_func;
+
+    /* Convenience aliases */
+    libxl_device_pci *pcidev = pas->pcidev;
+
+    if (rc) goto out;
+
+    /* `query-pci' returns:
+     * [
+     *   {'bus': 'int',
+     *    'devices': [
+     *       {'bus': 'int', 'slot': 'int', 'function': 'int',
+     *        'class_info': 'PciDeviceClass', 'id': 'PciDeviceId',
+     *        '*irq': 'int', 'qdev_id': 'str',
+     *        '*pci_bridge': 'PciBridgeInfo',
+     *        'regions': ['PciMemoryRegion']
+     *       }
+     *    ]
+     *   }
+     * ]
+     * (See qemu.git/qapi/ for the struct that aren't detailed here)
+     */
+
+    asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
+                         pcidev->bus, pcidev->dev, pcidev->func);
+
+    for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
+        devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
+        if (!devices) {
+            rc = ERROR_QEMU_API;
+            goto out;
+        }
+
+        for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
+             o = libxl__json_map_get("qdev_id", device, JSON_STRING);
+             if (!o) {
+                 rc = ERROR_QEMU_API;
+                 goto out;
+             }
+             id = libxl__json_object_get_string(o);
+             if (!id || strcmp(asked_id, id))
+                 continue;
+
+             o = libxl__json_map_get("slot", device, JSON_INTEGER);
+             if (!o) {
+                 rc = ERROR_QEMU_API;
+                 goto out;
+             }
+             dev_slot = libxl__json_object_get_integer(o);
+             o = libxl__json_map_get("function", device, JSON_INTEGER);
+             if (!o) {
+                 rc = ERROR_QEMU_API;
+                 goto out;
+             }
+             dev_func = libxl__json_object_get_integer(o);
+
+             pcidev->vdevfn = PCI_DEVFN(dev_slot, dev_func);
+
+             rc = 0;
+             goto out;
+        }
+    }
+
+    rc = ERROR_FAIL;
+    LOGD(ERROR, qmp->domid,
+         "PCI device id '%s' wasn't found in QEMU's 'query-pci' response.",
+         asked_id);
+
+out:
+    if (rc == ERROR_QEMU_API) {
+        LOGD(ERROR, qmp->domid,
+             "Unexpected response to QMP cmd 'query-pci', received:\n%s",
+             JSON(response));
+    }
+    pci_add_dm_done(egc, pas, rc); /* must be last */
+}
+
+static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                            const struct timeval *requested_abs,
+                            int rc)
+{
+    pci_add_state *pas = CONTAINER_OF(ev, *pas, timeout);
+
+    pci_add_dm_done(egc, pas, rc);
+}
+
+static void pci_add_dm_done(libxl__egc *egc,
+                            pci_add_state *pas,
+                            int rc)
+{
+    STATE_AO_GC(pas->aodev->ao);
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    libxl_domid domid = pas->pci_domid;
+    char *sysfs_path;
+    FILE *f;
+    unsigned long long start, end, flags, size;
+    int irq, i;
+    int r;
+    uint32_t flag = XEN_DOMCTL_DEV_RDM_RELAXED;
+    uint32_t domainid = domid;
+    bool isstubdom = libxl_is_stubdom(ctx, domid, &domainid);
+
+    /* Convenience aliases */
+    bool starting = pas->starting;
+    libxl_device_pci *pcidev = pas->pcidev;
+    bool hvm = libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM;
+
+    libxl__ev_qmp_dispose(gc, &pas->qmp);
+
+    if (rc) goto out;
+
+    /* stubdomain is always running by now, even at create time */
+    if (isstubdom)
+        starting = false;
+
+    sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
+                           pcidev->bus, pcidev->dev, pcidev->func);
+    f = fopen(sysfs_path, "r");
+    start = end = flags = size = 0;
+    irq = 0;
+
+    if (f == NULL) {
+        LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
+        if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3)
+            continue;
+        size = end - start + 1;
+        if (start) {
+            if (flags & PCI_BAR_IO) {
+                r = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
+                if (r < 0) {
+                    LOGED(ERROR, domainid,
+                          "xc_domain_ioport_permission 0x%llx/0x%llx (error %d)",
+                          start, size, r);
+                    fclose(f);
+                    rc = ERROR_FAIL;
+                    goto out;
+                }
+            } else {
+                r = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
+                                                (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
+                if (r < 0) {
+                    LOGED(ERROR, domainid,
+                          "xc_domain_iomem_permission 0x%llx/0x%llx (error %d)",
+                          start, size, r);
+                    fclose(f);
+                    rc = ERROR_FAIL;
+                    goto out;
+                }
+            }
+        }
+    }
+    fclose(f);
+    sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
+                                pcidev->bus, pcidev->dev, pcidev->func);
+    f = fopen(sysfs_path, "r");
+    if (f == NULL) {
+        LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+        goto out_no_irq;
+    }
+    if ((fscanf(f, "%u", &irq) == 1) && irq) {
+        r = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
+        if (r < 0) {
+            LOGED(ERROR, domainid, "xc_physdev_map_pirq irq=%d (error=%d)",
+                  irq, r);
+            fclose(f);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        r = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
+        if (r < 0) {
+            LOGED(ERROR, domainid,
+                  "xc_domain_irq_permission irq=%d (error=%d)", irq, r);
+            fclose(f);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+    fclose(f);
+
+    /* Don't restrict writes to the PCI config space from this VM */
+    if (pcidev->permissive) {
+        if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/permissive",
+                             pcidev) < 0 ) {
+            LOGD(ERROR, domainid, "Setting permissive for device");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+out_no_irq:
+    if (!isstubdom) {
+        if (pcidev->rdm_policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
+            flag &= ~XEN_DOMCTL_DEV_RDM_RELAXED;
+        } else if (pcidev->rdm_policy != LIBXL_RDM_RESERVE_POLICY_RELAXED) {
+            LOGED(ERROR, domainid, "unknown rdm check flag.");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        r = xc_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev), flag);
+        if (r < 0 && (hvm || errno != ENOSYS)) {
+            LOGED(ERROR, domainid, "xc_assign_device failed");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    if (!starting && !libxl_get_stubdom_id(CTX, domid))
+        rc = libxl__device_pci_add_xenstore(gc, domid, pcidev, starting);
+    else
+        rc = 0;
+out:
+    libxl__ev_time_deregister(gc, &pas->timeout);
+    pas->callback(egc, pas, rc);
+}
+
+static int libxl__device_pci_reset(libxl__gc *gc, unsigned int domain, unsigned int bus,
+                                   unsigned int dev, unsigned int func)
+{
+    char *reset;
+    int fd, rc;
+
+    reset = GCSPRINTF("%s/do_flr", SYSFS_PCIBACK_DRIVER);
+    fd = open(reset, O_WRONLY);
+    if (fd >= 0) {
+        char *buf = GCSPRINTF(PCI_BDF, domain, bus, dev, func);
+        rc = write(fd, buf, strlen(buf));
+        if (rc < 0)
+            LOGD(ERROR, domain, "write to %s returned %d", reset, rc);
+        close(fd);
+        return rc < 0 ? rc : 0;
+    }
+    if (errno != ENOENT)
+        LOGED(ERROR, domain, "Failed to access pciback path %s", reset);
+    reset = GCSPRINTF("%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func);
+    fd = open(reset, O_WRONLY);
+    if (fd >= 0) {
+        rc = write(fd, "1", 1);
+        if (rc < 0)
+            LOGED(ERROR, domain, "write to %s returned %d", reset, rc);
+        close(fd);
+        return rc < 0 ? rc : 0;
+    }
+    if (errno == ENOENT) {
+        LOGD(ERROR, domain,
+             "The kernel doesn't support reset from sysfs for PCI device "PCI_BDF,
+             domain, bus, dev, func);
+    } else {
+        LOGED(ERROR, domain, "Failed to access reset path %s", reset);
+    }
+    return -1;
+}
+
+int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
+                                 libxl_device_pci *pci, bool hotplug)
+{
+    /* We'd like to force reserve rdm specific to a device by default.*/
+    if (pci->rdm_policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
+        pci->rdm_policy = LIBXL_RDM_RESERVE_POLICY_STRICT;
+    return 0;
+}
+
+int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
+                         libxl_device_pci *pcidev,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__ao_device *aodev;
+
+    GCNEW(aodev);
+    libxl__prepare_ao_device(ao, aodev);
+    aodev->action = LIBXL__DEVICE_ACTION_ADD;
+    aodev->callback = device_addrm_aocomplete;
+    aodev->update_json = true;
+    libxl__device_pci_add(egc, domid, pcidev, false, aodev);
+    return AO_INPROGRESS;
+}
+
+static int libxl_pcidev_assignable(libxl_ctx *ctx, libxl_device_pci *pcidev)
+{
+    libxl_device_pci *pcidevs;
+    int num, i;
+
+    pcidevs = libxl_device_pci_assignable_list(ctx, &num);
+    for (i = 0; i < num; i++) {
+        if (pcidevs[i].domain == pcidev->domain &&
+            pcidevs[i].bus == pcidev->bus &&
+            pcidevs[i].dev == pcidev->dev &&
+            pcidevs[i].func == pcidev->func)
+            break;
+    }
+    free(pcidevs);
+    return i != num;
+}
+
+static void device_pci_add_stubdom_wait(libxl__egc *egc,
+    pci_add_state *pas, int rc);
+static void device_pci_add_stubdom_ready(libxl__egc *egc,
+    libxl__ev_devstate *ds, int rc);
+static void device_pci_add_stubdom_done(libxl__egc *egc,
+    pci_add_state *, int rc);
+static void device_pci_add_done(libxl__egc *egc,
+    pci_add_state *, int rc);
+
+void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
+                           libxl_device_pci *pcidev, bool starting,
+                           libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    libxl_device_pci *assigned;
+    int num_assigned, rc;
+    int stubdomid = 0;
+    pci_add_state *pas;
+
+    /* Store *pcidev to be used by callbacks */
+    aodev->device_config = pcidev;
+    aodev->device_type = &libxl__pcidev_devtype;
+
+    GCNEW(pas);
+    pas->aodev = aodev;
+    pas->domid = domid;
+    pas->starting = starting;
+    pas->callback = device_pci_add_stubdom_done;
+
+    if (libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
+        rc = xc_test_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev));
+        if (rc) {
+            LOGD(ERROR, domid,
+                 "PCI device %04x:%02x:%02x.%u %s?",
+                 pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
+                 errno == EOPNOTSUPP ? "cannot be assigned - no IOMMU"
+                 : "already assigned to a different guest");
+            goto out;
+        }
+    }
+
+    rc = libxl__device_pci_setdefault(gc, domid, pcidev, !starting);
+    if (rc) goto out;
+
+    if (pcidev->seize && !pciback_dev_is_assigned(gc, pcidev)) {
+        rc = libxl__device_pci_assignable_add(gc, pcidev, 1);
+        if ( rc )
+            goto out;
+    }
+
+    if (!libxl_pcidev_assignable(ctx, pcidev)) {
+        LOGD(ERROR, domid, "PCI device %x:%x:%x.%x is not assignable",
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = get_all_assigned_devices(gc, &assigned, &num_assigned);
+    if ( rc ) {
+        LOGD(ERROR, domid,
+             "cannot determine if device is assigned, refusing to continue");
+        goto out;
+    }
+    if ( is_pcidev_in_array(assigned, num_assigned, pcidev->domain,
+                     pcidev->bus, pcidev->dev, pcidev->func) ) {
+        LOGD(ERROR, domid, "PCI device already attached to a domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+
+    stubdomid = libxl_get_stubdom_id(ctx, domid);
+    if (stubdomid != 0) {
+        libxl_device_pci *pcidev_s;
+
+        GCNEW(pcidev_s);
+        libxl_device_pci_init(pcidev_s);
+        libxl_device_pci_copy(CTX, pcidev_s, pcidev);
+        pas->callback = device_pci_add_stubdom_wait;
+
+        do_pci_add(egc, stubdomid, pcidev_s, pas); /* must be last */
+        return;
+    }
+
+    device_pci_add_stubdom_done(egc, pas, 0); /* must be last */
+    return;
+
+out:
+    device_pci_add_done(egc, pas, rc); /* must be last */
+}
+
+static void device_pci_add_stubdom_wait(libxl__egc *egc,
+                                        pci_add_state *pas,
+                                        int rc)
+{
+    libxl__ao_device *aodev = pas->aodev;
+    STATE_AO_GC(aodev->ao);
+    int stubdomid = libxl_get_stubdom_id(CTX, pas->domid);
+    char *state_path;
+
+    if (rc) goto out;
+
+    /* Wait for the device actually being connected, otherwise device model
+     * running there will fail to find the device. */
+    state_path = GCSPRINTF("%s/state",
+            libxl__domain_device_backend_path(gc, 0, stubdomid, 0,
+                                              LIBXL__DEVICE_KIND_PCI));
+    rc = libxl__ev_devstate_wait(ao, &pas->pciback_ds,
+            device_pci_add_stubdom_ready,
+            state_path, XenbusStateConnected,
+            LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
+    if (rc) goto out;
+    return;
+out:
+    device_pci_add_done(egc, pas, rc); /* must be last */
+}
+
+static void device_pci_add_stubdom_ready(libxl__egc *egc,
+                                         libxl__ev_devstate *ds,
+                                         int rc)
+{
+    pci_add_state *pas = CONTAINER_OF(ds, *pas, pciback_ds);
+
+    device_pci_add_stubdom_done(egc, pas, rc); /* must be last */
+}
+
+static void device_pci_add_stubdom_done(libxl__egc *egc,
+                                        pci_add_state *pas,
+                                        int rc)
+{
+    STATE_AO_GC(pas->aodev->ao);
+    unsigned int orig_vdev, pfunc_mask;
+    int i;
+
+    /* Convenience aliases */
+    libxl__ao_device *aodev = pas->aodev;
+    libxl_domid domid = pas->domid;
+    libxl_device_pci *pcidev = aodev->device_config;
+
+    if (rc) goto out;
+
+    orig_vdev = pcidev->vdevfn & ~7U;
+
+    if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
+        if ( !(pcidev->vdevfn >> 3) ) {
+            LOGD(ERROR, domid, "Must specify a v-slot for multi-function devices");
+            rc = ERROR_INVAL;
+            goto out;
+        }
+        if ( pci_multifunction_check(gc, pcidev, &pfunc_mask) ) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        pcidev->vfunc_mask &= pfunc_mask;
+        /* so now vfunc_mask == pfunc_mask */
+    }else{
+        pfunc_mask = (1 << pcidev->func);
+    }
+
+    for(rc = 0, i = 7; i >= 0; --i) {
+        if ( (1 << i) & pfunc_mask ) {
+            if ( pcidev->vfunc_mask == pfunc_mask ) {
+                pcidev->func = i;
+                pcidev->vdevfn = orig_vdev | i;
+            }else{
+                /* if not passing through multiple devices in a block make
+                 * sure that virtual function number 0 is always used otherwise
+                 * guest won't see the device
+                 */
+                pcidev->vdevfn = orig_vdev;
+            }
+            pas->callback = device_pci_add_done;
+            do_pci_add(egc, domid, pcidev, pas); /* must be last */
+            return;
+        }
+    }
+
+out:
+    device_pci_add_done(egc, pas, rc);
+}
+
+static void device_pci_add_done(libxl__egc *egc,
+                                pci_add_state *pas,
+                                int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = pas->aodev;
+    libxl_domid domid = pas->domid;
+    libxl_device_pci *pcidev = aodev->device_config;
+
+    if (rc) {
+        LOGD(ERROR, domid,
+             "libxl__device_pci_add  failed for "
+             "PCI device %x:%x:%x.%x (rc %d)",
+             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
+             rc);
+    }
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+typedef struct {
+    libxl__multidev multidev;
+    libxl__ao_device *outer_aodev;
+    libxl_domain_config *d_config;
+    libxl_domid domid;
+} add_pcidevs_state;
+
+static void add_pcidevs_done(libxl__egc *, libxl__multidev *, int rc);
+
+static void libxl__add_pcidevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+                               libxl_domain_config *d_config,
+                               libxl__multidev *multidev)
+{
+    AO_GC;
+    add_pcidevs_state *apds;
+    int i;
+
+    /* We need to start a new multidev in order to be able to execute
+     * libxl__create_pci_backend only once. */
+
+    GCNEW(apds);
+    apds->outer_aodev = libxl__multidev_prepare(multidev);
+    apds->d_config = d_config;
+    apds->domid = domid;
+    apds->multidev.callback = add_pcidevs_done;
+    libxl__multidev_begin(ao, &apds->multidev);
+
+    for (i = 0; i < d_config->num_pcidevs; i++) {
+        libxl__ao_device *aodev = libxl__multidev_prepare(&apds->multidev);
+        libxl__device_pci_add(egc, domid, &d_config->pcidevs[i],
+                              true, aodev);
+    }
+
+    libxl__multidev_prepared(egc, &apds->multidev, 0);
+}
+
+static void add_pcidevs_done(libxl__egc *egc, libxl__multidev *multidev,
+                             int rc)
+{
+    EGC_GC;
+    add_pcidevs_state *apds = CONTAINER_OF(multidev, *apds, multidev);
+
+    /* Convenience aliases */
+    libxl_domain_config *d_config = apds->d_config;
+    libxl_domid domid = apds->domid;
+    libxl__ao_device *aodev = apds->outer_aodev;
+
+    if (rc) goto out;
+
+    if (d_config->num_pcidevs > 0 && !libxl_get_stubdom_id(CTX, domid)) {
+        rc = libxl__create_pci_backend(gc, domid, d_config->pcidevs,
+            d_config->num_pcidevs);
+        if (rc < 0) {
+            LOGD(ERROR, domid, "libxl_create_pci_backend failed: %d", rc);
+            goto out;
+        }
+    }
+
+out:
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+static int qemu_pci_remove_xenstore(libxl__gc *gc, uint32_t domid,
+                                    libxl_device_pci *pcidev, int force)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *state;
+    char *path;
+    uint32_t dm_domid;
+
+    dm_domid = libxl_get_stubdom_id(CTX, domid);
+
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+    state = libxl__xs_read(gc, XBT_NULL, path);
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
+    libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF, pcidev->domain,
+                     pcidev->bus, pcidev->dev, pcidev->func);
+
+    /* Remove all functions at once atomically by only signalling
+     * device-model for function 0 */
+    if ( !force && (pcidev->vdevfn & 0x7) == 0 ) {
+        libxl__qemu_traditional_cmd(gc, domid, "pci-rem");
+        if (libxl__wait_for_device_model_deprecated(gc, domid, "pci-removed",
+                                         NULL, NULL, NULL) < 0) {
+            LOGD(ERROR, domid, "Device Model didn't respond in time");
+            /* This depends on guest operating system acknowledging the
+             * SCI, if it doesn't respond in time then we may wish to
+             * force the removal.
+             */
+            return ERROR_FAIL;
+        }
+    }
+    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
+    xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
+
+    return 0;
+}
+
+typedef struct pci_remove_state {
+    libxl__ao_device *aodev;
+    libxl_domid domid;
+    libxl_device_pci *pcidev;
+    bool force;
+    bool hvm;
+    unsigned int orig_vdev;
+    unsigned int pfunc_mask;
+    int next_func;
+    libxl__ao_device stubdom_aodev;
+    libxl__xswait_state xswait;
+    libxl__ev_qmp qmp;
+    libxl__ev_time timeout;
+    libxl__ev_time retry_timer;
+} pci_remove_state;
+
+static void libxl__device_pci_remove_common(libxl__egc *egc,
+    uint32_t domid, libxl_device_pci *pcidev, bool force,
+    libxl__ao_device *aodev);
+static void device_pci_remove_common_next(libxl__egc *egc,
+    pci_remove_state *prs, int rc);
+
+static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
+    libxl__xswait_state *xswa, int rc, const char *state);
+static void pci_remove_qmp_device_del(libxl__egc *egc,
+    pci_remove_state *prs);
+static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void pci_remove_qmp_query_cb(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
+static void pci_remove_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void pci_remove_detatched(libxl__egc *egc,
+    pci_remove_state *prs, int rc);
+static void pci_remove_stubdom_done(libxl__egc *egc,
+    libxl__ao_device *aodev);
+static void pci_remove_done(libxl__egc *egc,
+    pci_remove_state *prs, int rc);
+
+static void do_pci_remove(libxl__egc *egc, uint32_t domid,
+                          libxl_device_pci *pcidev, int force,
+                          pci_remove_state *prs)
+{
+    STATE_AO_GC(prs->aodev->ao);
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    libxl_device_pci *assigned;
+    libxl_domain_type type = libxl__domain_type(gc, domid);
+    int rc, num;
+    uint32_t domainid = domid;
+
+    assigned = libxl_device_pci_list(ctx, domid, &num);
+    if (assigned == NULL) {
+        rc = ERROR_FAIL;
+        goto out_fail;
+    }
+    libxl__ptr_add(gc, assigned);
+
+    rc = ERROR_INVAL;
+    if ( !is_pcidev_in_array(assigned, num, pcidev->domain,
+                      pcidev->bus, pcidev->dev, pcidev->func) ) {
+        LOGD(ERROR, domainid, "PCI device not attached to this domain");
+        goto out_fail;
+    }
+
+    rc = ERROR_FAIL;
+    if (type == LIBXL_DOMAIN_TYPE_HVM) {
+        prs->hvm = true;
+        switch (libxl__device_model_version_running(gc, domid)) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            prs->xswait.ao = ao;
+            prs->xswait.what = "Device Model";
+            prs->xswait.path = DEVICE_MODEL_XS_PATH(gc,
+                libxl_get_stubdom_id(CTX, domid), domid, "/state");
+            prs->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
+            prs->xswait.callback = pci_remove_qemu_trad_watch_state_cb;
+            rc = libxl__xswait_start(gc, &prs->xswait);
+            if (rc) goto out_fail;
+            return;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            pci_remove_qmp_device_del(egc, prs); /* must be last */
+            return;
+        default:
+            rc = ERROR_INVAL;
+            goto out_fail;
+        }
+    } else {
+        assert(type == LIBXL_DOMAIN_TYPE_PV);
+
+        char *sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
+                                     pcidev->bus, pcidev->dev, pcidev->func);
+        FILE *f = fopen(sysfs_path, "r");
+        unsigned int start = 0, end = 0, flags = 0, size = 0;
+        int irq = 0;
+        int i;
+
+        if (f == NULL) {
+            LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+            goto skip1;
+        }
+        for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
+            if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3)
+                continue;
+            size = end - start + 1;
+            if (start) {
+                if (flags & PCI_BAR_IO) {
+                    rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
+                    if (rc < 0)
+                        LOGED(ERROR, domainid,
+                              "xc_domain_ioport_permission error 0x%x/0x%x",
+                              start,
+                              size);
+                } else {
+                    rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
+                                                    (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
+                    if (rc < 0)
+                        LOGED(ERROR, domainid,
+                              "xc_domain_iomem_permission error 0x%x/0x%x",
+                              start,
+                              size);
+                }
+            }
+        }
+        fclose(f);
+skip1:
+        sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
+                               pcidev->bus, pcidev->dev, pcidev->func);
+        f = fopen(sysfs_path, "r");
+        if (f == NULL) {
+            LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
+            goto skip_irq;
+        }
+        if ((fscanf(f, "%u", &irq) == 1) && irq) {
+            rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
+            if (rc < 0) {
+                LOGED(ERROR, domainid, "xc_physdev_unmap_pirq irq=%d", irq);
+            }
+            rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
+            if (rc < 0) {
+                LOGED(ERROR, domainid, "xc_domain_irq_permission irq=%d", irq);
+            }
+        }
+        fclose(f);
+    }
+skip_irq:
+    rc = 0;
+out_fail:
+    pci_remove_detatched(egc, prs, rc); /* must be last */
+}
+
+static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
+                                                libxl__xswait_state *xswa,
+                                                int rc,
+                                                const char *state)
+{
+    pci_remove_state *prs = CONTAINER_OF(xswa, *prs, xswait);
+    STATE_AO_GC(prs->aodev->ao);
+
+    /* Convenience aliases */
+    libxl_domid domid = prs->domid;
+    libxl_device_pci *const pcidev = prs->pcidev;
+
+    rc = check_qemu_running(gc, domid, xswa, rc, state);
+    if (rc == ERROR_NOT_READY)
+        return;
+    if (rc)
+        goto out;
+
+    rc = qemu_pci_remove_xenstore(gc, domid, pcidev, prs->force);
+
+out:
+    pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_device_del(libxl__egc *egc,
+                                      pci_remove_state *prs)
+{
+    STATE_AO_GC(prs->aodev->ao);
+    libxl__json_object *args = NULL;
+    int rc;
+
+    /* Convenience aliases */
+    libxl_device_pci *const pcidev = prs->pcidev;
+
+    rc = libxl__ev_time_register_rel(ao, &prs->timeout,
+                                     pci_remove_timeout,
+                                     LIBXL_QMP_CMD_TIMEOUT * 1000);
+    if (rc) goto out;
+
+    QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
+                           pcidev->bus, pcidev->dev, pcidev->func);
+    prs->qmp.callback = pci_remove_qmp_device_del_cb;
+    rc = libxl__ev_qmp_send(egc, &prs->qmp, "device_del", args);
+    if (rc) goto out;
+    return;
+
+out:
+    pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
+                                         libxl__ev_qmp *qmp,
+                                         const libxl__json_object *response,
+                                         int rc)
+{
+    EGC_GC;
+    pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
+
+    if (rc) goto out;
+
+    /* Now that the command is sent, we want to wait until QEMU has
+     * confirmed that the device is removed. */
+    /* TODO: Instead of using a poll loop { ev_timer ; query-pci }, it
+     * could be possible to listen to events sent by QEMU via QMP in order
+     * to wait for the passthrough pci-device to be removed from QEMU.  */
+    pci_remove_qmp_retry_timer_cb(egc, &prs->retry_timer, NULL,
+                                  ERROR_TIMEDOUT);
+    return;
+
+out:
+    pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc, libxl__ev_time *ev,
+                                          const struct timeval *requested_abs,
+                                          int rc)
+{
+    EGC_GC;
+    pci_remove_state *prs = CONTAINER_OF(ev, *prs, retry_timer);
+
+    prs->qmp.callback = pci_remove_qmp_query_cb;
+    rc = libxl__ev_qmp_send(egc, &prs->qmp, "query-pci", NULL);
+    if (rc) goto out;
+    return;
+
+out:
+    pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_qmp_query_cb(libxl__egc *egc,
+                                    libxl__ev_qmp *qmp,
+                                    const libxl__json_object *response,
+                                    int rc)
+{
+    EGC_GC;
+    pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
+    const libxl__json_object *bus = NULL;
+    const char *asked_id;
+    int i, j;
+
+    /* Convenience aliases */
+    libxl__ao *const ao = prs->aodev->ao;
+    libxl_device_pci *const pcidev = prs->pcidev;
+
+    if (rc) goto out;
+
+    libxl__ev_qmp_dispose(gc, qmp);
+
+    asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
+                         pcidev->bus, pcidev->dev, pcidev->func);
+
+    /* query-pci response:
+     * [{ 'devices': [ 'qdev_id': 'str', ...  ], ... }]
+     * */
+
+    for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
+        const libxl__json_object *devices = NULL;
+        const libxl__json_object *device = NULL;
+        const libxl__json_object *o = NULL;
+        const char *id = NULL;
+
+        devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
+        if (!devices) {
+            rc = ERROR_QEMU_API;
+            goto out;
+        }
+
+        for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
+             o = libxl__json_map_get("qdev_id", device, JSON_STRING);
+             if (!o) {
+                 rc = ERROR_QEMU_API;
+                 goto out;
+             }
+             id = libxl__json_object_get_string(o);
+
+             if (id && !strcmp(asked_id, id)) {
+                 /* Device still in QEMU, need to wait longuer. */
+                 rc = libxl__ev_time_register_rel(ao, &prs->retry_timer,
+                     pci_remove_qmp_retry_timer_cb, 1000);
+                 if (rc) goto out;
+                 return;
+             }
+        }
+    }
+
+out:
+    pci_remove_detatched(egc, prs, rc); /* must be last */
+}
+
+static void pci_remove_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                               const struct timeval *requested_abs,
+                               int rc)
+{
+    EGC_GC;
+    pci_remove_state *prs = CONTAINER_OF(ev, *prs, timeout);
+
+    /* Convenience aliases */
+    libxl_device_pci *const pcidev = prs->pcidev;
+
+    LOGD(WARN, prs->domid, "timed out waiting for DM to remove "
+         PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func);
+
+    /* If we timed out, we might still want to keep destroying the device
+     * (when force==true), so let the next function decide what to do on
+     * error */
+    pci_remove_detatched(egc, prs, rc);
+}
+
+static void pci_remove_detatched(libxl__egc *egc,
+                                 pci_remove_state *prs,
+                                 int rc)
+{
+    STATE_AO_GC(prs->aodev->ao);
+    int stubdomid = 0;
+    uint32_t domainid = prs->domid;
+    bool isstubdom;
+
+    /* Convenience aliases */
+    libxl_device_pci *const pcidev = prs->pcidev;
+    libxl_domid domid = prs->domid;
+
+    /* Cleaning QMP states ASAP */
+    libxl__ev_qmp_dispose(gc, &prs->qmp);
+    libxl__ev_time_deregister(gc, &prs->timeout);
+    libxl__ev_time_deregister(gc, &prs->retry_timer);
+
+    if (rc && !prs->force)
+        goto out;
+
+    isstubdom = libxl_is_stubdom(CTX, domid, &domainid);
+
+    /* don't do multiple resets while some functions are still passed through */
+    if ( (pcidev->vdevfn & 0x7) == 0 ) {
+        libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
+    }
+
+    if (!isstubdom) {
+        rc = xc_deassign_device(CTX->xch, domid, pcidev_encode_bdf(pcidev));
+        if (rc < 0 && (prs->hvm || errno != ENOSYS))
+            LOGED(ERROR, domainid, "xc_deassign_device failed");
+    }
+
+    stubdomid = libxl_get_stubdom_id(CTX, domid);
+    if (stubdomid != 0) {
+        libxl_device_pci *pcidev_s;
+        libxl__ao_device *const stubdom_aodev = &prs->stubdom_aodev;
+
+        GCNEW(pcidev_s);
+        libxl_device_pci_init(pcidev_s);
+        libxl_device_pci_copy(CTX, pcidev_s, pcidev);
+
+        libxl__prepare_ao_device(ao, stubdom_aodev);
+        stubdom_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+        stubdom_aodev->callback = pci_remove_stubdom_done;
+        stubdom_aodev->update_json = prs->aodev->update_json;
+        libxl__device_pci_remove_common(egc, stubdomid, pcidev_s,
+                                        prs->force, stubdom_aodev);
+        return;
+    }
+
+    rc = 0;
+out:
+    pci_remove_done(egc, prs, rc);
+}
+
+static void pci_remove_stubdom_done(libxl__egc *egc,
+                                    libxl__ao_device *aodev)
+{
+    pci_remove_state *prs = CONTAINER_OF(aodev, *prs, stubdom_aodev);
+
+    pci_remove_done(egc, prs, 0);
+}
+
+static void pci_remove_done(libxl__egc *egc,
+                            pci_remove_state *prs,
+                            int rc)
+{
+    EGC_GC;
+
+    if (rc) goto out;
+
+    libxl__device_pci_remove_xenstore(gc, prs->domid, prs->pcidev);
+out:
+    device_pci_remove_common_next(egc, prs, rc);
+}
+
+static void libxl__device_pci_remove_common(libxl__egc *egc,
+                                            uint32_t domid,
+                                            libxl_device_pci *pcidev,
+                                            bool force,
+                                            libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    int rc;
+    pci_remove_state *prs;
+
+    GCNEW(prs);
+    prs->aodev = aodev;
+    prs->domid = domid;
+    prs->pcidev = pcidev;
+    prs->force = force;
+    libxl__xswait_init(&prs->xswait);
+    libxl__ev_qmp_init(&prs->qmp);
+    prs->qmp.ao = prs->aodev->ao;
+    prs->qmp.domid = prs->domid;
+    prs->qmp.payload_fd = -1;
+    libxl__ev_time_init(&prs->timeout);
+    libxl__ev_time_init(&prs->retry_timer);
+
+    prs->orig_vdev = pcidev->vdevfn & ~7U;
+
+    if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
+        if ( pci_multifunction_check(gc, pcidev, &prs->pfunc_mask) ) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        pcidev->vfunc_mask &= prs->pfunc_mask;
+    }else{
+        prs->pfunc_mask = (1 << pcidev->func);
+    }
+
+    rc = 0;
+    prs->next_func = 7;
+out:
+    device_pci_remove_common_next(egc, prs, rc);
+}
+
+static void device_pci_remove_common_next(libxl__egc *egc,
+                                          pci_remove_state *prs,
+                                          int rc)
+{
+    EGC_GC;
+
+    /* Convenience aliases */
+    libxl_domid domid = prs->domid;
+    libxl_device_pci *const pcidev = prs->pcidev;
+    libxl__ao_device *const aodev = prs->aodev;
+    const unsigned int pfunc_mask = prs->pfunc_mask;
+    const unsigned int orig_vdev = prs->orig_vdev;
+
+    if (rc) goto out;
+
+    while (prs->next_func >= 0) {
+        const int i = prs->next_func;
+        prs->next_func--;
+        if ( (1 << i) & pfunc_mask ) {
+            if ( pcidev->vfunc_mask == pfunc_mask ) {
+                pcidev->func = i;
+                pcidev->vdevfn = orig_vdev | i;
+            }else{
+                pcidev->vdevfn = orig_vdev;
+            }
+            do_pci_remove(egc, domid, pcidev, prs->force, prs);
+            return;
+        }
+    }
+
+    rc = 0;
+out:
+    libxl__ev_qmp_dispose(gc, &prs->qmp);
+    libxl__xswait_stop(gc, &prs->xswait);
+    libxl__ev_time_deregister(gc, &prs->timeout);
+    libxl__ev_time_deregister(gc, &prs->retry_timer);
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_pci *pcidev,
+                            const libxl_asyncop_how *ao_how)
+
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__ao_device *aodev;
+
+    GCNEW(aodev);
+    libxl__prepare_ao_device(ao, aodev);
+    aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+    aodev->callback = device_addrm_aocomplete;
+    aodev->update_json = true;
+    libxl__device_pci_remove_common(egc, domid, pcidev, false, aodev);
+    return AO_INPROGRESS;
+}
+
+int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_pci *pcidev,
+                             const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__ao_device *aodev;
+
+    GCNEW(aodev);
+    libxl__prepare_ao_device(ao, aodev);
+    aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+    aodev->callback = device_addrm_aocomplete;
+    aodev->update_json = true;
+    libxl__device_pci_remove_common(egc, domid, pcidev, true, aodev);
+    return AO_INPROGRESS;
+}
+
+static int libxl__device_pci_from_xs_be(libxl__gc *gc,
+                                        const char *be_path,
+                                        libxl_devid nr, void *data)
+{
+    char *s;
+    unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
+    libxl_device_pci *pci = data;
+
+    s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, nr));
+    sscanf(s, PCI_BDF, &domain, &bus, &dev, &func);
+
+    s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vdevfn-%d", be_path, nr));
+    if (s)
+        vdevfn = strtol(s, (char **) NULL, 16);
+
+    pcidev_struct_fill(pci, domain, bus, dev, func, vdevfn);
+
+    s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/opts-%d", be_path, nr));
+    if (s) {
+        char *saveptr;
+        char *p = strtok_r(s, ",=", &saveptr);
+        do {
+            while (*p == ' ')
+                p++;
+            if (!strcmp(p, "msitranslate")) {
+                p = strtok_r(NULL, ",=", &saveptr);
+                pci->msitranslate = atoi(p);
+            } else if (!strcmp(p, "power_mgmt")) {
+                p = strtok_r(NULL, ",=", &saveptr);
+                pci->power_mgmt = atoi(p);
+            } else if (!strcmp(p, "permissive")) {
+                p = strtok_r(NULL, ",=", &saveptr);
+                pci->permissive = atoi(p);
+            }
+        } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
+    }
+
+    return 0;
+}
+
+static int libxl__device_pci_get_num(libxl__gc *gc, const char *be_path,
+                                     unsigned int *num)
+{
+    char *num_devs;
+    int rc = 0;
+
+    num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
+    if (!num_devs)
+        rc = ERROR_FAIL;
+    else
+        *num = atoi(num_devs);
+
+    return rc;
+}
+
+libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+    char *be_path;
+    unsigned int n, i;
+    libxl_device_pci *pcidevs = NULL;
+
+    *num = 0;
+
+    be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
+                                                LIBXL__DEVICE_KIND_PCI);
+    if (libxl__device_pci_get_num(gc, be_path, &n))
+        goto out;
+
+    pcidevs = calloc(n, sizeof(libxl_device_pci));
+
+    for (i = 0; i < n; i++)
+        libxl__device_pci_from_xs_be(gc, be_path, i, pcidevs + i);
+
+    *num = n;
+out:
+    GC_FREE;
+    return pcidevs;
+}
+
+void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
+                                   libxl__multidev *multidev)
+{
+    STATE_AO_GC(multidev->ao);
+    libxl_device_pci *pcidevs;
+    int num, i;
+
+    pcidevs = libxl_device_pci_list(CTX, domid, &num);
+    if ( pcidevs == NULL )
+        return;
+    libxl__ptr_add(gc, pcidevs);
+
+    for (i = 0; i < num; i++) {
+        /* Force remove on shutdown since, on HVM, qemu will not always
+         * respond to SCI interrupt because the guest kernel has shut down the
+         * devices by the time we even get here!
+         */
+        libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
+        libxl__device_pci_remove_common(egc, domid, pcidevs + i, true,
+                                        aodev);
+    }
+}
+
+int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
+                                      libxl_domain_config *const d_config)
+{
+    int i, ret;
+
+    if (!libxl_defbool_val(d_config->b_info.u.hvm.gfx_passthru))
+        return 0;
+
+    for (i = 0 ; i < d_config->num_pcidevs ; i++) {
+        uint64_t vga_iomem_start = 0xa0000 >> XC_PAGE_SHIFT;
+        uint32_t stubdom_domid;
+        libxl_device_pci *pcidev = &d_config->pcidevs[i];
+        unsigned long pci_device_class;
+
+        if (sysfs_dev_get_class(gc, pcidev, &pci_device_class))
+            continue;
+        if (pci_device_class != 0x030000) /* VGA class */
+            continue;
+
+        stubdom_domid = libxl_get_stubdom_id(CTX, domid);
+        ret = xc_domain_iomem_permission(CTX->xch, stubdom_domid,
+                                         vga_iomem_start, 0x20, 1);
+        if (ret < 0) {
+            LOGED(ERROR, domid,
+                  "failed to give stubdom%d access to iomem range "
+                  "%"PRIx64"-%"PRIx64" for VGA passthru",
+                  stubdom_domid,
+                  vga_iomem_start, (vga_iomem_start + 0x20 - 1));
+            return ret;
+        }
+        ret = xc_domain_iomem_permission(CTX->xch, domid,
+                                         vga_iomem_start, 0x20, 1);
+        if (ret < 0) {
+            LOGED(ERROR, domid,
+                  "failed to give dom%d access to iomem range "
+                  "%"PRIx64"-%"PRIx64" for VGA passthru",
+                  domid, vga_iomem_start, (vga_iomem_start + 0x20 - 1));
+            return ret;
+        }
+        break;
+    }
+
+    return 0;
+}
+
+static int libxl_device_pci_compare(const libxl_device_pci *d1,
+                                    const libxl_device_pci *d2)
+{
+    return COMPARE_PCI(d1, d2);
+}
+
+#define libxl__device_pci_update_devid NULL
+
+DEFINE_DEVICE_TYPE_STRUCT_X(pcidev, pci, PCI,
+    .get_num = libxl__device_pci_get_num,
+    .from_xenstore = libxl__device_pci_from_xs_be,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_psr.c b/tools/libs/light/libxl_psr.c
new file mode 100644 (file)
index 0000000..9ced7d1
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2014      Intel Corporation
+ * Author Dongxiao Xu <dongxiao.xu@intel.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+#include "libxl_internal.h"
+
+#include <xen-tools/libs.h>
+
+#define IA32_QM_CTR_ERROR_MASK         (0x3ul << 62)
+
+static void libxl__psr_log_err_msg(libxl__gc *gc, int err)
+{
+    char *msg;
+
+    switch (err) {
+    case ENOSYS:
+    case EOPNOTSUPP:
+        msg = "unsupported operation";
+        break;
+    case ESRCH:
+        msg = "invalid domain ID";
+        break;
+    case ENOTSOCK:
+        msg = "socket is not supported";
+        break;
+    case EFAULT:
+        msg = "failed to exchange data with Xen";
+        break;
+    default:
+        msg = "unknown error";
+        break;
+    }
+
+    LOGE(ERROR, "%s", msg);
+}
+
+static void libxl__psr_cmt_log_err_msg(libxl__gc *gc, int err)
+{
+    char *msg;
+
+    switch (err) {
+    case ENODEV:
+        msg = "CMT is not supported in this system";
+        break;
+    case EEXIST:
+        msg = "CMT is already attached to this domain";
+        break;
+    case ENOENT:
+        msg = "CMT is not attached to this domain";
+        break;
+    case EOVERFLOW:
+        msg = "no free RMID available";
+        break;
+    default:
+        libxl__psr_log_err_msg(gc, err);
+        return;
+    }
+
+    LOGE(ERROR, "%s", msg);
+}
+
+static void libxl__psr_alloc_log_err_msg(libxl__gc *gc,
+                                         int err,
+                                         libxl_psr_type type)
+{
+    /*
+     * Index is 'libxl_psr_type' so we set two 'CDP' to correspond to
+     * DATA and CODE.
+     */
+    const char * const feat_name[] = {
+        [LIBXL_PSR_CBM_TYPE_UNKNOWN] = "UNKNOWN",
+        [LIBXL_PSR_CBM_TYPE_L3_CBM] = "L3 CAT",
+        [LIBXL_PSR_CBM_TYPE_L3_CBM_CODE...LIBXL_PSR_CBM_TYPE_L3_CBM_DATA] =
+                                      "CDP",
+        [LIBXL_PSR_CBM_TYPE_L2_CBM] = "L2 CAT",
+        [LIBXL_PSR_CBM_TYPE_MBA_THRTL] = "MBA",
+    };
+    char *msg;
+
+    switch (err) {
+    case ENODEV:
+        msg = "is not supported in this system";
+        break;
+    case ENOENT:
+        msg = "is not enabled on the socket";
+        break;
+    case EOVERFLOW:
+        msg = "no free COS available";
+        break;
+    case EEXIST:
+        msg = "The same CBM is already set to this domain";
+        break;
+    case ENXIO:
+        msg = "Unable to set code or data CBM when CDP is disabled";
+        break;
+    case EINVAL:
+        msg = "Invalid input or some internal values are not expected";
+        break;
+    case ERANGE:
+        msg = "Socket number is wrong";
+        break;
+    case ENOSPC:
+        msg = "Value array exceeds the range";
+        break;
+
+    default:
+        libxl__psr_log_err_msg(gc, err);
+        return;
+    }
+
+    LOG(ERROR, "%s: %s", feat_name[type], msg);
+}
+
+static int libxl__pick_socket_cpu(libxl__gc *gc, uint32_t socketid)
+{
+    int i, nr_cpus;
+    libxl_cputopology *topology;
+    int cpu = ERROR_FAIL;
+
+    topology = libxl_get_cpu_topology(CTX, &nr_cpus);
+    if (!topology)
+        return ERROR_FAIL;
+
+    for (i = 0; i < nr_cpus; i++)
+        if (topology[i].socket == socketid) {
+            cpu = i;
+            break;
+        }
+
+    libxl_cputopology_list_free(topology, nr_cpus);
+    return cpu;
+}
+
+int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = xc_psr_cmt_attach(ctx->xch, domid);
+    if (rc < 0) {
+        libxl__psr_cmt_log_err_msg(gc, errno);
+        rc = ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = xc_psr_cmt_detach(ctx->xch, domid);
+    if (rc < 0) {
+        libxl__psr_cmt_log_err_msg(gc, errno);
+        rc = ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid)
+{
+    int rc;
+    uint32_t rmid;
+
+    rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
+    if (rc < 0)
+        return 0;
+
+    return !!rmid;
+}
+
+int libxl_psr_cmt_enabled(libxl_ctx *ctx)
+{
+    return xc_psr_cmt_enabled(ctx->xch);
+}
+
+int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = xc_psr_cmt_get_total_rmid(ctx->xch, total_rmid);
+    if (rc < 0) {
+        libxl__psr_cmt_log_err_msg(gc, errno);
+        rc = ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
+                                    uint32_t socketid,
+                                    uint32_t *l3_cache_size)
+{
+    GC_INIT(ctx);
+
+    int rc;
+    int cpu = libxl__pick_socket_cpu(gc, socketid);
+
+    if (cpu < 0) {
+        LOGE(ERROR, "failed to get socket cpu");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = xc_psr_cmt_get_l3_cache_size(ctx->xch, cpu, l3_cache_size);
+    if (rc < 0) {
+        libxl__psr_cmt_log_err_msg(gc, errno);
+        rc = ERROR_FAIL;
+    }
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type)
+{
+    GC_INIT(ctx);
+    uint32_t event_mask;
+    int rc;
+
+    rc = xc_psr_cmt_get_l3_event_mask(ctx->xch, &event_mask);
+    if (rc < 0) {
+        libxl__psr_cmt_log_err_msg(gc, errno);
+        rc = 0;
+    } else {
+        rc = event_mask & (1 << (type - 1));
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
+                             uint32_t domid,
+                             libxl_psr_cmt_type type,
+                             uint64_t scope,
+                             uint64_t *sample_r,
+                             uint64_t *tsc_r)
+{
+    GC_INIT(ctx);
+    unsigned int rmid;
+    uint32_t upscaling_factor;
+    uint64_t monitor_data;
+    int cpu, rc;
+
+    rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
+    if (rc < 0 || rmid == 0) {
+        LOGED(ERROR, domid, "fail to get the domain rmid, "
+              "or domain is not attached with platform QoS monitoring service");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    cpu = libxl__pick_socket_cpu(gc, scope);
+    if (cpu < 0) {
+        LOGED(ERROR, domid, "failed to get socket cpu");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = xc_psr_cmt_get_data(ctx->xch, rmid, cpu, type - 1,
+                             &monitor_data, tsc_r);
+    if (rc < 0) {
+        LOGED(ERROR, domid, "failed to get monitoring data");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = xc_psr_cmt_get_l3_upscaling_factor(ctx->xch, &upscaling_factor);
+    if (rc < 0) {
+        LOGED(ERROR, domid, "failed to get L3 upscaling factor");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    *sample_r = monitor_data * upscaling_factor;
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
+                                      uint32_t domid,
+                                      uint32_t socketid,
+                                      uint32_t *l3_cache_occupancy)
+{
+    uint64_t data;
+    int rc;
+
+    rc = libxl_psr_cmt_get_sample(ctx, domid,
+                                  LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY,
+                                  socketid, &data, NULL);
+    if (rc < 0)
+        goto out;
+
+    *l3_cache_occupancy = data / 1024;
+out:
+    return rc;
+}
+
+static inline xc_psr_type libxl__psr_type_to_libxc_psr_type(
+    libxl_psr_type type)
+{
+    BUILD_BUG_ON(sizeof(libxl_psr_type) != sizeof(xc_psr_type));
+    return (xc_psr_type)type;
+}
+
+int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
+                          libxl_psr_cbm_type type, libxl_bitmap *target_map,
+                          uint64_t cbm)
+{
+    return libxl_psr_set_val(ctx, domid, type, target_map, cbm);
+}
+
+int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
+                          libxl_psr_cbm_type type, uint32_t target,
+                          uint64_t *cbm_r)
+{
+    return libxl_psr_get_val(ctx, domid, type, target, cbm_r);
+}
+
+static xc_psr_feat_type libxl__feat_type_to_libxc_feat_type(
+                            libxl_psr_feat_type type, unsigned int lvl)
+{
+    xc_psr_feat_type xc_type;
+
+    switch (type) {
+    case LIBXL_PSR_FEAT_TYPE_CAT:
+        assert(lvl == 3 || lvl == 2);
+
+        if (lvl == 3)
+            xc_type = XC_PSR_CAT_L3;
+        if (lvl == 2)
+            xc_type = XC_PSR_CAT_L2;
+        break;
+    case LIBXL_PSR_FEAT_TYPE_MBA:
+        xc_type = XC_PSR_MBA;
+        break;
+    default:
+        /* Could not happen */
+        assert(0);
+    }
+
+    return xc_type;
+}
+
+static void libxl__hw_info_to_libxl_cat_info(
+                libxl_psr_feat_type type, libxl_psr_hw_info *hw_info,
+                libxl_psr_cat_info *cat_info)
+{
+    assert(type == LIBXL_PSR_FEAT_TYPE_CAT);
+
+    cat_info->id = hw_info->id;
+    cat_info->cos_max = hw_info->u.cat.cos_max;
+    cat_info->cbm_len = hw_info->u.cat.cbm_len;
+    cat_info->cdp_enabled = hw_info->u.cat.cdp_enabled;
+}
+
+int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+                           unsigned int *nr, unsigned int lvl)
+{
+    GC_INIT(ctx);
+    int rc;
+    unsigned int i;
+    libxl_psr_hw_info *hw_info;
+    libxl_psr_cat_info *ptr;
+
+    rc = libxl_psr_get_hw_info(ctx, LIBXL_PSR_FEAT_TYPE_CAT, lvl, nr, &hw_info);
+    if (rc)
+        goto out;
+
+    ptr = libxl__malloc(NOGC, *nr * sizeof(libxl_psr_cat_info));
+
+    for (i = 0; i < *nr; i++)
+        libxl__hw_info_to_libxl_cat_info(LIBXL_PSR_FEAT_TYPE_CAT,
+                                         &hw_info[i],
+                                         &ptr[i]);
+
+    *info = ptr;
+    libxl_psr_hw_info_list_free(hw_info, *nr);
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
+                              int *nr)
+{
+    int rc;
+    unsigned int num;
+
+    rc = libxl_psr_cat_get_info(ctx, info, &num, 3);
+    if (!rc)
+        *nr = num;
+
+    return rc;
+}
+
+void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr)
+{
+    int i;
+
+    for (i = 0; i < nr; i++)
+        libxl_psr_cat_info_dispose(&list[i]);
+    free(list);
+}
+
+int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
+                      libxl_psr_type type, libxl_bitmap *target_map,
+                      uint64_t val)
+{
+    GC_INIT(ctx);
+    int rc, socketid, nr_sockets;
+    xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
+
+    rc = libxl__count_physical_sockets(gc, &nr_sockets);
+    if (rc) {
+        LOG(ERROR, "failed to get system socket count");
+        goto out;
+    }
+
+    libxl_for_each_set_bit(socketid, *target_map) {
+        if (socketid >= nr_sockets)
+            break;
+
+        if (xc_psr_set_domain_data(ctx->xch, domid, xc_type,
+                                   socketid, val)) {
+            libxl__psr_alloc_log_err_msg(gc, errno, type);
+            rc = ERROR_FAIL;
+        }
+    }
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
+                      libxl_psr_type type, unsigned int target,
+                      uint64_t *val)
+{
+    GC_INIT(ctx);
+    int rc = 0;
+    xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
+
+    if (xc_psr_get_domain_data(ctx->xch, domid, xc_type,
+                               target, val)) {
+        libxl__psr_alloc_log_err_msg(gc, errno, type);
+        rc = ERROR_FAIL;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+static void libxl__xc_hw_info_to_libxl_hw_info(
+                libxl_psr_feat_type type, xc_psr_hw_info *xc_info,
+                libxl_psr_hw_info *xl_info)
+{
+    switch (type) {
+    case LIBXL_PSR_FEAT_TYPE_CAT:
+        xl_info->u.cat.cos_max = xc_info->cat.cos_max;
+        xl_info->u.cat.cbm_len = xc_info->cat.cbm_len;
+        xl_info->u.cat.cdp_enabled = xc_info->cat.cdp_enabled;
+        break;
+    case LIBXL_PSR_FEAT_TYPE_MBA:
+        xl_info->u.mba.cos_max = xc_info->mba.cos_max;
+        xl_info->u.mba.thrtl_max = xc_info->mba.thrtl_max;
+        xl_info->u.mba.linear = xc_info->mba.linear;
+        break;
+    default:
+        assert(0);
+    }
+}
+
+int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
+                          unsigned int lvl, unsigned int *nr,
+                          libxl_psr_hw_info **info)
+{
+    GC_INIT(ctx);
+    int rc, nr_sockets;
+    unsigned int i = 0, socketid;
+    libxl_bitmap socketmap;
+    libxl_psr_hw_info *ptr;
+    xc_psr_feat_type xc_type;
+    xc_psr_hw_info hw_info;
+
+    libxl_bitmap_init(&socketmap);
+
+    xc_type = libxl__feat_type_to_libxc_feat_type(type, lvl);
+
+    rc = libxl__count_physical_sockets(gc, &nr_sockets);
+    if (rc) {
+        LOG(ERROR, "failed to get system socket count");
+        goto out;
+    }
+
+    libxl_socket_bitmap_alloc(ctx, &socketmap, nr_sockets);
+    rc = libxl_get_online_socketmap(ctx, &socketmap);
+    if (rc) {
+        LOGE(ERROR, "failed to get available sockets");
+        goto out;
+    }
+
+    ptr = libxl__malloc(NOGC, nr_sockets * sizeof(libxl_psr_hw_info));
+
+    libxl_for_each_set_bit(socketid, socketmap) {
+        ptr[i].id = socketid;
+        if (xc_psr_get_hw_info(ctx->xch, socketid, xc_type, &hw_info)) {
+            rc = ERROR_FAIL;
+            free(ptr);
+            goto out;
+        }
+
+        libxl__xc_hw_info_to_libxl_hw_info(type, &hw_info, &ptr[i]);
+
+        i++;
+    }
+
+    *info = ptr;
+    *nr = i;
+out:
+    libxl_bitmap_dispose(&socketmap);
+    GC_FREE;
+    return rc;
+}
+
+void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr)
+{
+    unsigned int i;
+
+    for (i = 0; i < nr; i++)
+        libxl_psr_hw_info_dispose(&list[i]);
+    free(list);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_pvcalls.c b/tools/libs/light/libxl_pvcalls.c
new file mode 100644 (file)
index 0000000..870318e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018      Aporeto
+ * Author Stefano Stabellini <stefano@aporeto.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__device_pvcallsif_setdefault(libxl__gc *gc, uint32_t domid,
+                                              libxl_device_pvcallsif *pvcallsif,
+                                              bool hotplug)
+{
+    return libxl__resolve_domid(gc, pvcallsif->backend_domname,
+                                &pvcallsif->backend_domid);
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(pvcallsif)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(pvcallsif)
+
+#define libxl__add_pvcallsifs NULL
+#define libxl_device_pvcallsif_list NULL
+#define libxl_device_pvcallsif_compare NULL
+
+LIBXL_DEFINE_DEVICE_REMOVE(pvcallsif)
+
+DEFINE_DEVICE_TYPE_STRUCT(pvcallsif, PVCALLS);
diff --git a/tools/libs/light/libxl_qmp.c b/tools/libs/light/libxl_qmp.c
new file mode 100644 (file)
index 0000000..c394000
--- /dev/null
@@ -0,0 +1,1934 @@
+/*
+ * Copyright (C) 2011      Citrix Ltd.
+ * Author Anthony PERARD <anthony.perard@citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+/*
+ * This file implement a client for QMP (QEMU Monitor Protocol). For the
+ * Specification, see in the QEMU repository.
+ *
+ * WARNING - Do not trust QEMU when writing codes for new commands or when
+ *           improving the client code.
+ */
+
+/*
+ * Logic used to send command to QEMU
+ *
+ * qmp_open():
+ *  Will open a socket and connect to QEMU.
+ *
+ * qmp_next():
+ *  Will read data sent by QEMU and then call qmp_handle_response() once a
+ *  complete QMP message is received.
+ *  The function return on timeout/error or once every data received as been
+ *  processed.
+ *
+ * qmp_handle_response()
+ *  This process json messages received from QEMU and update different list and
+ *  may call callback function.
+ *  `libxl__qmp_handler.wait_for_id` is reset once a message with this ID is
+ *    processed.
+ *  `libxl__qmp_handler.callback_list`: list with ID of command sent and
+ *    optional assotiated callback function. The return value of a callback is
+ *    set in context.
+ *
+ * qmp_send():
+ *  Simply prepare a QMP command and send it to QEMU.
+ *  It also add a `struct callback_id_pair` on the
+ *  `libxl__qmp_handler.callback_list` via qmp_send_prepare().
+ *
+ * qmp_synchronous_send():
+ *  This function calls qmp_send(), then wait for QEMU to reply to the command.
+ *  The wait is done by calling qmp_next() over and over again until either
+ *  there is a response for the command or there is an error.
+ *
+ *  An ID can be set for each QMP command, this is set into
+ *  `libxl__qmp_handler.wait_for_id`. qmp_next will check every response's ID
+ *  again this field and change the value of the field once the ID is found.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <sys/un.h>
+
+#include <yajl/yajl_gen.h>
+
+#include "_libxl_list.h"
+#include "libxl_internal.h"
+
+/* #define DEBUG_RECEIVED */
+
+#ifdef DEBUG_RECEIVED
+#  define DEBUG_REPORT_RECEIVED(dom, buf, len) \
+    LOGD(DEBUG, dom, "received: '%.*s'", len, buf)
+#else
+#  define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0)
+#endif
+
+#ifdef DEBUG_QMP_CLIENT
+#  define LOG_QMP(f, ...) LOGD(DEBUG, ev->domid, f, ##__VA_ARGS__)
+#else
+#  define LOG_QMP(f, ...)
+#endif
+
+/*
+ * QMP types & constant
+ */
+
+#define QMP_RECEIVE_BUFFER_SIZE 4096
+#define QMP_MAX_SIZE_RX_BUF MB(1)
+
+/*
+ * qmp_callback_t is call whenever a message from QMP contain the "id"
+ * associated with the callback.
+ * "tree" contain the JSON tree that is in "return" of a QMP message. If QMP
+ * sent an error message, "tree" will be NULL.
+ */
+typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
+                              const libxl__json_object *tree,
+                              void *opaque);
+
+typedef struct qmp_request_context {
+    int rc;
+} qmp_request_context;
+
+typedef struct callback_id_pair {
+    int id;
+    qmp_callback_t callback;
+    void *opaque;
+    qmp_request_context *context;
+    LIBXL_STAILQ_ENTRY(struct callback_id_pair) next;
+} callback_id_pair;
+
+struct libxl__qmp_handler {
+    int qmp_fd;
+    bool connected;
+    time_t timeout;
+    /* wait_for_id will be used by the synchronous send function */
+    int wait_for_id;
+
+    char buffer[QMP_RECEIVE_BUFFER_SIZE + 1];
+
+    libxl_ctx *ctx;
+    uint32_t domid;
+
+    int last_id_used;
+    LIBXL_STAILQ_HEAD(callback_list, callback_id_pair) callback_list;
+    struct {
+        int major;
+        int minor;
+        int micro;
+    } version;
+};
+
+static int qmp_send(libxl__qmp_handler *qmp,
+                    const char *cmd, libxl__json_object *args,
+                    qmp_callback_t callback, void *opaque,
+                    qmp_request_context *context);
+
+static const int QMP_SOCKET_CONNECT_TIMEOUT = 5;
+
+/*
+ * QMP callbacks functions
+ */
+
+static int qmp_capabilities_callback(libxl__qmp_handler *qmp,
+                                     const libxl__json_object *o, void *unused)
+{
+    qmp->connected = true;
+
+    return 0;
+}
+
+/*
+ * QMP commands
+ */
+
+static int enable_qmp_capabilities(libxl__qmp_handler *qmp)
+{
+    return qmp_send(qmp, "qmp_capabilities", NULL,
+                    qmp_capabilities_callback, NULL, NULL);
+}
+
+/*
+ * Helpers
+ */
+
+static libxl__qmp_message_type qmp_response_type(const libxl__json_object *o)
+{
+    libxl__qmp_message_type type;
+    libxl__json_map_node *node = NULL;
+    int i = 0;
+
+    for (i = 0; (node = libxl__json_map_node_get(o, i)); i++) {
+        if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0)
+            return type;
+    }
+
+    return LIBXL__QMP_MESSAGE_TYPE_INVALID;
+}
+
+static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp,
+                                                  const libxl__json_object *o)
+{
+    const libxl__json_object *id_object = libxl__json_map_get("id", o,
+                                                              JSON_INTEGER);
+    int id = -1;
+    callback_id_pair *pp = NULL;
+
+    if (id_object) {
+        id = libxl__json_object_get_integer(id_object);
+
+        LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
+            if (pp->id == id) {
+                return pp;
+            }
+        }
+    }
+    return NULL;
+}
+
+static void qmp_handle_error_response(libxl__gc *gc, libxl__qmp_handler *qmp,
+                                      const libxl__json_object *resp)
+{
+    callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
+
+    resp = libxl__json_map_get("error", resp, JSON_MAP);
+    resp = libxl__json_map_get("desc", resp, JSON_STRING);
+
+    if (pp) {
+        if (pp->callback) {
+            int rc = pp->callback(qmp, NULL, pp->opaque);
+            if (pp->context) {
+                pp->context->rc = rc;
+            }
+        }
+        if (pp->id == qmp->wait_for_id) {
+            /* tell that the id have been processed */
+            qmp->wait_for_id = 0;
+        }
+        LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next);
+        free(pp);
+    }
+
+    LOGD(ERROR, qmp->domid, "received an error message from QMP server: %s",
+         libxl__json_object_get_string(resp));
+}
+
+static int qmp_handle_response(libxl__gc *gc, libxl__qmp_handler *qmp,
+                               const libxl__json_object *resp)
+{
+    libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID;
+
+    type = qmp_response_type(resp);
+    LOGD(DEBUG, qmp->domid, "message type: %s", libxl__qmp_message_type_to_string(type));
+
+    switch (type) {
+    case LIBXL__QMP_MESSAGE_TYPE_QMP: {
+        const libxl__json_object *o;
+        o = libxl__json_map_get("QMP", resp, JSON_MAP);
+        o = libxl__json_map_get("version", o, JSON_MAP);
+        o = libxl__json_map_get("qemu", o, JSON_MAP);
+        qmp->version.major = libxl__json_object_get_integer(
+            libxl__json_map_get("major", o, JSON_INTEGER));
+        qmp->version.minor = libxl__json_object_get_integer(
+            libxl__json_map_get("minor", o, JSON_INTEGER));
+        qmp->version.micro = libxl__json_object_get_integer(
+            libxl__json_map_get("micro", o, JSON_INTEGER));
+        LOGD(DEBUG, qmp->domid, "QEMU version: %d.%d.%d",
+             qmp->version.major, qmp->version.minor, qmp->version.micro);
+        /* On the greeting message from the server, enable QMP capabilities */
+        return enable_qmp_capabilities(qmp);
+    }
+    case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
+        callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
+
+        if (pp) {
+            if (pp->callback) {
+                int rc = pp->callback(qmp,
+                             libxl__json_map_get("return", resp, JSON_ANY),
+                             pp->opaque);
+                if (pp->context) {
+                    pp->context->rc = rc;
+                }
+            }
+            if (pp->id == qmp->wait_for_id) {
+                /* tell that the id have been processed */
+                qmp->wait_for_id = 0;
+            }
+            LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair,
+                                next);
+            free(pp);
+        }
+        return 0;
+    }
+    case LIBXL__QMP_MESSAGE_TYPE_ERROR:
+        qmp_handle_error_response(gc, qmp, resp);
+        return -1;
+    case LIBXL__QMP_MESSAGE_TYPE_EVENT:
+        return 0;
+    case LIBXL__QMP_MESSAGE_TYPE_INVALID:
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * return values:
+ *   < 0  if qemu's version <  asked version
+ *   = 0  if qemu's version == asked version
+ *   > 0  if qemu's version >  asked version
+ */
+static int qmp_ev_qemu_compare_version(libxl__ev_qmp *ev, int major,
+                                       int minor, int micro)
+{
+#define CHECK_VERSION(level) do { \
+    if (ev->qemu_version.level > (level)) return +1; \
+    if (ev->qemu_version.level < (level)) return -1; \
+} while (0)
+
+    CHECK_VERSION(major);
+    CHECK_VERSION(minor);
+    CHECK_VERSION(micro);
+
+#undef CHECK_VERSION
+
+    return 0;
+}
+
+/*
+ * Handler functions
+ */
+
+static libxl__qmp_handler *qmp_init_handler(libxl__gc *gc, uint32_t domid)
+{
+    libxl__qmp_handler *qmp = NULL;
+
+    qmp = calloc(1, sizeof (libxl__qmp_handler));
+    if (qmp == NULL) {
+        LOGED(ERROR, domid, "Failed to allocate qmp_handler");
+        return NULL;
+    }
+    qmp->ctx = CTX;
+    qmp->domid = domid;
+    qmp->timeout = 5;
+
+    LIBXL_STAILQ_INIT(&qmp->callback_list);
+
+    return qmp;
+}
+
+static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path,
+                    int timeout)
+{
+    GC_INIT(qmp->ctx);
+    int ret = -1;
+    int i = 0;
+    struct sockaddr_un addr;
+
+    qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (qmp->qmp_fd < 0) {
+        goto out;
+    }
+    ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1);
+    if (ret) {
+        ret = -1;
+        goto out;
+    }
+    ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1);
+    if (ret) {
+        ret = -1;
+        goto out;
+    }
+
+    ret = libxl__prepare_sockaddr_un(gc, &addr, qmp_socket_path, "QMP socket");
+    if (ret)
+        goto out;
+
+    do {
+        ret = connect(qmp->qmp_fd, (struct sockaddr *) &addr, sizeof(addr));
+        if (ret == 0)
+            break;
+        if (errno == ENOENT || errno == ECONNREFUSED) {
+            /* ENOENT       : Socket may not have shown up yet
+             * ECONNREFUSED : Leftover socket hasn't been removed yet */
+            continue;
+        }
+        ret = -1;
+        goto out;
+    } while ((++i / 5 <= timeout) && (usleep(200 * 1000) <= 0));
+
+out:
+    if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd);
+
+    GC_FREE;
+    return ret;
+}
+
+static void qmp_close(libxl__qmp_handler *qmp)
+{
+    callback_id_pair *pp = NULL;
+    callback_id_pair *tmp = NULL;
+
+    close(qmp->qmp_fd);
+    LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
+        free(tmp);
+        tmp = pp;
+    }
+    free(tmp);
+}
+
+static int qmp_next(libxl__gc *gc, libxl__qmp_handler *qmp)
+{
+    ssize_t rd;
+    char *s = NULL;
+    char *s_end = NULL;
+
+    char *incomplete = NULL;
+    size_t incomplete_size = 0;
+    int rc = 0;
+
+    do {
+        fd_set rfds;
+        int ret = 0;
+        struct timeval timeout = {
+            .tv_sec = qmp->timeout,
+            .tv_usec = 0,
+        };
+
+        FD_ZERO(&rfds);
+        FD_SET(qmp->qmp_fd, &rfds);
+
+        ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout);
+        if (ret == 0) {
+            LOGD(ERROR, qmp->domid, "timeout");
+            return -1;
+        } else if (ret < 0) {
+            if (errno == EINTR)
+                continue;
+            LOGED(ERROR, qmp->domid, "Select error");
+            return -1;
+        }
+
+        rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE);
+        if (rd == 0) {
+            LOGD(ERROR, qmp->domid, "Unexpected end of socket");
+            return -1;
+        } else if (rd < 0) {
+            LOGED(ERROR, qmp->domid, "Socket read error");
+            return rd;
+        }
+        qmp->buffer[rd] = '\0';
+
+        DEBUG_REPORT_RECEIVED(qmp->domid, qmp->buffer, (int)rd);
+
+        if (incomplete) {
+            size_t current_pos = s - incomplete;
+            incomplete = libxl__realloc(gc, incomplete,
+                                        incomplete_size + rd + 1);
+            strncat(incomplete + incomplete_size, qmp->buffer, rd);
+            s = incomplete + current_pos;
+            incomplete_size += rd;
+            s_end = incomplete + incomplete_size;
+        } else {
+            incomplete = libxl__strndup(gc, qmp->buffer, rd);
+            incomplete_size = rd;
+            s = incomplete;
+            s_end = s + rd;
+            rd = 0;
+        }
+
+        do {
+            char *end = NULL;
+
+            end = strstr(s, "\r\n");
+            if (end) {
+                libxl__json_object *o = NULL;
+
+                *end = '\0';
+
+                o = libxl__json_parse(gc, s);
+
+                if (o) {
+                    rc = qmp_handle_response(gc, qmp, o);
+                } else {
+                    LOGD(ERROR, qmp->domid, "Parse error of : %s", s);
+                    return -1;
+                }
+
+                s = end + 2;
+            } else {
+                break;
+            }
+        } while (s < s_end);
+    } while (s < s_end);
+
+    return rc;
+}
+
+static char *qmp_prepare_cmd(libxl__gc *gc, const char *cmd,
+                             const libxl__json_object *args,
+                             int id)
+{
+    yajl_gen hand = NULL;
+    /* memory for 'buf' is owned by 'hand' */
+    const unsigned char *buf;
+    libxl_yajl_length len;
+    yajl_gen_status s;
+    char *ret = NULL;
+
+    hand = libxl_yajl_gen_alloc(NULL);
+
+    if (!hand) {
+        return NULL;
+    }
+
+#if HAVE_YAJL_V2
+    /* Disable beautify for data sent to QEMU */
+    yajl_gen_config(hand, yajl_gen_beautify, 0);
+#endif
+
+    yajl_gen_map_open(hand);
+    libxl__yajl_gen_asciiz(hand, "execute");
+    libxl__yajl_gen_asciiz(hand, cmd);
+    libxl__yajl_gen_asciiz(hand, "id");
+    yajl_gen_integer(hand, id);
+    if (args) {
+        libxl__yajl_gen_asciiz(hand, "arguments");
+        libxl__json_object_to_yajl_gen(gc, hand, args);
+    }
+    yajl_gen_map_close(hand);
+
+    s = yajl_gen_get_buf(hand, &buf, &len);
+
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    ret = libxl__sprintf(gc, "%*.*s\r\n", (int)len, (int)len, buf);
+
+out:
+    yajl_gen_free(hand);
+    return ret;
+}
+
+static char *qmp_send_prepare(libxl__gc *gc, libxl__qmp_handler *qmp,
+                              const char *cmd, libxl__json_object *args,
+                              qmp_callback_t callback, void *opaque,
+                              qmp_request_context *context)
+{
+    char *buf;
+    callback_id_pair *elm;
+
+    buf = qmp_prepare_cmd(gc, cmd, args, ++qmp->last_id_used);
+
+    if (!buf) {
+        LOGD(ERROR, qmp->domid, "Failed to generate a qmp command");
+        goto out;
+    }
+
+    elm = malloc(sizeof (callback_id_pair));
+    if (elm == NULL) {
+        LOGED(ERROR, qmp->domid, "Failed to allocate a QMP callback");
+        goto out;
+    }
+    elm->id = qmp->last_id_used;
+    elm->callback = callback;
+    elm->opaque = opaque;
+    elm->context = context;
+    LIBXL_STAILQ_INSERT_TAIL(&qmp->callback_list, elm, next);
+
+    LOGD(DEBUG, qmp->domid, "next qmp command: '%s'", buf);
+
+out:
+    return buf;
+}
+
+static int qmp_send(libxl__qmp_handler *qmp,
+                    const char *cmd, libxl__json_object *args,
+                    qmp_callback_t callback, void *opaque,
+                    qmp_request_context *context)
+{
+    char *buf = NULL;
+    int rc = -1;
+    GC_INIT(qmp->ctx);
+
+    buf = qmp_send_prepare(gc, qmp, cmd, args, callback, opaque, context);
+
+    if (buf == NULL) {
+        goto out;
+    }
+
+    if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, strlen(buf),
+                            "QMP command", "QMP socket"))
+        goto out;
+
+    rc = qmp->last_id_used;
+out:
+    GC_FREE;
+    return rc;
+}
+
+static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd,
+                                libxl__json_object *args,
+                                qmp_callback_t callback, void *opaque,
+                                int ask_timeout)
+{
+    int id = 0;
+    int ret = 0;
+    GC_INIT(qmp->ctx);
+    qmp_request_context context = { .rc = 0 };
+
+    id = qmp_send(qmp, cmd, args, callback, opaque, &context);
+    if (id <= 0) {
+        return ERROR_FAIL;
+    }
+    qmp->wait_for_id = id;
+
+    while (qmp->wait_for_id == id) {
+        if ((ret = qmp_next(gc, qmp)) < 0) {
+            break;
+        }
+    }
+
+    if (qmp->wait_for_id != id && ret == 0) {
+        ret = context.rc;
+    }
+
+    GC_FREE;
+
+    return ret;
+}
+
+static void qmp_free_handler(libxl__qmp_handler *qmp)
+{
+    free(qmp);
+}
+
+/*
+ * QMP Parameters Helpers
+ */
+static void qmp_parameters_common_add(libxl__gc *gc,
+                                      libxl__json_object **param,
+                                      const char *name,
+                                      libxl__json_object *obj)
+{
+    libxl__json_map_node *arg = NULL;
+
+    if (!*param) {
+        *param = libxl__json_object_alloc(gc, JSON_MAP);
+    }
+
+    GCNEW(arg);
+
+    arg->map_key = libxl__strdup(gc, name);
+    arg->obj = obj;
+
+    flexarray_append((*param)->u.map, arg);
+}
+
+void libxl__qmp_param_add_string(libxl__gc *gc,
+                                 libxl__json_object **param,
+                                 const char *name, const char *argument)
+{
+    libxl__json_object *obj;
+
+    obj = libxl__json_object_alloc(gc, JSON_STRING);
+    obj->u.string = libxl__strdup(gc, argument);
+
+    qmp_parameters_common_add(gc, param, name, obj);
+}
+
+void libxl__qmp_param_add_bool(libxl__gc *gc,
+                               libxl__json_object **param,
+                               const char *name, bool b)
+{
+    libxl__json_object *obj;
+
+    obj = libxl__json_object_alloc(gc, JSON_BOOL);
+    obj->u.b = b;
+    qmp_parameters_common_add(gc, param, name, obj);
+}
+
+void libxl__qmp_param_add_integer(libxl__gc *gc,
+                                  libxl__json_object **param,
+                                  const char *name, const int i)
+{
+    libxl__json_object *obj;
+
+    obj = libxl__json_object_alloc(gc, JSON_INTEGER);
+    obj->u.i = i;
+
+    qmp_parameters_common_add(gc, param, name, obj);
+}
+
+/*
+ * API
+ */
+
+libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid)
+{
+    int ret = 0;
+    libxl__qmp_handler *qmp = NULL;
+    char *qmp_socket;
+
+    qmp = qmp_init_handler(gc, domid);
+    if (!qmp) return NULL;
+
+    qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
+    if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
+        LOGED(ERROR, domid, "Connection error");
+        qmp_free_handler(qmp);
+        return NULL;
+    }
+
+    LOGD(DEBUG, domid, "connected to %s", qmp_socket);
+
+    /* Wait for the response to qmp_capabilities */
+    while (!qmp->connected) {
+        if ((ret = qmp_next(gc, qmp)) < 0) {
+            break;
+        }
+    }
+
+    if (!qmp->connected) {
+        LOGD(ERROR, domid, "Failed to connect to QMP");
+        libxl__qmp_close(qmp);
+        return NULL;
+    }
+    return qmp;
+}
+
+void libxl__qmp_close(libxl__qmp_handler *qmp)
+{
+    if (!qmp)
+        return;
+    qmp_close(qmp);
+    qmp_free_handler(qmp);
+}
+
+void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid)
+{
+    char *qmp_socket;
+
+    qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
+    if (unlink(qmp_socket) == -1) {
+        if (errno != ENOENT) {
+            LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
+        }
+    }
+
+    qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid);
+    if (unlink(qmp_socket) == -1) {
+        if (errno != ENOENT) {
+            LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
+        }
+    }
+}
+
+static int qmp_run_command(libxl__gc *gc, int domid,
+                           const char *cmd, libxl__json_object *args,
+                           qmp_callback_t callback, void *opaque)
+{
+    libxl__qmp_handler *qmp = NULL;
+    int rc = 0;
+
+    qmp = libxl__qmp_initialize(gc, domid);
+    if (!qmp)
+        return ERROR_FAIL;
+
+    rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout);
+
+    libxl__qmp_close(qmp);
+    return rc;
+}
+
+int libxl__qmp_restore(libxl__gc *gc, int domid, const char *state_file)
+{
+    libxl__json_object *args = NULL;
+
+    libxl__qmp_param_add_string(gc, &args, "filename", state_file);
+
+    return qmp_run_command(gc, domid, "xen-load-devices-state", args,
+                           NULL, NULL);
+}
+
+int libxl__qmp_resume(libxl__gc *gc, int domid)
+{
+    return qmp_run_command(gc, domid, "cont", NULL, NULL, NULL);
+}
+
+int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
+                                const char *host, const char *port)
+{
+    libxl__json_object *args = NULL;
+    libxl__json_object *addr = NULL;
+    libxl__json_object *data = NULL;
+
+    /* 'addr': {
+     *   'type': 'inet',
+     *   'data': {
+     *     'host': '$nbd_host',
+     *     'port': '$nbd_port'
+     *   }
+     * }
+     */
+    libxl__qmp_param_add_string(gc, &data, "host", host);
+    libxl__qmp_param_add_string(gc, &data, "port", port);
+
+    libxl__qmp_param_add_string(gc, &addr, "type", "inet");
+    qmp_parameters_common_add(gc, &addr, "data", data);
+
+    qmp_parameters_common_add(gc, &args, "addr", addr);
+
+    return qmp_run_command(gc, domid, "nbd-server-start", args, NULL, NULL);
+}
+
+int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk)
+{
+    libxl__json_object *args = NULL;
+
+    libxl__qmp_param_add_string(gc, &args, "device", disk);
+    libxl__qmp_param_add_bool(gc, &args, "writable", true);
+
+    return qmp_run_command(gc, domid, "nbd-server-add", args, NULL, NULL);
+}
+
+int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary)
+{
+    libxl__json_object *args = NULL;
+
+    libxl__qmp_param_add_bool(gc, &args, "enable", true);
+    libxl__qmp_param_add_bool(gc, &args, "primary", primary);
+
+    return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
+}
+
+int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid)
+{
+    return qmp_run_command(gc, domid, "query-xen-replication-status", NULL,
+                           NULL, NULL);
+}
+
+int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid)
+{
+    return qmp_run_command(gc, domid, "xen-colo-do-checkpoint",
+                           NULL, NULL, NULL);
+}
+
+int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary)
+{
+    libxl__json_object *args = NULL;
+
+    libxl__qmp_param_add_bool(gc, &args, "enable", false);
+    libxl__qmp_param_add_bool(gc, &args, "primary", primary);
+
+    return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
+}
+
+int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid)
+{
+    return qmp_run_command(gc, domid, "nbd-server-stop", NULL, NULL, NULL);
+}
+
+int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parent,
+                                 const char *child, const char *node)
+{
+    libxl__json_object *args = NULL;
+
+    libxl__qmp_param_add_string(gc, &args, "parent", parent);
+    if (child)
+        libxl__qmp_param_add_string(gc, &args, "child", child);
+    if (node)
+        libxl__qmp_param_add_string(gc, &args, "node", node);
+
+    return qmp_run_command(gc, domid, "x-blockdev-change", args, NULL, NULL);
+}
+
+static int hmp_callback(libxl__qmp_handler *qmp,
+                        const libxl__json_object *response,
+                        void *opaque)
+{
+    char **output = opaque;
+    GC_INIT(qmp->ctx);
+    int rc;
+
+    rc = 0;
+    if (!output)
+        goto out;
+
+    *output = NULL;
+
+    if (libxl__json_object_is_string(response)) {
+        *output = libxl__strdup(NOGC, libxl__json_object_get_string(response));
+        goto out;
+    }
+
+    LOG(ERROR, "Response has unexpected format");
+    rc = ERROR_FAIL;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
+                   char **output)
+{
+    libxl__json_object *args = NULL;
+
+    libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
+
+    return qmp_run_command(gc, domid, "human-monitor-command", args,
+                           hmp_callback, output);
+}
+
+
+typedef struct {
+    libxl__ev_qmp qmp;
+    char **output; /* user pointer */
+} qemu_monitor_command_state;
+
+static void qemu_monitor_command_done(libxl__egc *, libxl__ev_qmp *,
+                                      const libxl__json_object *response,
+                                      int rc);
+
+int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
+                               const char *command_line, char **output,
+                               const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    qemu_monitor_command_state *qmcs;
+    libxl__json_object *args = NULL;
+    int rc;
+
+    if (!output) {
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    GCNEW(qmcs);
+    libxl__ev_qmp_init(&qmcs->qmp);
+    qmcs->qmp.ao = ao;
+    qmcs->qmp.domid = domid;
+    qmcs->qmp.payload_fd = -1;
+    qmcs->qmp.callback = qemu_monitor_command_done;
+    qmcs->output = output;
+    libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
+    rc = libxl__ev_qmp_send(egc, &qmcs->qmp, "human-monitor-command", args);
+out:
+    if (rc) return AO_CREATE_FAIL(rc);
+    return AO_INPROGRESS;
+}
+
+static void qemu_monitor_command_done(libxl__egc *egc, libxl__ev_qmp *qmp,
+                                      const libxl__json_object *response,
+                                      int rc)
+{
+    STATE_AO_GC(qmp->ao);
+    qemu_monitor_command_state *qmcs = CONTAINER_OF(qmp, *qmcs, qmp);
+
+    if (rc) goto out;
+
+    if (!libxl__json_object_is_string(response)) {
+        rc = ERROR_QEMU_API;
+        LOGD(ERROR, qmp->domid, "Response has unexpected format");
+        goto out;
+    }
+
+    *(qmcs->output) =
+        libxl__strdup(NOGC, libxl__json_object_get_string(response));
+    rc = 0;
+
+out:
+    libxl__ev_qmp_dispose(gc, qmp);
+    libxl__ao_complete(egc, ao, rc);
+}
+
+/*
+ * Functions using libxl__ev_qmp
+ */
+
+static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
+                       const libxl__json_object *response, int rc);
+static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
+                              const libxl__json_object *response, int rc);
+static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset);
+static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
+                           const libxl__json_object *response, int rc);
+
+/* calls dsps->callback_device_model_done when done */
+void libxl__qmp_suspend_save(libxl__egc *egc,
+                             libxl__domain_suspend_state *dsps)
+{
+    EGC_GC;
+    int rc;
+    libxl__ev_qmp *ev = &dsps->qmp;
+
+    ev->ao = dsps->ao;
+    ev->domid = dsps->domid;
+    ev->callback = dm_stopped;
+    ev->payload_fd = -1;
+
+    rc = libxl__ev_qmp_send(egc, ev, "stop", NULL);
+    if (rc)
+        goto error;
+
+    return;
+
+error:
+    dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
+                       const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+    const char *const filename = dsps->dm_savefile;
+    uint32_t dm_domid = libxl_get_stubdom_id(CTX, dsps->domid);
+
+    if (rc)
+        goto error;
+
+    if (dm_domid) {
+        /* see Linux stubdom interface in docs/stubdom.txt */
+        dm_state_save_to_fdset(egc, ev, 1);
+        return;
+    }
+
+    ev->payload_fd = open(filename, O_WRONLY | O_CREAT, 0600);
+    if (ev->payload_fd < 0) {
+        LOGED(ERROR, ev->domid,
+              "Failed to open file %s for QEMU", filename);
+        rc = ERROR_FAIL;
+        goto error;
+    }
+
+    ev->callback = dm_state_fd_ready;
+    rc = libxl__ev_qmp_send(egc, ev, "add-fd", NULL);
+    if (rc)
+        goto error;
+
+    return;
+
+error:
+    if (ev->payload_fd >= 0) {
+        close(ev->payload_fd);
+        libxl__remove_file(gc, filename);
+        ev->payload_fd = -1;
+    }
+    dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
+                              const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    int fdset;
+    const libxl__json_object *o;
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+
+    close(ev->payload_fd);
+    ev->payload_fd = -1;
+
+    if (rc)
+        goto error;
+
+    o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
+    if (!o) {
+        rc = ERROR_QEMU_API;
+        goto error;
+    }
+    fdset = libxl__json_object_get_integer(o);
+    dm_state_save_to_fdset(egc, ev, fdset);
+    return;
+
+error:
+    assert(rc);
+    libxl__remove_file(gc, dsps->dm_savefile);
+    dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset)
+{
+    EGC_GC;
+    int rc;
+    libxl__json_object *args = NULL;
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+
+    ev->callback = dm_state_saved;
+
+    /* The `live` parameter was added to QEMU 2.11. It signals QEMU that
+     * the save operation is for a live migration rather than for taking a
+     * snapshot. */
+    if (qmp_ev_qemu_compare_version(ev, 2, 11, 0) >= 0)
+        libxl__qmp_param_add_bool(gc, &args, "live", dsps->live);
+    QMP_PARAMETERS_SPRINTF(&args, "filename", "/dev/fdset/%d", fdset);
+    rc = libxl__ev_qmp_send(egc, ev, "xen-save-devices-state", args);
+    if (rc)
+        goto error;
+
+    return;
+
+error:
+    assert(rc);
+    if (!libxl_get_stubdom_id(CTX, dsps->domid))
+        libxl__remove_file(gc, dsps->dm_savefile);
+    dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
+                           const libxl__json_object *response, int rc)
+{
+    EGC_GC;
+    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
+
+    if (rc)
+        libxl__remove_file(gc, dsps->dm_savefile);
+
+    dsps->callback_device_model_done(egc, dsps, rc);
+}
+
+
+/* ------------ Implementation of libxl__ev_qmp ---------------- */
+
+/*
+ * Possible internal state compared to qmp_state:
+ *
+ * qmp_state     External   cfd    efd     id     rx_buf* tx_buf* msg* lock
+ * disconnected   Idle       NULL   Idle    reset  free    free    free Idle
+ * waiting_lock   Active     open   Idle    reset  used    free    set  Active
+ * connecting     Active     open   IN      reset  used    free    set  Acquired
+ * cap.neg        Active     open   IN|OUT  sent   used    cap_neg set  Acquired
+ * cap.neg        Active     open   IN      sent   used    free    set  Acquired
+ * connected      Connected  open   IN      any    used    free    free Acquired
+ * waiting_reply  Active     open   IN|OUT  sent   used    free    set  Acquired
+ * waiting_reply  Active     open   IN|OUT  sent   used    user's  free Acquired
+ * waiting_reply  Active     open   IN      sent   used    free    free Acquired
+ * broken[1]      none[2]    any    Active  any    any     any     any  any
+ *
+ * [1] When an internal function return an error, it can leave ev_qmp in a
+ * `broken` state but only if the caller is another internal function.
+ * That `broken` needs to be cleaned up, e.i. transitionned to the
+ * `disconnected` state, before the control of ev_qmp is released outsides
+ * of ev_qmp implementation.
+ *
+ * [2] This internal state should not be visible externally, see [1].
+ *
+ * Possible buffers states:
+ * - receiving buffer:
+ *                     free   used
+ *     rx_buf           NULL   NULL or allocated
+ *     rx_buf_size      0      allocation size of `rx_buf`
+ *     rx_buf_used      0      <= rx_buf_size, actual data in the buffer
+ * - transmitting buffer:
+ *                     free   used
+ *     tx_buf           NULL   contains data
+ *     tx_buf_len       0      size of data
+ *     tx_buf_off       0      <= tx_buf_len, data already sent
+ * - queued user command:
+ *                     free  set
+ *     msg              NULL  contains data
+ *     msg_id           0     id assoctiated with the command in `msg`
+ *
+ * - Allowed internal state transition:
+ * disconnected                     -> waiting_lock
+ * waiting_lock                     -> connecting
+ * connection                       -> capability_negotiation
+ * capability_negotiation/connected -> waiting_reply
+ * waiting_reply                    -> connected
+ * any                              -> broken
+ * broken                           -> disconnected
+ * any                              -> disconnected
+ *
+ * The QEMU Machine Protocol (QMP) specification can be found in the QEMU
+ * repository:
+ * https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/interop/qmp-spec.txt
+ */
+
+/* prototypes */
+
+static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
+                               int fd, short events, short revents);
+static int qmp_ev_callback_writable(libxl__gc *gc,
+                                    libxl__ev_qmp *ev, int fd);
+static int qmp_ev_callback_readable(libxl__egc *egc,
+                                    libxl__ev_qmp *ev, int fd);
+static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
+                               libxl__json_object **o_r);
+static int qmp_ev_handle_message(libxl__egc *egc,
+                                 libxl__ev_qmp *ev,
+                                 const libxl__json_object *resp);
+
+/* helpers */
+
+static void qmp_ev_ensure_reading_writing(libxl__gc *gc, libxl__ev_qmp *ev)
+    /* Update the state of `efd` to match the permited state
+     * on entry: !disconnected */
+{
+    short events = POLLIN;
+
+    if (ev->state == qmp_state_waiting_lock)
+        /* We can't modify the efd yet, as it isn't registered. */
+        return;
+
+    if (ev->tx_buf)
+        events |= POLLOUT;
+    else if ((ev->state == qmp_state_waiting_reply) && ev->msg)
+        events |= POLLOUT;
+
+    libxl__ev_fd_modify(gc, &ev->efd, events);
+}
+
+static void qmp_ev_set_state(libxl__gc *gc, libxl__ev_qmp *ev,
+                             libxl__qmp_state new_state)
+    /* on entry: !broken and !disconnected */
+{
+    switch (new_state) {
+    case qmp_state_disconnected:
+        break;
+    case qmp_state_waiting_lock:
+        assert(ev->state == qmp_state_disconnected);
+        break;
+    case qmp_state_connecting:
+        assert(ev->state == qmp_state_waiting_lock);
+        break;
+    case qmp_state_capability_negotiation:
+        assert(ev->state == qmp_state_connecting);
+        break;
+    case qmp_state_waiting_reply:
+        assert(ev->state == qmp_state_capability_negotiation ||
+               ev->state == qmp_state_connected);
+        break;
+    case qmp_state_connected:
+        assert(ev->state == qmp_state_waiting_reply);
+        break;
+    }
+
+    ev->state = new_state;
+
+    qmp_ev_ensure_reading_writing(gc, ev);
+}
+
+static void qmp_ev_tx_buf_clear(libxl__ev_qmp *ev)
+{
+    ev->tx_buf = NULL;
+    ev->tx_buf_len = 0;
+    ev->tx_buf_off = 0;
+}
+
+static int qmp_error_class_to_libxl_error_code(libxl__gc *gc,
+                                               const char *eclass)
+{
+    const libxl_enum_string_table *t = libxl_error_string_table;
+    const char skip[] = "QMP_";
+    const size_t skipl = sizeof(skip) - 1;
+
+    /* compare "QMP_GENERIC_ERROR" from libxl_error to "GenericError"
+     * generated by the QMP server */
+
+    for (; t->s; t++) {
+            const char *s = eclass;
+            const char *se = t->s;
+        if (strncasecmp(t->s, skip, skipl))
+            continue;
+        se += skipl;
+        while (*s && *se) {
+            /* skip underscores */
+            if (*se == '_') {
+                se++;
+                continue;
+            }
+            if (tolower(*s) != tolower(*se))
+                break;
+            s++, se++;
+        }
+        if (!*s && !*se)
+            return t->v;
+    }
+
+    LOG(ERROR, "Unknown QMP error class '%s'", eclass);
+    return ERROR_UNKNOWN_QMP_ERROR;
+}
+
+/* Setup connection */
+
+static void qmp_ev_lock_aquired(libxl__egc *, libxl__ev_slowlock *,
+                                int rc);
+static void lock_error_callback(libxl__egc *, libxl__ev_immediate *);
+
+static int qmp_ev_connect(libxl__egc *egc, libxl__ev_qmp *ev)
+    /* disconnected -> waiting_lock/connecting but with `msg` free
+     * on error: broken */
+{
+    EGC_GC;
+    int fd;
+    int rc;
+
+    /* Convenience aliases */
+    libxl__ev_slowlock *lock = &ev->lock;
+
+    assert(ev->state == qmp_state_disconnected);
+
+    libxl__carefd_begin();
+    fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    ev->cfd = libxl__carefd_opened(CTX, fd);
+    if (!ev->cfd) {
+        LOGED(ERROR, ev->domid, "socket() failed");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = libxl_fd_set_nonblock(CTX, libxl__carefd_fd(ev->cfd), 1);
+    if (rc)
+        goto out;
+
+    qmp_ev_set_state(gc, ev, qmp_state_waiting_lock);
+
+    lock->ao = ev->ao;
+    lock->domid = ev->domid;
+    lock->callback = qmp_ev_lock_aquired;
+    libxl__ev_slowlock_lock(egc, &ev->lock);
+
+    return 0;
+
+out:
+    return rc;
+}
+
+static void qmp_ev_lock_aquired(libxl__egc *egc, libxl__ev_slowlock *lock,
+                                int rc)
+    /* waiting_lock (with `lock' Acquired) -> connecting
+     * on error: broken */
+{
+    libxl__ev_qmp *ev = CONTAINER_OF(lock, *ev, lock);
+    EGC_GC;
+    const char *qmp_socket_path;
+    struct sockaddr_un un;
+    int r;
+
+    if (rc) goto out;
+
+    qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid);
+
+    LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path);
+
+    rc = libxl__prepare_sockaddr_un(gc, &un, qmp_socket_path,
+                                    "QMP socket");
+    if (rc)
+        goto out;
+
+    r = connect(libxl__carefd_fd(ev->cfd),
+                (struct sockaddr *) &un, sizeof(un));
+    if (r && errno != EINPROGRESS) {
+        LOGED(ERROR, ev->domid, "Failed to connect to QMP socket %s",
+              qmp_socket_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__ev_fd_register(gc, &ev->efd, qmp_ev_fd_callback,
+                               libxl__carefd_fd(ev->cfd), POLLIN);
+    if (rc)
+        goto out;
+
+    qmp_ev_set_state(gc, ev, qmp_state_connecting);
+
+    return;
+
+out:
+    /* An error occurred and we need to let the caller know.  At this
+     * point, we can only do so via the callback. Unfortunately, the
+     * callback of libxl__ev_slowlock_lock() might be called synchronously,
+     * but libxl__ev_qmp_send() promise that it will not call the callback
+     * synchronously. So we have to arrange to call the callback
+     * asynchronously. */
+    ev->rc = rc;
+    ev->ei.callback = lock_error_callback;
+    libxl__ev_immediate_register(egc, &ev->ei);
+}
+
+static void lock_error_callback(libxl__egc *egc, libxl__ev_immediate *ei)
+    /* broken -> disconnected */
+{
+    EGC_GC;
+    libxl__ev_qmp *ev = CONTAINER_OF(ei, *ev, ei);
+
+    int rc = ev->rc;
+
+    /* On error, deallocate all private resources */
+    libxl__ev_qmp_dispose(gc, ev);
+
+    /* And tell libxl__ev_qmp user about the error */
+    ev->callback(egc, ev, NULL, rc); /* must be last */
+}
+
+/* QMP FD callbacks */
+
+static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
+                               int fd, short events, short revents)
+    /* On entry, ev_fd is (of course) Active.  The ev_qmp may be in any
+     * state where this is permitted.  qmp_ev_fd_callback will do the work
+     * necessary to make progress, depending on the current state, and make
+     * the appropriate state transitions and callbacks.  */
+{
+    libxl__ev_qmp *ev = CONTAINER_OF(ev_fd, *ev, efd);
+    STATE_AO_GC(ev->ao);
+    int rc;
+
+    if (revents & (POLLHUP|POLLERR)) {
+        int r;
+        int error_val = 0;
+        socklen_t opt_len = sizeof(error_val);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error_val, &opt_len);
+        if (r)
+            LOGED(ERROR, ev->domid, "getsockopt failed");
+        if (!r && error_val) {
+            errno = error_val;
+            LOGED(ERROR, ev->domid, "error on QMP socket");
+        } else {
+            LOGD(ERROR, ev->domid,
+                 "received POLLHUP|POLLERR from QMP socket");
+        }
+        rc = ERROR_PROTOCOL_ERROR_QMP;
+        goto error;
+    }
+
+    if (revents & ~(POLLIN|POLLOUT)) {
+        LOGD(ERROR, ev->domid,
+             "unexpected poll event 0x%x on QMP socket (expected POLLIN "
+             "and/or POLLOUT)",
+            revents);
+        rc = ERROR_FAIL;
+        goto error;
+    }
+
+    if (revents & POLLOUT) {
+        rc = qmp_ev_callback_writable(gc, ev, fd);
+        if (rc)
+            goto error;
+    }
+
+    if (revents & POLLIN) {
+        rc = qmp_ev_callback_readable(egc, ev, fd);
+        if (rc < 0)
+            goto error;
+        if (rc == 1) {
+            /* user callback has been called */
+            return;
+        }
+    }
+
+    return;
+
+error:
+    assert(rc);
+
+    LOGD(ERROR, ev->domid,
+         "Error happened with the QMP connection to QEMU");
+
+    /* On error, deallocate all private ressources */
+    libxl__ev_qmp_dispose(gc, ev);
+
+    /* And tell libxl__ev_qmp user about the error */
+    ev->callback(egc, ev, NULL, rc); /* must be last */
+}
+
+static int qmp_ev_callback_writable(libxl__gc *gc,
+                                    libxl__ev_qmp *ev, int fd)
+    /* on entry: !disconnected
+     * on return, one of these state transition:
+     *   waiting_reply (with msg set) -> waiting_reply (with msg free)
+     *   tx_buf set -> same state or tx_buf free
+     * on error: broken */
+{
+    int rc;
+    ssize_t r;
+
+    if (ev->state == qmp_state_waiting_reply) {
+        if (ev->msg) {
+            assert(!ev->tx_buf);
+            ev->tx_buf = ev->msg;
+            ev->tx_buf_len = strlen(ev->msg);
+            ev->tx_buf_off = 0;
+            ev->id = ev->msg_id;
+            ev->msg = NULL;
+            ev->msg_id = 0;
+        }
+    }
+
+    assert(ev->tx_buf);
+
+    LOG_QMP("sending: '%.*s'", (int)ev->tx_buf_len, ev->tx_buf);
+
+    /*
+     * We will send a file descriptor associated with a command on the
+     * first byte of this command.
+     */
+    if (ev->state == qmp_state_waiting_reply &&
+        ev->payload_fd >= 0 &&
+        ev->tx_buf_off == 0) {
+
+        rc = libxl__sendmsg_fds(gc, fd, ev->tx_buf[ev->tx_buf_off],
+                                1, &ev->payload_fd, "QMP socket");
+        /* Check for EWOULDBLOCK, and return to try again later */
+        if (rc == ERROR_NOT_READY)
+            return 0;
+        if (rc)
+            return rc;
+        ev->tx_buf_off++;
+    }
+
+    while (ev->tx_buf_off < ev->tx_buf_len) {
+        ssize_t max_write = ev->tx_buf_len - ev->tx_buf_off;
+        r = write(fd, ev->tx_buf + ev->tx_buf_off, max_write);
+        if (r < 0) {
+            if (errno == EINTR)
+                continue;
+            if (errno == EWOULDBLOCK)
+                break;
+            LOGED(ERROR, ev->domid, "failed to write to QMP socket");
+            return ERROR_FAIL;
+        }
+        assert(r > 0 && r <= max_write);
+        ev->tx_buf_off += r;
+    }
+
+    if (ev->tx_buf_off == ev->tx_buf_len)
+        qmp_ev_tx_buf_clear(ev);
+
+    qmp_ev_ensure_reading_writing(gc, ev);
+
+    return 0;
+}
+
+static int qmp_ev_callback_readable(libxl__egc *egc,
+                                    libxl__ev_qmp *ev, int fd)
+    /*
+     * Return values:
+     *   < 0    libxl error code
+     *   0      success
+     *   1      success, but a user callback has been called,
+     *          `ev` should not be used anymore.
+     *
+     * This function will update the rx buffer and possibly update
+     * ev->state:
+     *  connecting             -> capability_negotiation
+     *  capability_negotiation -> waiting_reply
+     *  waiting_reply          -> connected
+     * on error: broken
+     */
+{
+    STATE_AO_GC(ev->ao);
+    int rc;
+    ssize_t r;
+
+    while (1) {
+        while (1) {
+            libxl__json_object *o = NULL;
+
+            /* parse rx buffer to find one json object */
+            rc = qmp_ev_get_next_msg(egc, ev, &o);
+            if (rc == ERROR_NOTFOUND)
+                break;
+            else if (rc)
+                return rc;
+
+            /* Must be last and return when the user callback is called */
+            rc = qmp_ev_handle_message(egc, ev, o);
+            if (rc)
+                /* returns both rc values -ERROR_* and 1 */
+                return rc;
+        }
+
+        /* Check if the buffer still have space, or increase size */
+        if (ev->rx_buf_size - ev->rx_buf_used < QMP_RECEIVE_BUFFER_SIZE) {
+            size_t newsize = ev->rx_buf_size * 2 + QMP_RECEIVE_BUFFER_SIZE;
+
+            if (newsize > QMP_MAX_SIZE_RX_BUF) {
+                LOGD(ERROR, ev->domid,
+                     "QMP receive buffer is too big (%zu > %lld)",
+                     newsize, QMP_MAX_SIZE_RX_BUF);
+                return ERROR_BUFFERFULL;
+            }
+            ev->rx_buf_size = newsize;
+            ev->rx_buf = libxl__realloc(gc, ev->rx_buf, ev->rx_buf_size);
+        }
+
+        r = read(fd, ev->rx_buf + ev->rx_buf_used,
+                 ev->rx_buf_size - ev->rx_buf_used);
+        if (r < 0) {
+            if (errno == EINTR)
+                continue;
+            if (errno == EWOULDBLOCK)
+                break;
+            LOGED(ERROR, ev->domid, "error reading QMP socket");
+            return ERROR_FAIL;
+        }
+
+        if (r == 0) {
+            LOGD(ERROR, ev->domid, "Unexpected EOF on QMP socket");
+            return ERROR_PROTOCOL_ERROR_QMP;
+        }
+
+        LOG_QMP("received %ldB: '%.*s'", r,
+                (int)r, ev->rx_buf + ev->rx_buf_used);
+
+        ev->rx_buf_used += r;
+        assert(ev->rx_buf_used <= ev->rx_buf_size);
+    }
+
+    return 0;
+}
+
+/* Handle messages received from QMP server */
+
+static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
+                               libxl__json_object **o_r)
+    /* Find a JSON object and store it in o_r.
+     * return ERROR_NOTFOUND if no object is found.
+     *
+     * !disconnected -> same state (with rx buffer updated)
+     */
+{
+    STATE_AO_GC(ev->ao);
+    size_t len;
+    char *end = NULL;
+    const char eom[] = "\r\n";
+    const size_t eoml = sizeof(eom) - 1;
+    libxl__json_object *o = NULL;
+
+    if (!ev->rx_buf_used)
+        return ERROR_NOTFOUND;
+
+    /* Search for the end of a QMP message: "\r\n" */
+    end = memmem(ev->rx_buf, ev->rx_buf_used, eom, eoml);
+    if (!end)
+        return ERROR_NOTFOUND;
+    len = (end - ev->rx_buf) + eoml;
+
+    LOG_QMP("parsing %luB: '%.*s'", len, (int)len, ev->rx_buf);
+
+    /* Replace \r by \0 so that libxl__json_parse can use strlen */
+    ev->rx_buf[len - eoml] = '\0';
+    o = libxl__json_parse(gc, ev->rx_buf);
+
+    if (!o) {
+        LOGD(ERROR, ev->domid, "Parse error");
+        return ERROR_PROTOCOL_ERROR_QMP;
+    }
+
+    ev->rx_buf_used -= len;
+    memmove(ev->rx_buf, ev->rx_buf + len, ev->rx_buf_used);
+
+    LOG_QMP("JSON object received: %s", JSON(o));
+
+    *o_r = o;
+
+    return 0;
+}
+
+static int qmp_ev_parse_error_messages(libxl__egc *egc,
+                                       libxl__ev_qmp *ev,
+                                       const libxl__json_object *resp);
+
+static int qmp_ev_handle_message(libxl__egc *egc,
+                                 libxl__ev_qmp *ev,
+                                 const libxl__json_object *resp)
+    /*
+     * This function will handle every messages sent by the QMP server.
+     * Return values:
+     *   < 0    libxl error code
+     *   0      success
+     *   1      success, but a user callback has been called,
+     *          `ev` should not be used anymore.
+     *
+     * Possible state changes:
+     * connecting -> capability_negotiation
+     * capability_negotiation -> waiting_reply
+     * waiting_reply -> waiting_reply/connected
+     *
+     * on error: broken
+     */
+{
+    STATE_AO_GC(ev->ao);
+    int id;
+    char *buf;
+    int rc = 0;
+    const libxl__json_object *o;
+    const libxl__json_object *response;
+    libxl__qmp_message_type type = qmp_response_type(resp);
+
+    switch (type) {
+    case LIBXL__QMP_MESSAGE_TYPE_QMP:
+        /* greeting message */
+
+        if (ev->state != qmp_state_connecting) {
+            LOGD(ERROR, ev->domid,
+                 "Unexpected greeting message received");
+            return ERROR_PROTOCOL_ERROR_QMP;
+        }
+
+        /*
+         * Store advertised QEMU version
+         * { "QMP": { "version": {
+         *     "qemu": { "major": int, "minor": int, "micro": int } } } }
+         */
+        o = libxl__json_map_get("QMP", resp, JSON_MAP);
+        o = libxl__json_map_get("version", o, JSON_MAP);
+        o = libxl__json_map_get("qemu", o, JSON_MAP);
+#define GRAB_VERSION(level) do { \
+        ev->qemu_version.level = libxl__json_object_get_integer( \
+            libxl__json_map_get(#level, o, JSON_INTEGER)); \
+        } while (0)
+        GRAB_VERSION(major);
+        GRAB_VERSION(minor);
+        GRAB_VERSION(micro);
+#undef GRAB_VERSION
+        LOGD(DEBUG, ev->domid, "QEMU version: %d.%d.%d",
+             ev->qemu_version.major,
+             ev->qemu_version.minor,
+             ev->qemu_version.micro);
+
+        /* Prepare next message to send */
+        assert(!ev->tx_buf);
+        ev->id = ev->next_id++;
+        buf = qmp_prepare_cmd(gc, "qmp_capabilities", NULL, ev->id);
+        if (!buf) {
+            LOGD(ERROR, ev->domid,
+                 "Failed to generate qmp_capabilities command");
+            return ERROR_FAIL;
+        }
+        ev->tx_buf = buf;
+        ev->tx_buf_len = strlen(buf);
+        ev->tx_buf_off = 0;
+        qmp_ev_set_state(gc, ev, qmp_state_capability_negotiation);
+
+        return 0;
+
+    case LIBXL__QMP_MESSAGE_TYPE_RETURN:
+    case LIBXL__QMP_MESSAGE_TYPE_ERROR:
+        /*
+         * Reply to a command (success/error) or server error
+         *
+         * In this cases, we are parsing two possibles responses:
+         * - success:
+         * { "return": json-value, "id": int }
+         * - error:
+         * { "error": { "class": string, "desc": string }, "id": int }
+         */
+
+        o = libxl__json_map_get("id", resp, JSON_INTEGER);
+        if (!o) {
+            /*
+             * If "id" isn't present, an error occur on the server before
+             * it has read the "id" provided by libxl.
+             *
+             * We deliberately squash all errors into
+             * ERROR_PROTOCOL_ERROR_QMP as qmp_ev_parse_error_messages may
+             * also return ERROR_QMP_* but those are reserved for errors
+             * return by the caller's command.
+             */
+            qmp_ev_parse_error_messages(egc, ev, resp);
+            return ERROR_PROTOCOL_ERROR_QMP;
+        }
+
+        id = libxl__json_object_get_integer(o);
+
+        if (id != ev->id) {
+            LOGD(ERROR, ev->domid,
+                 "Message from QEMU with unexpected id %d: %s",
+                 id, JSON(resp));
+            return ERROR_PROTOCOL_ERROR_QMP;
+        }
+
+        switch (ev->state) {
+        case qmp_state_capability_negotiation:
+            if (type != LIBXL__QMP_MESSAGE_TYPE_RETURN) {
+                LOGD(ERROR, ev->domid,
+                     "Error during capability negotiation: %s",
+                     JSON(resp));
+                return ERROR_PROTOCOL_ERROR_QMP;
+            }
+            qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
+            return 0;
+        case qmp_state_waiting_reply:
+            if (type == LIBXL__QMP_MESSAGE_TYPE_RETURN) {
+                response = libxl__json_map_get("return", resp, JSON_ANY);
+                rc = 0;
+            } else {
+                /* error message */
+                response = NULL;
+                rc = qmp_ev_parse_error_messages(egc, ev, resp);
+            }
+            qmp_ev_set_state(gc, ev, qmp_state_connected);
+            ev->callback(egc, ev, response, rc); /* must be last */
+            return 1;
+        default:
+            LOGD(ERROR, ev->domid, "Unexpected message: %s", JSON(resp));
+            return ERROR_PROTOCOL_ERROR_QMP;
+        }
+        return 0;
+
+    case LIBXL__QMP_MESSAGE_TYPE_EVENT:
+        /* Events are ignored */
+        return 0;
+
+    case LIBXL__QMP_MESSAGE_TYPE_INVALID:
+        LOGD(ERROR, ev->domid, "Unexpected message received: %s",
+             JSON(resp));
+        return ERROR_PROTOCOL_ERROR_QMP;
+
+    default:
+        abort();
+    }
+
+    return 0;
+}
+
+static int qmp_ev_parse_error_messages(libxl__egc *egc,
+                                       libxl__ev_qmp *ev,
+                                       const libxl__json_object *resp)
+    /* no state change */
+{
+    STATE_AO_GC(ev->ao);
+    int rc;
+    const char *s;
+    const libxl__json_object *o;
+    const libxl__json_object *err;
+
+    /*
+     * { "error": { "class": string, "desc": string } }
+     */
+
+    err = libxl__json_map_get("error", resp, JSON_MAP);
+
+    o = libxl__json_map_get("class", err, JSON_STRING);
+    if (!o) {
+        LOGD(ERROR, ev->domid,
+             "Protocol error: missing 'class' member in error message");
+        return ERROR_PROTOCOL_ERROR_QMP;
+    }
+    s = libxl__json_object_get_string(o);
+    if (s)
+        rc = qmp_error_class_to_libxl_error_code(gc, s);
+    else
+        rc = ERROR_PROTOCOL_ERROR_QMP;
+
+    o = libxl__json_map_get("desc", err, JSON_STRING);
+    if (!o) {
+        LOGD(ERROR, ev->domid,
+             "Protocol error: missing 'desc' member in error message");
+        return ERROR_PROTOCOL_ERROR_QMP;
+    }
+    s = libxl__json_object_get_string(o);
+    if (s)
+        LOGD(ERROR, ev->domid, "%s", s);
+    else
+        LOGD(ERROR, ev->domid, "Received unexpected error: %s",
+             JSON(resp));
+    return rc;
+}
+
+/*
+ * libxl__ev_qmp_*
+ */
+
+void libxl__ev_qmp_init(libxl__ev_qmp *ev)
+    /* disconnected -> disconnected */
+{
+    /* Start with an message ID that is obviously generated by libxl
+     * "xlq\0" */
+    ev->next_id = 0x786c7100;
+
+    ev->cfd = NULL;
+    libxl__ev_fd_init(&ev->efd);
+    ev->state = qmp_state_disconnected;
+    ev->id = 0;
+
+    ev->rx_buf = NULL;
+    ev->rx_buf_size = ev->rx_buf_used = 0;
+    qmp_ev_tx_buf_clear(ev);
+
+    ev->msg = NULL;
+    ev->msg_id = 0;
+
+    ev->qemu_version.major = -1;
+    ev->qemu_version.minor = -1;
+    ev->qemu_version.micro = -1;
+
+    libxl__ev_qmplock_init(&ev->lock);
+    ev->rc = 0;
+}
+
+int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
+                       const char *cmd, libxl__json_object *args)
+    /* disconnected -> waiting_lock/connecting
+     * connected -> waiting_reply (with msg set)
+     * on error: disconnected */
+{
+    STATE_AO_GC(ev->ao);
+    int rc;
+
+    LOGD(DEBUG, ev->domid, " ev %p, cmd '%s'", ev, cmd);
+
+    assert(ev->state == qmp_state_disconnected ||
+           ev->state == qmp_state_connected);
+    assert(cmd);
+
+    /* Connect to QEMU if not already connected */
+    if (ev->state == qmp_state_disconnected) {
+        rc = qmp_ev_connect(egc, ev);
+        if (rc)
+            goto error;
+    }
+
+    /* Prepare user command */
+    ev->msg_id = ev->next_id++;
+    ev->msg = qmp_prepare_cmd(gc, cmd, args, ev->msg_id);
+    if (!ev->msg) {
+        LOGD(ERROR, ev->domid, "Failed to generate caller's command %s",
+             cmd);
+        rc = ERROR_FAIL;
+        goto error;
+    }
+    if (ev->state == qmp_state_connected) {
+        qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
+    }
+
+    return 0;
+
+error:
+    libxl__ev_qmp_dispose(gc, ev);
+    return rc;
+}
+
+void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev)
+    /* * -> disconnected */
+{
+    LOGD(DEBUG, ev->domid, " ev %p", ev);
+
+    libxl__ev_fd_deregister(gc, &ev->efd);
+    libxl__carefd_close(ev->cfd);
+    libxl__ev_slowlock_dispose(gc, &ev->lock);
+
+    libxl__ev_qmp_init(ev);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_remus.c b/tools/libs/light/libxl_remus.c
new file mode 100644 (file)
index 0000000..6338a1b
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *        Yang Hongyang <hongyang.yang@easystack.cn>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+extern const libxl__checkpoint_device_instance_ops remus_device_nic;
+extern const libxl__checkpoint_device_instance_ops remus_device_drbd_disk;
+static const libxl__checkpoint_device_instance_ops *remus_ops[] = {
+    &remus_device_nic,
+    &remus_device_drbd_disk,
+    NULL,
+};
+
+/*----- helper functions -----*/
+
+static int init_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+    /* init device subkind-specific state in the libxl ctx */
+    int rc;
+    STATE_AO_GC(cds->ao);
+
+    if (libxl__netbuffer_enabled(gc)) {
+        rc = init_subkind_nic(cds);
+        if (rc) goto out;
+    }
+
+    rc = init_subkind_drbd_disk(cds);
+    if (rc) goto out;
+
+    rc = 0;
+out:
+    return rc;
+}
+
+static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
+{
+    /* cleanup device subkind-specific state in the libxl ctx */
+    STATE_AO_GC(cds->ao);
+
+    if (libxl__netbuffer_enabled(gc))
+        cleanup_subkind_nic(cds);
+
+    cleanup_subkind_drbd_disk(cds);
+}
+
+/*-------------------- Remus setup and teardown ---------------------*/
+
+static void remus_setup_done(libxl__egc *egc,
+                             libxl__checkpoint_devices_state *cds, int rc);
+static void remus_setup_failed(libxl__egc *egc,
+                               libxl__checkpoint_devices_state *cds, int rc);
+static void remus_checkpoint_stream_written(
+    libxl__egc *egc, libxl__stream_write_state *sws, int rc);
+static void libxl__remus_domain_suspend_callback(void *data);
+static void libxl__remus_domain_resume_callback(void *data);
+static void libxl__remus_domain_save_checkpoint_callback(void *data);
+
+void libxl__remus_setup(libxl__egc *egc, libxl__remus_state *rs)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+    const libxl_domain_remus_info *const info = dss->remus;
+    libxl__srm_save_autogen_callbacks *const callbacks =
+        &dss->sws.shs.callbacks.save.a;
+
+    STATE_AO_GC(dss->ao);
+
+    if (libxl_defbool_val(info->netbuf)) {
+        if (!libxl__netbuffer_enabled(gc)) {
+            LOGD(ERROR, dss->domid,
+                 "Remus: No support for network buffering");
+            goto out;
+        }
+        cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VIF);
+    }
+
+    if (libxl_defbool_val(info->diskbuf))
+        cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VBD);
+
+    cds->ao = ao;
+    cds->domid = dss->domid;
+    cds->callback = remus_setup_done;
+    cds->ops = remus_ops;
+    cds->concrete_data = rs;
+    rs->interval = info->interval;
+
+    if (init_device_subkind(cds)) {
+        LOGD(ERROR, dss->domid,
+             "Remus: failed to init device subkind");
+        goto out;
+    }
+
+    dss->sws.checkpoint_callback = remus_checkpoint_stream_written;
+
+    callbacks->suspend = libxl__remus_domain_suspend_callback;
+    callbacks->postcopy = libxl__remus_domain_resume_callback;
+    callbacks->checkpoint = libxl__remus_domain_save_checkpoint_callback;
+
+    libxl__checkpoint_devices_setup(egc, cds);
+    return;
+
+out:
+    dss->callback(egc, dss, ERROR_FAIL);
+}
+
+static void remus_setup_done(libxl__egc *egc,
+                             libxl__checkpoint_devices_state *cds, int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+    STATE_AO_GC(dss->ao);
+
+    if (!rc) {
+        libxl__domain_save(egc, dss);
+        return;
+    }
+
+    LOGD(ERROR, dss->domid, "Remus: failed to setup device, rc %d", rc);
+    cds->callback = remus_setup_failed;
+    libxl__checkpoint_devices_teardown(egc, cds);
+}
+
+static void remus_setup_failed(libxl__egc *egc,
+                               libxl__checkpoint_devices_state *cds, int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+    STATE_AO_GC(dss->ao);
+
+    if (rc)
+        LOGD(ERROR, dss->domid,
+             "Remus: failed to teardown device after setup failed, rc %d", rc);
+
+    cleanup_device_subkind(cds);
+
+    dss->callback(egc, dss, rc);
+}
+
+static void remus_teardown_done(libxl__egc *egc,
+                                libxl__checkpoint_devices_state *cds,
+                                int rc);
+void libxl__remus_teardown(libxl__egc *egc,
+                           libxl__remus_state *rs,
+                           int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+    EGC_GC;
+
+    LOGD(WARN, dss->domid, "Remus: Domain suspend terminated with rc %d,"
+         " teardown Remus devices...", rc);
+    cds->callback = remus_teardown_done;
+    libxl__checkpoint_devices_teardown(egc, cds);
+}
+
+static void remus_teardown_done(libxl__egc *egc,
+                                libxl__checkpoint_devices_state *cds,
+                                int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+    STATE_AO_GC(dss->ao);
+
+    if (rc)
+        LOGD(ERROR, dss->domid, "Remus: failed to teardown device,"
+            " rc %d", rc);
+
+    cleanup_device_subkind(cds);
+
+    dss->callback(egc, dss, rc);
+}
+
+/*---------------------- remus callbacks (save) -----------------------*/
+
+static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
+                                libxl__domain_suspend_state *dsps, int ok);
+static void remus_devices_postsuspend_cb(libxl__egc *egc,
+                                         libxl__checkpoint_devices_state *cds,
+                                         int rc);
+static void remus_devices_preresume_cb(libxl__egc *egc,
+                                       libxl__checkpoint_devices_state *cds,
+                                       int rc);
+
+static void libxl__remus_domain_suspend_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__egc *egc = shs->egc;
+    libxl__domain_save_state *dss = shs->caller_state;
+    libxl__domain_suspend_state *dsps = &dss->dsps;
+
+    dsps->callback_common_done = remus_domain_suspend_callback_common_done;
+    libxl__domain_suspend(egc, dsps);
+}
+
+static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
+                                libxl__domain_suspend_state *dsps, int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
+
+    if (rc)
+        goto out;
+
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+    cds->callback = remus_devices_postsuspend_cb;
+    libxl__checkpoint_devices_postsuspend(egc, cds);
+    return;
+
+out:
+    dss->rc = rc;
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void remus_devices_postsuspend_cb(libxl__egc *egc,
+                                         libxl__checkpoint_devices_state *cds,
+                                         int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+
+    if (rc)
+        goto out;
+
+    rc = 0;
+
+out:
+    if (rc)
+        dss->rc = rc;
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+static void libxl__remus_domain_resume_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__egc *egc = shs->egc;
+    libxl__domain_save_state *dss = shs->caller_state;
+    STATE_AO_GC(dss->ao);
+
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+    cds->callback = remus_devices_preresume_cb;
+    libxl__checkpoint_devices_preresume(egc, cds);
+}
+
+static void remus_devices_preresume_cb(libxl__egc *egc,
+                                       libxl__checkpoint_devices_state *cds,
+                                       int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+    STATE_AO_GC(dss->ao);
+
+    if (rc)
+        goto out;
+
+    /* Resumes the domain and the device model */
+    rc = libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1);
+    if (rc)
+        goto out;
+
+    rc = 0;
+
+out:
+    if (rc)
+        dss->rc = rc;
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+/*----- remus asynchronous checkpoint callback -----*/
+
+static void remus_devices_commit_cb(libxl__egc *egc,
+                                    libxl__checkpoint_devices_state *cds,
+                                    int rc);
+static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
+                                  const struct timeval *requested_abs,
+                                  int rc);
+
+static void libxl__remus_domain_save_checkpoint_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__domain_save_state *dss = shs->caller_state;
+    libxl__egc *egc = shs->egc;
+    STATE_AO_GC(dss->ao);
+
+    libxl__stream_write_start_checkpoint(egc, &dss->sws);
+}
+
+static void remus_checkpoint_stream_written(
+    libxl__egc *egc, libxl__stream_write_state *sws, int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(sws, *dss, sws);
+
+    /* Convenience aliases */
+    libxl__checkpoint_devices_state *const cds = &dss->cds;
+
+    STATE_AO_GC(dss->ao);
+
+    if (rc) {
+        LOGD(ERROR, dss->domid, "Failed to save device model."
+             " Terminating Remus..");
+        goto out;
+    }
+
+    cds->callback = remus_devices_commit_cb;
+    libxl__checkpoint_devices_commit(egc, cds);
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void remus_devices_commit_cb(libxl__egc *egc,
+                                    libxl__checkpoint_devices_state *cds,
+                                    int rc)
+{
+    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
+
+    STATE_AO_GC(dss->ao);
+
+    if (rc) {
+        LOGD(ERROR, dss->domid, "Failed to do device commit op."
+            " Terminating Remus..");
+        goto out;
+    }
+
+    /*
+     * At this point, we have successfully checkpointed the guest and
+     * committed it at the backup. We'll come back after the checkpoint
+     * interval to checkpoint the guest again. Until then, let the guest
+     * continue execution.
+     */
+
+    /* Set checkpoint interval timeout */
+    rc = libxl__ev_time_register_rel(ao, &dss->rs.checkpoint_timeout,
+                                     remus_next_checkpoint,
+                                     dss->rs.interval);
+
+    if (rc)
+        goto out;
+
+    return;
+
+out:
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
+}
+
+static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
+                                  const struct timeval *requested_abs,
+                                  int rc)
+{
+    libxl__domain_save_state *dss =
+                            CONTAINER_OF(ev, *dss, rs.checkpoint_timeout);
+
+    STATE_AO_GC(dss->ao);
+
+    if (rc == ERROR_TIMEDOUT) /* As intended */
+        rc = 0;
+
+    /*
+     * Time to checkpoint the guest again. We return 1 to libxc
+     * (xc_domain_save.c). in order to continue executing the infinite loop
+     * (suspend, checkpoint, resume) in xc_domain_save().
+     */
+
+    if (rc)
+        dss->rc = rc;
+
+    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
+}
+
+/*---------------------- remus callbacks (restore) -----------------------*/
+
+/*----- remus asynchronous checkpoint callback -----*/
+
+static void remus_checkpoint_stream_done(
+    libxl__egc *egc, libxl__stream_read_state *srs, int rc);
+
+static void libxl__remus_domain_restore_checkpoint_callback(void *data)
+{
+    libxl__save_helper_state *shs = data;
+    libxl__domain_create_state *dcs = shs->caller_state;
+    libxl__egc *egc = shs->egc;
+    STATE_AO_GC(dcs->ao);
+
+    libxl__stream_read_start_checkpoint(egc, &dcs->srs);
+}
+
+static void remus_checkpoint_stream_done(
+    libxl__egc *egc, libxl__stream_read_state *stream, int rc)
+{
+    libxl__xc_domain_saverestore_async_callback_done(egc, &stream->shs, rc);
+}
+
+void libxl__remus_restore_setup(libxl__egc *egc,
+                                libxl__domain_create_state *dcs)
+{
+    /* Convenience aliases */
+    libxl__srm_restore_autogen_callbacks *const callbacks =
+        &dcs->srs.shs.callbacks.restore.a;
+
+    callbacks->checkpoint = libxl__remus_domain_restore_checkpoint_callback;
+    dcs->srs.checkpoint_callback = remus_checkpoint_stream_done;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_remus_disk_drbd.c b/tools/libs/light/libxl_remus_disk_drbd.c
new file mode 100644 (file)
index 0000000..d08e470
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2014 FUJITSU LIMITED
+ * Author Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*** drbd implementation ***/
+const int DRBD_SEND_CHECKPOINT = 20;
+const int DRBD_WAIT_CHECKPOINT_ACK = 30;
+
+typedef struct libxl__remus_drbd_disk {
+    int ctl_fd;
+    int ackwait;
+} libxl__remus_drbd_disk;
+
+int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
+{
+    libxl__remus_state *rs = cds->concrete_data;
+    STATE_AO_GC(cds->ao);
+
+    rs->drbd_probe_script = GCSPRINTF("%s/block-drbd-probe",
+                                      libxl__xen_script_dir_path());
+
+    return 0;
+}
+
+void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
+{
+    return;
+}
+
+/*----- match(), setup() and teardown() -----*/
+
+/* callbacks */
+static void match_async_exec_cb(libxl__egc *egc,
+                                libxl__async_exec_state *aes,
+                                int rc, int status);
+
+/* implementations */
+
+static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev);
+
+static void drbd_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    STATE_AO_GC(dev->cds->ao);
+
+    match_async_exec(egc, dev);
+}
+
+static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    int arraysize, nr = 0, rc;
+    const libxl_device_disk *disk = dev->backend_dev;
+    libxl__async_exec_state *aes = &dev->aodev.aes;
+    libxl__remus_state *rs = dev->cds->concrete_data;
+    STATE_AO_GC(dev->cds->ao);
+
+    /* setup env & args */
+    arraysize = 1;
+    GCNEW_ARRAY(aes->env, arraysize);
+    aes->env[nr++] = NULL;
+    assert(nr <= arraysize);
+
+    arraysize = 3;
+    nr = 0;
+    GCNEW_ARRAY(aes->args, arraysize);
+    aes->args[nr++] = rs->drbd_probe_script;
+    aes->args[nr++] = disk->pdev_path;
+    aes->args[nr++] = NULL;
+    assert(nr <= arraysize);
+
+    aes->ao = dev->cds->ao;
+    aes->what = GCSPRINTF("%s %s", aes->args[0], aes->args[1]);
+    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
+    aes->callback = match_async_exec_cb;
+    aes->stdfds[0] = -1;
+    aes->stdfds[1] = -1;
+    aes->stdfds[2] = -1;
+
+    rc = libxl__async_exec_start(aes);
+    if (rc)
+        goto out;
+
+    return;
+
+out:
+    dev->aodev.rc = rc;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+static void match_async_exec_cb(libxl__egc *egc,
+                                libxl__async_exec_state *aes,
+                                int rc, int status)
+{
+    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
+    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+    libxl__remus_drbd_disk *drbd_disk;
+    const libxl_device_disk *disk = dev->backend_dev;
+
+    STATE_AO_GC(aodev->ao);
+
+    if (rc)
+        goto out;
+
+    if (status) {
+        rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
+        /* BUG: seems to assume that any exit status means `no match' */
+        /* BUG: exit status will have been logged as an error */
+        goto out;
+    }
+
+    /* ops matched */
+    dev->matched = true;
+
+    GCNEW(drbd_disk);
+    dev->concrete_data = drbd_disk;
+    drbd_disk->ackwait = 0;
+    drbd_disk->ctl_fd = open(disk->pdev_path, O_RDONLY);
+    if (drbd_disk->ctl_fd < 0) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+static void drbd_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    libxl__remus_drbd_disk *drbd_disk = dev->concrete_data;
+    STATE_AO_GC(dev->cds->ao);
+
+    close(drbd_disk->ctl_fd);
+    dev->aodev.rc = 0;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+/*----- checkpointing APIs -----*/
+
+/* callbacks */
+static void checkpoint_async_call_done(libxl__egc *egc,
+                                       libxl__ev_child *child,
+                                       pid_t pid, int status);
+
+/* API implementations */
+
+/* this op will not wait and block, so implement as sync op */
+static void drbd_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    STATE_AO_GC(dev->cds->ao);
+
+    libxl__remus_drbd_disk *rdd = dev->concrete_data;
+
+    if (!rdd->ackwait) {
+        if (ioctl(rdd->ctl_fd, DRBD_SEND_CHECKPOINT, 0) <= 0)
+            rdd->ackwait = 1;
+    }
+
+    dev->aodev.rc = 0;
+    dev->aodev.callback(egc, &dev->aodev);
+}
+
+
+static void drbd_preresume_async(libxl__checkpoint_device *dev);
+
+static void drbd_preresume(libxl__egc *egc, libxl__checkpoint_device *dev)
+{
+    ASYNC_CALL(egc, dev->cds->ao, &dev->aodev.child, dev,
+               drbd_preresume_async,
+               checkpoint_async_call_done);
+}
+
+static void drbd_preresume_async(libxl__checkpoint_device *dev)
+{
+    libxl__remus_drbd_disk *rdd = dev->concrete_data;
+    int ackwait = rdd->ackwait;
+
+    if (ackwait) {
+        ioctl(rdd->ctl_fd, DRBD_WAIT_CHECKPOINT_ACK, 0);
+        ackwait = 0;
+    }
+
+    _exit(ackwait);
+}
+
+static void checkpoint_async_call_done(libxl__egc *egc,
+                                       libxl__ev_child *child,
+                                       pid_t pid, int status)
+{
+    int rc;
+    libxl__ao_device *aodev = CONTAINER_OF(child, *aodev, child);
+    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
+    libxl__remus_drbd_disk *rdd = dev->concrete_data;
+
+    STATE_AO_GC(aodev->ao);
+
+    if (!WIFEXITED(status)) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rdd->ackwait = WEXITSTATUS(status);
+    rc = 0;
+
+out:
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+const libxl__checkpoint_device_instance_ops remus_device_drbd_disk = {
+    .kind = LIBXL__DEVICE_KIND_VBD,
+    .setup = drbd_setup,
+    .teardown = drbd_teardown,
+    .postsuspend = drbd_postsuspend,
+    .preresume = drbd_preresume,
+};
diff --git a/tools/libs/light/libxl_save_callout.c b/tools/libs/light/libxl_save_callout.c
new file mode 100644 (file)
index 0000000..0b11495
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2012      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/* stream_fd is as from the caller (eventually, the application).
+ * It may be 0, 1 or 2, in which case we need to dup it elsewhere.
+ * The actual fd value is not included in the supplied argnums; rather
+ * it will be automatically supplied by run_helper as the 2nd argument.
+ *
+ * preserve_fds are fds that the caller is intending to pass to the
+ * helper so which need cloexec clearing.  They may not be 0, 1 or 2.
+ * An entry may be -1 in which case it will be ignored.
+ */
+static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
+                       const char *mode_arg,
+                       int stream_fd, int back_channel_fd,
+                       const int *preserve_fds, int num_preserve_fds,
+                       const unsigned long *argnums, int num_argnums);
+
+static void helper_failed(libxl__egc*, libxl__save_helper_state *shs, int rc);
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable*, int rc);
+static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
+                                   int fd, short events, short revents);
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+                          pid_t pid, int status);
+static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs);
+
+/*----- entrypoints -----*/
+
+void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs,
+                              libxl__save_helper_state *shs)
+{
+    STATE_AO_GC(dcs->ao);
+
+    /* Convenience aliases */
+    const uint32_t domid = dcs->guest_domid;
+    const int restore_fd = dcs->libxc_fd;
+    const int send_back_fd = dcs->send_back_fd;
+    libxl__domain_build_state *const state = &dcs->build_state;
+
+    unsigned cbflags =
+        libxl__srm_callout_enumcallbacks_restore(&shs->callbacks.restore.a);
+
+    const unsigned long argnums[] = {
+        domid,
+        state->store_port,
+        state->store_domid, state->console_port,
+        state->console_domid,
+        cbflags, dcs->restore_params.checkpointed_stream,
+    };
+
+    shs->ao = ao;
+    shs->domid = domid;
+    shs->recv_callback = libxl__srm_callout_received_restore;
+    if (dcs->restore_params.checkpointed_stream ==
+        LIBXL_CHECKPOINTED_STREAM_COLO)
+        shs->completion_callback = libxl__colo_restore_teardown;
+    else
+        shs->completion_callback = libxl__xc_domain_restore_done;
+    shs->caller_state = dcs;
+    shs->need_results = 1;
+
+    run_helper(egc, shs, "--restore-domain", restore_fd, send_back_fd, 0, 0,
+               argnums, ARRAY_SIZE(argnums));
+}
+
+void libxl__xc_domain_save(libxl__egc *egc, libxl__domain_save_state *dss,
+                           libxl__save_helper_state *shs)
+{
+    STATE_AO_GC(dss->ao);
+
+    unsigned cbflags =
+        libxl__srm_callout_enumcallbacks_save(&shs->callbacks.save.a);
+
+    const unsigned long argnums[] = {
+        dss->domid, dss->xcflags, cbflags,
+        dss->checkpointed_stream,
+    };
+
+    shs->ao = ao;
+    shs->domid = dss->domid;
+    shs->recv_callback = libxl__srm_callout_received_save;
+    shs->completion_callback = libxl__xc_domain_save_done;
+    shs->caller_state = dss;
+    shs->need_results = 0;
+
+    run_helper(egc, shs, "--save-domain", dss->fd, dss->recv_fd,
+               NULL, 0,
+               argnums, ARRAY_SIZE(argnums));
+    return;
+}
+
+
+void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
+                           libxl__save_helper_state *shs, int return_value)
+{
+    shs->egc = egc;
+    libxl__srm_callout_sendreply(return_value, shs);
+    shs->egc = 0;
+}
+
+void libxl__save_helper_init(libxl__save_helper_state *shs)
+{
+    libxl__ao_abortable_init(&shs->abrt);
+    libxl__ev_fd_init(&shs->readable);
+    libxl__ev_child_init(&shs->child);
+}
+
+/*----- helper execution -----*/
+
+/* This function can not fail. */
+static int dup_cloexec(libxl__gc *gc, int fd, const char *what)
+{
+    int dup_fd = fd;
+
+    if (fd <= 2) {
+        dup_fd = dup(fd);
+        if (dup_fd < 0) {
+            LOGE(ERROR,"dup %s", what);
+            exit(-1);
+        }
+    }
+    libxl_fd_set_cloexec(CTX, dup_fd, 0);
+
+    return dup_fd;
+}
+
+/*
+ * Both save and restore share four parameters:
+ * 1) Path to libxl-save-helper.
+ * 2) --[restore|save]-domain.
+ * 3) stream file descriptor.
+ * 4) back channel file descriptor.
+ * n) save/restore specific parameters.
+ * 5) A \0 at the end.
+ */
+#define HELPER_NR_ARGS 5
+static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
+                       const char *mode_arg,
+                       int stream_fd, int back_channel_fd,
+                       const int *preserve_fds, int num_preserve_fds,
+                       const unsigned long *argnums, int num_argnums)
+{
+    STATE_AO_GC(shs->ao);
+    const char *args[HELPER_NR_ARGS + num_argnums];
+    const char **arg = args;
+    int i, rc;
+
+    /* Resources we must free */
+    libxl__carefd *childs_pipes[2] = { 0,0 };
+
+    /* Convenience aliases */
+    const uint32_t domid = shs->domid;
+
+    shs->rc = 0;
+    shs->completed = 0;
+    shs->pipes[0] = shs->pipes[1] = 0;
+    libxl__save_helper_init(shs);
+
+    shs->abrt.ao = shs->ao;
+    shs->abrt.callback = helper_stop;
+    rc = libxl__ao_abortable_register(&shs->abrt);
+    if (rc) goto out;
+
+    shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
+                                " stdin pipe", domid);
+    shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
+                                 " stdout pipe", domid);
+
+    *arg++ = getenv("LIBXL_SAVE_HELPER") ?: LIBEXEC_BIN "/" "libxl-save-helper";
+    *arg++ = mode_arg;
+    const char **stream_fd_arg = arg++;
+    const char **back_channel_fd_arg = arg++;
+    for (i=0; i<num_argnums; i++)
+        *arg++ = GCSPRINTF("%lu", argnums[i]);
+    *arg++ = 0;
+    assert(arg == args + ARRAY_SIZE(args));
+
+    libxl__carefd_begin();
+    int childfd;
+    for (childfd=0; childfd<2; childfd++) {
+        /* Setting up the pipe for the child's fd childfd */
+        int fds[2];
+        if (libxl_pipe(CTX,fds)) {
+            rc = ERROR_FAIL;
+            libxl__carefd_unlock();
+            goto out;
+        }
+        int childs_end = childfd==0 ? 0 /*read*/  : 1 /*write*/;
+        int our_end    = childfd==0 ? 1 /*write*/ : 0 /*read*/;
+        childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]);
+        shs->pipes[childfd] =   libxl__carefd_record(CTX, fds[our_end]);
+    }
+    libxl__carefd_unlock();
+
+    pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited);
+    if (!pid) {
+        stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd");
+        *stream_fd_arg = GCSPRINTF("%d", stream_fd);
+
+        if (back_channel_fd >= 0)
+            back_channel_fd = dup_cloexec(gc, back_channel_fd,
+                                          "migration back channel fd");
+        *back_channel_fd_arg = GCSPRINTF("%d", back_channel_fd);
+
+        for (i=0; i<num_preserve_fds; i++)
+            if (preserve_fds[i] >= 0) {
+                assert(preserve_fds[i] > 2);
+                libxl_fd_set_cloexec(CTX, preserve_fds[i], 0);
+            }
+
+        libxl__exec(gc,
+                    libxl__carefd_fd(childs_pipes[0]),
+                    libxl__carefd_fd(childs_pipes[1]),
+                    -1,
+                    args[0], (char**)args, 0);
+    }
+
+    libxl__carefd_close(childs_pipes[0]);
+    libxl__carefd_close(childs_pipes[1]);
+
+    rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable,
+                               libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI);
+    if (rc) goto out;
+    return;
+
+ out:
+    libxl__carefd_close(childs_pipes[0]);
+    libxl__carefd_close(childs_pipes[1]);
+    helper_failed(egc, shs, rc);;
+}
+
+static void helper_failed(libxl__egc *egc, libxl__save_helper_state *shs,
+                          int rc)
+{
+    STATE_AO_GC(shs->ao);
+
+    if (!shs->rc)
+        shs->rc = rc;
+
+    libxl__ev_fd_deregister(gc, &shs->readable);
+
+    if (!libxl__save_helper_inuse(shs)) {
+        helper_done(egc, shs);
+        return;
+    }
+
+    libxl__kill(gc, shs->child.pid, SIGKILL, "save/restore helper");
+}
+
+static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
+{
+    libxl__save_helper_state *shs = CONTAINER_OF(abrt, *shs, abrt);
+    STATE_AO_GC(shs->ao);
+
+    if (!libxl__save_helper_inuse(shs)) {
+        helper_failed(egc, shs, rc);
+        return;
+    }
+
+    if (!shs->rc)
+        shs->rc = rc;
+
+    libxl__kill(gc, shs->child.pid, SIGTERM, "save/restore helper");
+}
+
+void libxl__save_helper_abort(libxl__egc *egc,
+                              libxl__save_helper_state *shs)
+{
+    helper_stop(egc, &shs->abrt, ERROR_FAIL);
+}
+
+static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
+                                   int fd, short events, short revents)
+{
+    libxl__save_helper_state *shs = CONTAINER_OF(ev, *shs, readable);
+    STATE_AO_GC(shs->ao);
+    int rc, errnoval;
+
+    if (revents & (POLLERR|POLLPRI)) {
+        LOGD(ERROR, shs->domid, "%s signaled POLLERR|POLLPRI (%#x)",
+             shs->stdout_what, revents);
+        rc = ERROR_FAIL;
+ out:
+        /* this is here because otherwise we bypass the decl of msg[] */
+        helper_failed(egc, shs, rc);
+        return;
+    }
+
+    uint16_t msglen;
+    errnoval = libxl_read_exactly(CTX, fd, &msglen, sizeof(msglen),
+                                  shs->stdout_what, "ipc msg header");
+    if (errnoval) { rc = ERROR_FAIL; goto out; }
+
+    unsigned char msg[msglen];
+    errnoval = libxl_read_exactly(CTX, fd, msg, msglen,
+                                  shs->stdout_what, "ipc msg body");
+    if (errnoval) { rc = ERROR_FAIL; goto out; }
+
+    shs->egc = egc;
+    shs->recv_callback(msg, msglen, shs);
+    shs->egc = 0;
+    return;
+}
+
+static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
+                          pid_t pid, int status)
+{
+    libxl__save_helper_state *shs = CONTAINER_OF(ch, *shs, child);
+    STATE_AO_GC(shs->ao);
+
+    /* Convenience aliases */
+    const uint32_t domid = shs->domid;
+
+    const char *what =
+        GCSPRINTF("domain %"PRIu32" save/restore helper", domid);
+
+    if (status) {
+        libxl_report_child_exitstatus(CTX, XTL_ERROR, what, pid, status);
+        if (!shs->rc)
+            shs->rc = ERROR_FAIL;
+    }
+
+    if (shs->need_results) {
+        if (!shs->rc) {
+            LOGD(ERROR,shs->domid,"%s exited without providing results",what);
+            shs->rc = ERROR_FAIL;
+        }
+    }
+
+    if (!shs->completed) {
+        if (!shs->rc) {
+            LOGD(ERROR,shs->domid,"%s exited without signaling completion",what);
+            shs->rc = ERROR_FAIL;
+        }
+    }
+
+    helper_done(egc, shs);
+    return;
+}
+
+static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs)
+{
+    STATE_AO_GC(shs->ao);
+
+    libxl__ao_abortable_deregister(&shs->abrt);
+    libxl__ev_fd_deregister(gc, &shs->readable);
+    libxl__carefd_close(shs->pipes[0]);  shs->pipes[0] = 0;
+    libxl__carefd_close(shs->pipes[1]);  shs->pipes[1] = 0;
+    assert(!libxl__save_helper_inuse(shs));
+
+    shs->egc = egc;
+    shs->completion_callback(egc, shs->caller_state,
+                             shs->rc, shs->retval, shs->errnoval);
+    shs->egc = 0;
+}
+
+/*----- generic helpers for the autogenerated code -----*/
+
+const libxl__srm_save_autogen_callbacks*
+libxl__srm_callout_get_callbacks_save(void *user)
+{
+    libxl__save_helper_state *shs = user;
+    return &shs->callbacks.save.a;
+}
+
+const libxl__srm_restore_autogen_callbacks*
+libxl__srm_callout_get_callbacks_restore(void *user)
+{
+    libxl__save_helper_state *shs = user;
+    return &shs->callbacks.restore.a;
+}
+
+void libxl__srm_callout_sendreply(int r, void *user)
+{
+    libxl__save_helper_state *shs = user;
+    libxl__egc *egc = shs->egc;
+    STATE_AO_GC(shs->ao);
+    int errnoval;
+
+    errnoval = libxl_write_exactly(CTX, libxl__carefd_fd(shs->pipes[0]),
+                                   &r, sizeof(r), shs->stdin_what,
+                                   "callback return value");
+    if (errnoval)
+        helper_failed(egc, shs, ERROR_FAIL);
+}
+
+void libxl__srm_callout_callback_log(uint32_t level, uint32_t errnoval,
+                  const char *context, const char *formatted, void *user)
+{
+    libxl__save_helper_state *shs = user;
+    STATE_AO_GC(shs->ao);
+    xtl_log(CTX->lg, level, errnoval, context, "%s", formatted);
+}
+
+void libxl__srm_callout_callback_progress(const char *context,
+                   const char *doing_what, unsigned long done,
+                   unsigned long total, void *user)
+{
+    libxl__save_helper_state *shs = user;
+    STATE_AO_GC(shs->ao);
+    xtl_progress(CTX->lg, context, doing_what, done, total);
+}
+
+int libxl__srm_callout_callback_complete(int retval, int errnoval,
+                                         void *user)
+{
+    libxl__save_helper_state *shs = user;
+    STATE_AO_GC(shs->ao);
+
+    shs->completed = 1;
+    shs->retval = retval;
+    shs->errnoval = errnoval;
+    libxl__ev_fd_deregister(gc, &shs->readable);
+    return 0;
+}
diff --git a/tools/libs/light/libxl_save_helper.c b/tools/libs/light/libxl_save_helper.c
new file mode 100644 (file)
index 0000000..65dff38
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2012      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+/*
+ * The libxl-save-helper utility speaks a protocol to its caller for
+ * the callbacks.  The protocol is as follows.
+ *
+ * The helper talks on stdin and stdout, in binary in machine
+ * endianness.  The helper speaks first, and only when it has a
+ * callback to make.  It writes a 16-bit number being the message
+ * length, and then the message body.
+ *
+ * Each message starts with a 16-bit number indicating which of the
+ * messages it is, and then some arguments in a binary marshalled form.
+ * If the callback does not need a reply (it returns void), the helper
+ * just continues.  Otherwise the helper waits for its caller to send a
+ * single int which is to be the return value from the callback.
+ *
+ * Where feasible the stubs and callbacks have prototypes identical to
+ * those required by xc_domain_save and xc_domain_restore, so that the
+ * autogenerated functions can be used/provided directly.
+ *
+ * The actual messages are in the array @msgs in libxl_save_msgs_gen.pl
+ */
+
+#include "libxl_osdeps.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "libxl.h"
+#include "libxl_utils.h"
+
+#include "xenctrl.h"
+#include "xenguest.h"
+#include "_libxl_save_msgs_helper.h"
+
+/*----- logger -----*/
+
+__attribute__((format(printf, 5, 0)))
+static void tellparent_vmessage(xentoollog_logger *logger_in,
+                                xentoollog_level level,
+                                int errnoval,
+                                const char *context,
+                                const char *format,
+                                va_list al)
+{
+    char *formatted;
+    int r = vasprintf(&formatted, format, al);
+    if (r < 0) { perror("memory allocation failed during logging"); exit(-1); }
+    helper_stub_log(level, errnoval, context, formatted, 0);
+    free(formatted);
+}
+
+static void tellparent_progress(struct xentoollog_logger *logger_in,
+                                const char *context,
+                                const char *doing_what, int percent,
+                                unsigned long done, unsigned long total)
+{
+    helper_stub_progress(context, doing_what, done, total, 0);
+}
+
+static void tellparent_destroy(struct xentoollog_logger *logger_in)
+{
+    abort();
+}
+
+/*----- globals -----*/
+
+static const char *program = "libxl-save-helper";
+static xentoollog_logger logger = {
+    tellparent_vmessage,
+    tellparent_progress,
+    tellparent_destroy,
+};
+static xc_interface *xch;
+static int io_fd;
+
+/*----- error handling -----*/
+
+static void fail(int errnoval, const char *fmt, ...)
+    __attribute__((noreturn,format(printf,2,3)));
+static void fail(int errnoval, const char *fmt, ...)
+{
+    va_list al;
+    va_start(al,fmt);
+    xtl_logv(&logger,XTL_ERROR,errnoval,program,fmt,al);
+    exit(-1);
+}
+
+static int read_exactly(int fd, void *buf, size_t len)
+/* returns 0 if we get eof, even if we got it midway through; 1 if ok */
+{
+    while (len) {
+        ssize_t r = read(fd, buf, len);
+        if (r<=0) return r;
+        assert(r <= len);
+        len -= r;
+        buf = (char*)buf + r;
+    }
+    return 1;
+}
+
+static void *xmalloc(size_t sz)
+{
+    if (!sz) return 0;
+    void *r = malloc(sz);
+    if (!r) { perror("memory allocation failed"); exit(-1); }
+    return r;
+}
+
+/*----- signal handling -----*/
+
+static int unwriteable_fd;
+
+static void save_signal_handler(int num)
+{
+    /*
+     * We want to be able to interrupt save.  But the code in libxc
+     * which does the actual saving is straight-through, and we need
+     * to execute its error path to put the guest back to sanity.
+     *
+     * So what we do is this: when we get the signal, we dup2
+     * the result of open("/dev/null",O_RDONLY) onto the output fd.
+     *
+     * This is guaranteed to 1. interrupt libxc's write (causing it to
+     * return short, or maybe EINTR); 2. make the next write give
+     * EBADF, so that: 3. at latest, libxc will notice when it next
+     * tries to write data and will then go into its cleanup path.
+     *
+     * We make no effort here to sanitise the resulting errors.
+     * That's libxl's job.
+     */
+    int esave = errno;
+
+    int r = dup2(unwriteable_fd, io_fd);
+    if (r != io_fd)
+        /* we can't write an xtl message because we might end up
+         * interleaving on our control stream; we can't use stdio
+         * because it's not async-signal-safe */
+        abort();
+
+    errno = esave;
+}
+
+static void setup_signals(void (*handler)(int))
+{
+    struct sigaction sa;
+    sigset_t spmask;
+    int r;
+
+    unwriteable_fd = open("/dev/null",O_RDONLY);
+    if (unwriteable_fd < 0) fail(errno,"open /dev/null for reading");
+
+    LIBXL_FILLZERO(sa);
+    sa.sa_handler = handler;
+    sigemptyset(&sa.sa_mask);
+    r = sigaction(SIGTERM, &sa, 0);
+    if (r) fail(errno,"sigaction SIGTERM failed");
+
+    sigemptyset(&spmask);
+    sigaddset(&spmask,SIGTERM);
+    r = sigprocmask(SIG_UNBLOCK,&spmask,0);
+    if (r) fail(errno,"sigprocmask unblock SIGTERM failed");
+}
+
+/*----- helper functions called by autogenerated stubs -----*/
+
+unsigned char * helper_allocbuf(int len, void *user)
+{
+    return xmalloc(len);
+}
+
+static void transmit(const unsigned char *msg, int len, void *user)
+{
+    while (len) {
+        int r = write(1, msg, len);
+        if (r<0) { perror("write"); exit(-1); }
+        assert(r >= 0);
+        assert(r <= len);
+        len -= r;
+        msg += r;
+    }
+}
+
+void helper_transmitmsg(unsigned char *msg_freed, int len_in, void *user)
+{
+    assert(len_in < 64*1024);
+    uint16_t len = len_in;
+    transmit((const void*)&len, sizeof(len), user);
+    transmit(msg_freed, len, user);
+    free(msg_freed);
+}
+
+int helper_getreply(void *user)
+{
+    int v;
+    int r = read_exactly(0, &v, sizeof(v));
+    if (r<=0) exit(-2);
+    return v;
+}
+
+/*----- other callbacks -----*/
+
+static void startup(const char *op) {
+    xtl_log(&logger,XTL_DEBUG,0,program,"starting %s",op);
+
+    xch = xc_interface_open(&logger,&logger,0);
+    if (!xch) fail(errno,"xc_interface_open failed");
+}
+
+static void complete(int retval) {
+    int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */
+    xtl_log(&logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval);
+    helper_stub_complete(retval,errnoval,0);
+    xc_interface_close(xch);
+    exit(0);
+}
+
+int main(int argc, char **argv)
+{
+    int r;
+    int send_back_fd, recv_fd;
+
+#define NEXTARG (++argv, assert(*argv), *argv)
+
+    const char *mode = *++argv;
+    assert(mode);
+
+    if (!strcmp(mode,"--save-domain")) {
+        static struct save_callbacks cb;
+
+        io_fd =                             atoi(NEXTARG);
+        recv_fd =                           atoi(NEXTARG);
+        uint32_t dom =                      strtoul(NEXTARG,0,10);
+        uint32_t flags =                    strtoul(NEXTARG,0,10);
+        unsigned cbflags =                  strtoul(NEXTARG,0,10);
+        xc_stream_type_t stream_type =      strtoul(NEXTARG,0,10);
+        assert(!*++argv);
+
+        helper_setcallbacks_save(&cb, cbflags);
+
+        startup("save");
+        setup_signals(save_signal_handler);
+
+        r = xc_domain_save(xch, io_fd, dom, flags, &cb, stream_type, recv_fd);
+        complete(r);
+
+    } else if (!strcmp(mode,"--restore-domain")) {
+        static struct restore_callbacks cb;
+
+        io_fd =                             atoi(NEXTARG);
+        send_back_fd =                      atoi(NEXTARG);
+        uint32_t dom =                      strtoul(NEXTARG,0,10);
+        unsigned store_evtchn =             strtoul(NEXTARG,0,10);
+        domid_t store_domid =               strtoul(NEXTARG,0,10);
+        unsigned console_evtchn =           strtoul(NEXTARG,0,10);
+        domid_t console_domid =             strtoul(NEXTARG,0,10);
+        unsigned cbflags =                  strtoul(NEXTARG,0,10);
+        xc_stream_type_t stream_type =      strtoul(NEXTARG,0,10);
+        assert(!*++argv);
+
+        helper_setcallbacks_restore(&cb, cbflags);
+
+        unsigned long store_mfn = 0;
+        unsigned long console_mfn = 0;
+
+        startup("restore");
+        setup_signals(SIG_DFL);
+
+        r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn,
+                              store_domid, console_evtchn, &console_mfn,
+                              console_domid, stream_type, &cb, send_back_fd);
+        helper_stub_restore_results(store_mfn,console_mfn,0);
+        complete(r);
+
+    } else {
+        assert(!"unexpected mode argument");
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_save_msgs_gen.pl b/tools/libs/light/libxl_save_msgs_gen.pl
new file mode 100755 (executable)
index 0000000..5bfbd4f
--- /dev/null
@@ -0,0 +1,396 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+use POSIX;
+
+our $debug = 0; # produce copious debugging output at run-time?
+
+our @msgs = (
+    # flags:
+    #   s  - applicable to save
+    #   r  - applicable to restore
+    #   c  - function pointer in callbacks struct rather than fixed function
+    #   x  - function pointer is in struct {save,restore}_callbacks
+    #         and its null-ness needs to be passed through to the helper's xc
+    #   W  - needs a return value; callback is synchronous
+    #   A  - needs a return value; callback is asynchronous
+    [ 'sr',     "log",                   [qw(uint32_t level
+                                             uint32_t errnoval
+                                             STRING context
+                                             STRING formatted)] ],
+    [ 'sr',     "progress",              [qw(STRING context
+                                             STRING doing_what),
+                                            'unsigned long', 'done',
+                                            'unsigned long', 'total'] ],
+    [ 'srcxA',  "suspend", [] ],
+    [ 'srcxA',  "postcopy", [] ],
+    [ 'srcxA',  "checkpoint", [] ],
+    [ 'srcxA',  "wait_checkpoint", [] ],
+    [ 'scxA',   "switch_qemu_logdirty",  [qw(uint32_t domid
+                                          unsigned enable)] ],
+    [ 'rcxW',   "static_data_done",      [qw(unsigned missing)] ],
+    [ 'rcx',    "restore_results",       ['xen_pfn_t', 'store_gfn',
+                                          'xen_pfn_t', 'console_gfn'] ],
+    [ 'srW',    "complete",              [qw(int retval
+                                             int errnoval)] ],
+);
+
+#----------------------------------------
+
+our %cbs;
+our %func;
+our %func_ah;
+our @outfuncs;
+our %out_decls;
+our %out_body;
+our $msgnum = 0;
+
+die unless @ARGV==1;
+die if $ARGV[0] =~ m/^-/;
+
+our ($intendedout) = @ARGV;
+
+$intendedout =~ m/([a-z]+)\.([ch])$/ or die;
+my ($want_ah, $ch) = ($1, $2);
+
+my $declprefix = '';
+
+foreach my $ah (qw(callout helper)) {
+    $out_body{$ah} .=
+        <<END_BOTH.($ah eq 'callout' ? <<END_CALLOUT : <<END_HELPER);
+#include "libxl_osdeps.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+END_BOTH
+
+#include "libxl_internal.h"
+
+END_CALLOUT
+
+#include <xenctrl.h>
+#include <xenguest.h>
+#include "_libxl_save_msgs_${ah}.h"
+
+END_HELPER
+}
+
+die $want_ah unless defined $out_body{$want_ah};
+
+sub f_decl ($$$$) {
+    my ($name, $ah, $c_rtype, $c_decl) = @_;
+    $out_decls{$name} = "${declprefix}$c_rtype $name$c_decl;\n";
+    $func{$name} = "$c_rtype $name$c_decl\n{\n" . ($func{$name} || '');
+    $func_ah{$name} = $ah;
+}
+
+sub f_more ($$) {
+    my ($name, $addbody) = @_;
+    $func{$name} ||= '';
+    $func{$name} .= $addbody;
+    push @outfuncs, $name;
+}
+
+our $libxl = "libxl__srm";
+our $callback = "${libxl}_callout_callback";
+our $receiveds = "${libxl}_callout_received";
+our $sendreply = "${libxl}_callout_sendreply";
+our $getcallbacks = "${libxl}_callout_get_callbacks";
+our $enumcallbacks = "${libxl}_callout_enumcallbacks";
+sub cbtype ($) { "${libxl}_".$_[0]."_autogen_callbacks"; };
+
+f_decl($sendreply, 'callout', 'void', "(int r, void *user)");
+
+our $helper = "helper";
+our $encode = "${helper}_stub";
+our $allocbuf = "${helper}_allocbuf";
+our $transmit = "${helper}_transmitmsg";
+our $getreply = "${helper}_getreply";
+our $setcallbacks = "${helper}_setcallbacks";
+
+f_decl($allocbuf, 'helper', 'unsigned char *', '(int len, void *user)');
+f_decl($transmit, 'helper', 'void',
+       '(unsigned char *msg_freed, int len, void *user)');
+f_decl($getreply, 'helper', 'int', '(void *user)');
+
+sub typeid ($) { my ($t) = @_; $t =~ s/\W/_/; return $t; };
+
+$out_body{'callout'} .= <<END;
+static int bytes_get(const unsigned char **msg,
+                    const unsigned char *const endmsg,
+                    void *result, int rlen)
+{
+    if (endmsg - *msg < rlen) return 0;
+    memcpy(result, *msg, rlen);
+    *msg += rlen;
+    return 1;
+}
+
+END
+$out_body{'helper'} .= <<END;
+static void bytes_put(unsigned char *const buf, int *len,
+                     const void *value, int vlen)
+{
+    assert(vlen < INT_MAX/2 - *len);
+    if (buf)
+       memcpy(buf + *len, value, vlen);
+    *len += vlen;
+}
+
+END
+
+foreach my $simpletype (qw(int uint16_t uint32_t unsigned), 'unsigned long', 'xen_pfn_t') {
+    my $typeid = typeid($simpletype);
+    $out_body{'callout'} .= <<END;
+static int ${typeid}_get(const unsigned char **msg,
+                        const unsigned char *const endmsg,
+                        $simpletype *result)
+{
+    return bytes_get(msg, endmsg, result, sizeof(*result));
+}
+
+END
+    $out_body{'helper'} .= <<END;
+static void ${typeid}_put(unsigned char *const buf, int *len,
+                        const $simpletype value)
+{
+    bytes_put(buf, len, &value, sizeof(value));
+}
+
+END
+}
+
+$out_body{'callout'} .= <<END;
+static int BLOCK_get(const unsigned char **msg,
+                      const unsigned char *const endmsg,
+                      const uint8_t **result, uint32_t *result_size)
+{
+    if (!uint32_t_get(msg, endmsg, result_size)) return 0;
+    if (endmsg - *msg < *result_size) return 0;
+    *result = (const void*)*msg;
+    *msg += *result_size;
+    return 1;
+}
+
+static int STRING_get(const unsigned char **msg,
+                      const unsigned char *const endmsg,
+                      const char **result)
+{
+    const uint8_t *data;
+    uint32_t datalen;
+    if (!BLOCK_get(msg, endmsg, &data, &datalen)) return 0;
+    if (datalen == 0) return 0;
+    if (data[datalen-1] != '\\0') return 0;
+    *result = (const void*)data;
+    return 1;
+}
+
+END
+$out_body{'helper'} .= <<END;
+static void BLOCK_put(unsigned char *const buf,
+                      int *len,
+                     const uint8_t *bytes, uint32_t size)
+{
+    uint32_t_put(buf, len, size);
+    bytes_put(buf, len, bytes, size);
+}
+
+static void STRING_put(unsigned char *const buf,
+                      int *len,
+                      const char *string)
+{
+    size_t slen = strlen(string);
+    assert(slen < INT_MAX / 4);
+    assert(slen < (uint32_t)0x40000000);
+    BLOCK_put(buf, len, (const void*)string, slen+1);
+}
+
+END
+
+foreach my $sr (qw(save restore)) {
+    f_decl("${getcallbacks}_${sr}", 'callout',
+           "const ".cbtype($sr)." *",
+           "(void *data)");
+
+    f_decl("${receiveds}_${sr}", 'callout', 'int',
+          "(const unsigned char *msg, uint32_t len, void *user)");
+
+    f_decl("${enumcallbacks}_${sr}", 'callout', 'unsigned',
+           "(const ".cbtype($sr)." *cbs)");
+    f_more("${enumcallbacks}_${sr}", "    unsigned cbflags = 0;\n");
+
+    f_decl("${setcallbacks}_${sr}", 'helper', 'void',
+           "(struct ${sr}_callbacks *cbs, unsigned cbflags)");
+
+    f_more("${receiveds}_${sr}",
+           <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
+    const unsigned char *const endmsg = msg + len;
+    uint16_t mtype;
+    if (!uint16_t_get(&msg, endmsg, &mtype)) return 0;
+END_ALWAYS
+    fprintf(stderr,"libxl callout receiver: got len=%u mtype=%u\\n",len,mtype);
+END_DEBUG
+    switch (mtype) {
+
+END_ALWAYS
+
+    $cbs{$sr} = "typedef struct ".cbtype($sr)." {\n";
+}
+
+foreach my $msginfo (@msgs) {
+    my ($flags, $name, $args) = @$msginfo;
+    $msgnum++;
+
+    my $f_more_sr = sub {
+        my ($contents_spec, $fnamebase) = @_;
+        $fnamebase ||= "${receiveds}";
+        foreach my $sr (qw(save restore)) {
+            $sr =~ m/^./;
+            next unless $flags =~ m/$&/;
+            my $contents = (!ref $contents_spec) ? $contents_spec :
+                $contents_spec->($sr);
+            f_more("${fnamebase}_${sr}", $contents);
+        }
+    };
+
+    $f_more_sr->("    case $msgnum: { /* $name */\n");
+    if ($flags =~ m/W/) {
+        $f_more_sr->("        int r;\n");
+    }
+
+    my $c_rtype_helper = $flags =~ m/[WA]/ ? 'int' : 'void';
+    my $c_rtype_callout = $flags =~ m/W/ ? 'int' : 'void';
+    my $c_decl = '(';
+    my $c_callback_args = '';
+
+    f_more("${encode}_$name",
+           <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
+    unsigned char *buf = 0;
+    int len = 0, allocd = 0;
+
+END_ALWAYS
+    fprintf(stderr,"libxl-save-helper: encoding $name\\n");
+END_DEBUG
+    for (;;) {
+        uint16_t_put(buf, &len, $msgnum /* $name */);
+END_ALWAYS
+
+    my @args = @$args;
+    my $c_recv = '';
+    my ($argtype, $arg);
+    while (($argtype, $arg, @args) = @args) {
+       my $typeid = typeid($argtype);
+        my $c_args = "$arg";
+        my $c_get_args = "&$arg";
+       if ($argtype eq 'STRING') {
+           $c_decl .= "const char *$arg, ";
+           $f_more_sr->("        const char *$arg;\n");
+        } elsif ($argtype eq 'BLOCK') {
+            $c_decl .= "const uint8_t *$arg, uint32_t ${arg}_size, ";
+            $c_args .= ", ${arg}_size";
+            $c_get_args .= ", &${arg}_size";
+           $f_more_sr->("        const uint8_t *$arg;\n".
+                         "        uint32_t ${arg}_size;\n");
+       } else {
+           $c_decl .= "$argtype $arg, ";
+           $f_more_sr->("        $argtype $arg;\n");
+       }
+       $c_callback_args .= "$c_args, ";
+       $c_recv.=
+            "        if (!${typeid}_get(&msg, endmsg, $c_get_args)) return 0;\n";
+        f_more("${encode}_$name", "    ${typeid}_put(buf, &len, $c_args);\n");
+    }
+    $f_more_sr->($c_recv);
+    $c_decl .= "void *user)";
+    $c_callback_args .= "user";
+
+    $f_more_sr->("        if (msg != endmsg) return 0;\n");
+
+    my $c_callback;
+    if ($flags !~ m/c/) {
+        $c_callback = "${callback}_$name";
+    } else {
+        $f_more_sr->(sub {
+            my ($sr) = @_;
+            $cbs{$sr} .= "    $c_rtype_callout (*${name})$c_decl;\n";
+            return
+          "        const ".cbtype($sr)." *const cbs =\n".
+            "            ${getcallbacks}_${sr}(user);\n";
+                       });
+        $c_callback = "cbs->${name}";
+    }
+    my $c_make_callback = "$c_callback($c_callback_args)";
+    if ($flags !~ m/W/) {
+       $f_more_sr->("        $c_make_callback;\n");
+    } else {
+        $f_more_sr->("        r = $c_make_callback;\n".
+                     "        $sendreply(r, user);\n");
+       f_decl($sendreply, 'callout', 'void', '(int r, void *user)');
+    }
+    if ($flags =~ m/x/) {
+        my $c_v = "(1u<<$msgnum)";
+        my $c_cb = "cbs->$name";
+        $f_more_sr->("    if ($c_cb) cbflags |= $c_v;\n", $enumcallbacks);
+        $f_more_sr->("    if (cbflags & $c_v) $c_cb = ${encode}_${name};\n",
+                     $setcallbacks);
+    }
+    $f_more_sr->("        return 1;\n    }\n\n");
+    f_decl("${callback}_$name", 'callout', $c_rtype_callout, $c_decl);
+    f_decl("${encode}_$name", 'helper', $c_rtype_helper, $c_decl);
+    f_more("${encode}_$name",
+"        if (buf) break;
+        buf = ${helper}_allocbuf(len, user);
+        assert(buf);
+        allocd = len;
+        len = 0;
+    }
+    assert(len == allocd);
+    ${transmit}(buf, len, user);
+");
+    if ($flags =~ m/[WA]/) {
+       f_more("${encode}_$name",
+               (<<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS));
+    int r = ${helper}_getreply(user);
+END_ALWAYS
+    fprintf(stderr,"libxl-save-helper: $name got reply %d\\n",r);
+END_DEBUG
+    return r;
+END_ALWAYS
+    }
+}
+
+print "/* AUTOGENERATED by $0 DO NOT EDIT */\n\n" or die $!;
+
+foreach my $sr (qw(save restore)) {
+    f_more("${enumcallbacks}_${sr}",
+           "    return cbflags;\n");
+    f_more("${receiveds}_${sr}",
+           "    default:\n".
+           "        return 0;\n".
+           "    }\n");
+    $cbs{$sr} .= "} ".cbtype($sr).";\n\n";
+    if ($ch eq 'h') {
+        print $cbs{$sr} or die $!;
+        print "struct ${sr}_callbacks;\n";
+    }
+}
+
+if ($ch eq 'c') {
+    foreach my $name (@outfuncs) {
+        next unless defined $func{$name};
+        $func{$name} .= "}\n\n";
+        $out_body{$func_ah{$name}} .= $func{$name};
+        delete $func{$name};
+    }
+    print $out_body{$want_ah} or die $!;
+} else {
+    foreach my $name (sort keys %out_decls) {
+        next unless $func_ah{$name} eq $want_ah;
+        print $out_decls{$name} or die $!;
+    }
+}
+
+close STDOUT or die $!;
diff --git a/tools/libs/light/libxl_sched.c b/tools/libs/light/libxl_sched.c
new file mode 100644 (file)
index 0000000..7c53dc6
--- /dev/null
@@ -0,0 +1,986 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid,
+                                   uint32_t vcpuid,
+                                   const libxl_bitmap *cpumap_hard,
+                                   const libxl_bitmap *cpumap_soft,
+                                   unsigned flags)
+{
+    GC_INIT(ctx);
+    libxl_bitmap hard, soft;
+    int rc;
+
+    libxl_bitmap_init(&hard);
+    libxl_bitmap_init(&soft);
+
+    if (!cpumap_hard && !cpumap_soft && !flags) {
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    /*
+     * Xen wants writable hard and/or soft cpumaps, to put back in them
+     * the effective hard and/or soft affinity that will be used.
+     */
+    if (cpumap_hard) {
+        rc = libxl_cpu_bitmap_alloc(ctx, &hard, 0);
+        if (rc)
+            goto out;
+
+        libxl__bitmap_copy_best_effort(gc, &hard, cpumap_hard);
+        flags |= XEN_VCPUAFFINITY_HARD;
+    }
+    if (cpumap_soft) {
+        rc = libxl_cpu_bitmap_alloc(ctx, &soft, 0);
+        if (rc)
+            goto out;
+
+        libxl__bitmap_copy_best_effort(gc, &soft, cpumap_soft);
+        flags |= XEN_VCPUAFFINITY_SOFT;
+    }
+
+    if (xc_vcpu_setaffinity(ctx->xch, domid, vcpuid,
+                            cpumap_hard ? hard.map : NULL,
+                            cpumap_soft ? soft.map : NULL,
+                            flags)) {
+        LOGED(ERROR, domid, "Setting vcpu affinity");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /*
+     * Let's check the results. Hard affinity will never be empty, but it
+     * is possible that Xen will use something different from what we asked
+     * for various reasons. If that's the case, report it.
+     */
+    if (cpumap_hard &&
+        !libxl_bitmap_equal(cpumap_hard, &hard, 0))
+        LOGD(DEBUG, domid, "New hard affinity for vcpu %d has unreachable cpus", vcpuid);
+    /*
+     * Soft affinity can both be different from what asked and empty. Check
+     * for (and report) both.
+     */
+    if (cpumap_soft) {
+        if (!libxl_bitmap_equal(cpumap_soft, &soft, 0))
+            LOGD(DEBUG, domid, "New soft affinity for vcpu %d has unreachable cpus",
+                 vcpuid);
+        if (libxl_bitmap_is_empty(&soft))
+            LOGD(WARN, domid, "All cpus in soft affinity of vcpu %d are unreachable."
+                 " Only hard affinity will be considered for scheduling",
+                 vcpuid);
+    }
+
+    rc = 0;
+ out:
+    libxl_bitmap_dispose(&hard);
+    libxl_bitmap_dispose(&soft);
+    GC_FREE;
+    return rc;
+}
+
+int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
+                           const libxl_bitmap *cpumap_hard,
+                           const libxl_bitmap *cpumap_soft)
+{
+    return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
+                                   cpumap_soft, 0);
+}
+
+int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
+                                 uint32_t vcpuid,
+                                 const libxl_bitmap *cpumap_hard,
+                                 const libxl_bitmap *cpumap_soft)
+{
+    return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
+                                   cpumap_soft, XEN_VCPUAFFINITY_FORCE);
+}
+
+int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
+                               unsigned int max_vcpus,
+                               const libxl_bitmap *cpumap_hard,
+                               const libxl_bitmap *cpumap_soft)
+{
+    GC_INIT(ctx);
+    int i, rc = 0;
+
+    for (i = 0; i < max_vcpus; i++) {
+        if (libxl_set_vcpuaffinity(ctx, domid, i, cpumap_hard, cpumap_soft)) {
+            LOGD(WARN, domid, "Failed to set affinity for %d", i);
+            rc = ERROR_FAIL;
+        }
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_bitmap *nodemap)
+{
+    GC_INIT(ctx);
+    if (xc_domain_node_setaffinity(ctx->xch, domid, nodemap->map)) {
+        LOGED(ERROR, domid, "Setting node affinity");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    GC_FREE;
+    return 0;
+}
+
+int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_bitmap *nodemap)
+{
+    GC_INIT(ctx);
+    if (xc_domain_node_getaffinity(ctx->xch, domid, nodemap->map)) {
+        LOGED(ERROR, domid, "Getting node affinity");
+        GC_FREE;
+        return ERROR_FAIL;
+    }
+    GC_FREE;
+    return 0;
+}
+
+int libxl_get_scheduler(libxl_ctx *ctx)
+{
+    int r, sched;
+
+    GC_INIT(ctx);
+    r = xc_sched_id(ctx->xch, &sched);
+    if (r != 0) {
+        LOGE(ERROR, "getting current scheduler id");
+        sched = ERROR_FAIL;
+    }
+    GC_FREE;
+    return sched;
+}
+
+static int sched_arinc653_domain_set(libxl__gc *gc, uint32_t domid,
+                                     const libxl_domain_sched_params *scinfo)
+{
+    /* Currently, the ARINC 653 scheduler does not take any domain-specific
+         configuration, so we simply return success. */
+    return 0;
+}
+
+static int sched_null_domain_set(libxl__gc *gc, uint32_t domid,
+                                 const libxl_domain_sched_params *scinfo)
+{
+    /* There aren't any domain-specific parameters to be set. */
+    return 0;
+}
+
+static int sched_null_domain_get(libxl__gc *gc, uint32_t domid,
+                                 libxl_domain_sched_params *scinfo)
+{
+    /* There aren't any domain-specific parameters to return. */
+    return 0;
+}
+
+static int sched_credit_domain_get(libxl__gc *gc, uint32_t domid,
+                                   libxl_domain_sched_params *scinfo)
+{
+    struct xen_domctl_sched_credit sdom;
+    int rc;
+
+    rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
+    if (rc != 0) {
+        LOGED(ERROR, domid, "Getting domain sched credit");
+        return ERROR_FAIL;
+    }
+
+    libxl_domain_sched_params_init(scinfo);
+    scinfo->sched = LIBXL_SCHEDULER_CREDIT;
+    scinfo->weight = sdom.weight;
+    scinfo->cap = sdom.cap;
+
+    return 0;
+}
+
+static int sched_credit_domain_set(libxl__gc *gc, uint32_t domid,
+                                   const libxl_domain_sched_params *scinfo)
+{
+    struct xen_domctl_sched_credit sdom;
+    xc_domaininfo_t domaininfo;
+    int rc;
+
+    rc = xc_domain_getinfolist(CTX->xch, domid, 1, &domaininfo);
+    if (rc < 0) {
+        LOGED(ERROR, domid, "Getting domain info list");
+        return ERROR_FAIL;
+    }
+    if (rc != 1 || domaininfo.domain != domid)
+        return ERROR_INVAL;
+
+    rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
+    if (rc != 0) {
+        LOGED(ERROR, domid, "Getting domain sched credit");
+        return ERROR_FAIL;
+    }
+
+    if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
+        if (scinfo->weight < 1 || scinfo->weight > 65535) {
+            LOGD(ERROR, domid, "Cpu weight out of range, "
+                 "valid values are within range from 1 to 65535");
+            return ERROR_INVAL;
+        }
+        sdom.weight = scinfo->weight;
+    }
+
+    if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
+        if (scinfo->cap < 0
+            || scinfo->cap > (domaininfo.max_vcpu_id + 1) * 100) {
+            LOGD(ERROR, domid, "Cpu cap out of range, "
+                 "valid range is from 0 to %d for specified number of vcpus",
+                 ((domaininfo.max_vcpu_id + 1) * 100));
+            return ERROR_INVAL;
+        }
+        sdom.cap = scinfo->cap;
+    }
+
+    rc = xc_sched_credit_domain_set(CTX->xch, domid, &sdom);
+    if ( rc < 0 ) {
+        LOGED(ERROR, domid, "Setting domain sched credit");
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+static int sched_ratelimit_check(libxl__gc *gc, int ratelimit)
+{
+    if (ratelimit != 0 &&
+        (ratelimit <  XEN_SYSCTL_SCHED_RATELIMIT_MIN ||
+         ratelimit > XEN_SYSCTL_SCHED_RATELIMIT_MAX)) {
+        LOG(ERROR, "Ratelimit out of range, valid range is from %d to %d",
+            XEN_SYSCTL_SCHED_RATELIMIT_MIN, XEN_SYSCTL_SCHED_RATELIMIT_MAX);
+        return ERROR_INVAL;
+    }
+
+    return 0;
+}
+
+int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
+                                  libxl_sched_credit_params *scinfo)
+{
+    struct xen_sysctl_credit_schedule sparam;
+    int r, rc;
+    GC_INIT(ctx);
+
+    r = xc_sched_credit_params_get(ctx->xch, poolid, &sparam);
+    if (r < 0) {
+        LOGE(ERROR, "getting Credit scheduler parameters");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    scinfo->tslice_ms = sparam.tslice_ms;
+    scinfo->ratelimit_us = sparam.ratelimit_us;
+    scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
+
+    rc = 0;
+ out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
+                                  libxl_sched_credit_params *scinfo)
+{
+    struct xen_sysctl_credit_schedule sparam;
+    int r, rc;
+    GC_INIT(ctx);
+
+    if (scinfo->tslice_ms <  XEN_SYSCTL_CSCHED_TSLICE_MIN
+        || scinfo->tslice_ms > XEN_SYSCTL_CSCHED_TSLICE_MAX) {
+        LOG(ERROR, "Time slice out of range, valid range is from %d to %d",
+            XEN_SYSCTL_CSCHED_TSLICE_MIN, XEN_SYSCTL_CSCHED_TSLICE_MAX);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
+    if (rc) {
+        goto out;
+    }
+    if (scinfo->ratelimit_us > scinfo->tslice_ms*1000) {
+        LOG(ERROR, "Ratelimit cannot be greater than timeslice");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    if (scinfo->vcpu_migr_delay_us > XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US) {
+        LOG(ERROR, "vcpu migration delay should be >= 0 and <= %dus",
+            XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    sparam.tslice_ms = scinfo->tslice_ms;
+    sparam.ratelimit_us = scinfo->ratelimit_us;
+    sparam.vcpu_migr_delay_us = scinfo->vcpu_migr_delay_us;
+
+    r = xc_sched_credit_params_set(ctx->xch, poolid, &sparam);
+    if ( r < 0 ) {
+        LOGE(ERROR, "Setting Credit scheduler parameters");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    scinfo->tslice_ms = sparam.tslice_ms;
+    scinfo->ratelimit_us = sparam.ratelimit_us;
+    scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
+
+    rc = 0;
+ out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
+                                   libxl_sched_credit2_params *scinfo)
+{
+    struct xen_sysctl_credit2_schedule sparam;
+    int r, rc;
+    GC_INIT(ctx);
+
+    r = xc_sched_credit2_params_get(ctx->xch, poolid, &sparam);
+    if (r < 0) {
+        LOGE(ERROR, "getting Credit2 scheduler parameters");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    scinfo->ratelimit_us = sparam.ratelimit_us;
+
+    rc = 0;
+ out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
+                                   libxl_sched_credit2_params *scinfo)
+{
+    struct xen_sysctl_credit2_schedule sparam;
+    int r, rc;
+    GC_INIT(ctx);
+
+    rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
+    if (rc) goto out;
+
+    sparam.ratelimit_us = scinfo->ratelimit_us;
+
+    r = xc_sched_credit2_params_set(ctx->xch, poolid, &sparam);
+    if (r < 0) {
+        LOGE(ERROR, "Setting Credit2 scheduler parameters");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    scinfo->ratelimit_us = sparam.ratelimit_us;
+
+    rc = 0;
+ out:
+    GC_FREE;
+    return rc;
+}
+
+static int sched_credit2_domain_get(libxl__gc *gc, uint32_t domid,
+                                    libxl_domain_sched_params *scinfo)
+{
+    struct xen_domctl_sched_credit2 sdom;
+    int rc;
+
+    rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
+    if (rc != 0) {
+        LOGED(ERROR, domid, "Getting domain sched credit2");
+        return ERROR_FAIL;
+    }
+
+    libxl_domain_sched_params_init(scinfo);
+    scinfo->sched = LIBXL_SCHEDULER_CREDIT2;
+    scinfo->weight = sdom.weight;
+    scinfo->cap = sdom.cap;
+
+    return 0;
+}
+
+static int sched_credit2_domain_set(libxl__gc *gc, uint32_t domid,
+                                    const libxl_domain_sched_params *scinfo)
+{
+    struct xen_domctl_sched_credit2 sdom;
+    xc_domaininfo_t info;
+    int rc;
+
+    rc = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
+    if (rc < 0) {
+        LOGED(ERROR, domid, "Getting domain info");
+        return ERROR_FAIL;
+    }
+    if (rc != 1 || info.domain != domid)
+        return ERROR_INVAL;
+
+    rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
+    if (rc != 0) {
+        LOGED(ERROR, domid, "Getting domain sched credit2");
+        return ERROR_FAIL;
+    }
+
+    if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
+        if (scinfo->weight < 1 || scinfo->weight > 65535) {
+            LOGD(ERROR, domid, "Cpu weight out of range, "
+                        "valid values are within range from 1 to 65535");
+            return ERROR_INVAL;
+        }
+        sdom.weight = scinfo->weight;
+    }
+
+    if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
+        if (scinfo->cap < 0
+            || scinfo->cap > (info.max_vcpu_id + 1) * 100) {
+            LOGD(ERROR, domid, "Cpu cap out of range, "
+                 "valid range is from 0 to %d for specified number of vcpus",
+                 ((info.max_vcpu_id + 1) * 100));
+            return ERROR_INVAL;
+        }
+        sdom.cap = scinfo->cap;
+    }
+
+    rc = xc_sched_credit2_domain_set(CTX->xch, domid, &sdom);
+    if ( rc < 0 ) {
+        LOGED(ERROR, domid, "Setting domain sched credit2");
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+static int sched_rtds_validate_params(libxl__gc *gc, int period, int budget)
+{
+    int rc;
+
+    if (period < 1) {
+        LOG(ERROR, "Invalid VCPU period of %d (it should be >= 1)", period);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    if (budget < 1) {
+        LOG(ERROR, "Invalid VCPU budget of %d (it should be >= 1)", budget);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    if (budget > period) {
+        LOG(ERROR, "VCPU budget must be smaller than or equal to period, "
+                   "but %d > %d", budget, period);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    rc = 0;
+out:
+    return rc;
+}
+
+/* Get the RTDS scheduling parameters of vcpu(s) */
+static int sched_rtds_vcpu_get(libxl__gc *gc, uint32_t domid,
+                               libxl_vcpu_sched_params *scinfo)
+{
+    uint32_t num_vcpus;
+    int i, r, rc;
+    xc_dominfo_t info;
+    struct xen_domctl_schedparam_vcpu *vcpus;
+
+    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+    if (r < 0) {
+        LOGED(ERROR, domid, "Getting domain info");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (scinfo->num_vcpus <= 0) {
+        rc = ERROR_INVAL;
+        goto out;
+    } else {
+        num_vcpus = scinfo->num_vcpus;
+        GCNEW_ARRAY(vcpus, num_vcpus);
+        for (i = 0; i < num_vcpus; i++) {
+            if (scinfo->vcpus[i].vcpuid < 0 ||
+                scinfo->vcpus[i].vcpuid > info.max_vcpu_id) {
+                LOGD(ERROR, domid, "VCPU index is out of range, "
+                            "valid values are within range from 0 to %d",
+                            info.max_vcpu_id);
+                rc = ERROR_INVAL;
+                goto out;
+            }
+            vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
+        }
+    }
+
+    r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
+    if (r != 0) {
+        LOGED(ERROR, domid, "Getting vcpu sched rtds");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    scinfo->sched = LIBXL_SCHEDULER_RTDS;
+    for (i = 0; i < num_vcpus; i++) {
+        scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
+        scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
+        scinfo->vcpus[i].extratime =
+                !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
+        scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
+    }
+    rc = 0;
+out:
+    return rc;
+}
+
+/* Get the RTDS scheduling parameters of all vcpus of a domain */
+static int sched_rtds_vcpu_get_all(libxl__gc *gc, uint32_t domid,
+                                   libxl_vcpu_sched_params *scinfo)
+{
+    uint32_t num_vcpus;
+    int i, r, rc;
+    xc_dominfo_t info;
+    struct xen_domctl_schedparam_vcpu *vcpus;
+
+    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+    if (r < 0) {
+        LOGED(ERROR, domid, "Getting domain info");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (scinfo->num_vcpus > 0) {
+        rc = ERROR_INVAL;
+        goto out;
+    } else {
+        num_vcpus = info.max_vcpu_id + 1;
+        GCNEW_ARRAY(vcpus, num_vcpus);
+        for (i = 0; i < num_vcpus; i++)
+            vcpus[i].vcpuid = i;
+    }
+
+    r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
+    if (r != 0) {
+        LOGED(ERROR, domid, "Getting vcpu sched rtds");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    scinfo->sched = LIBXL_SCHEDULER_RTDS;
+    scinfo->num_vcpus = num_vcpus;
+    scinfo->vcpus = libxl__calloc(NOGC, num_vcpus,
+                                  sizeof(libxl_sched_params));
+
+    for (i = 0; i < num_vcpus; i++) {
+        scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
+        scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
+        scinfo->vcpus[i].extratime =
+                !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
+        scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
+    }
+    rc = 0;
+out:
+    return rc;
+}
+
+/* Set the RTDS scheduling parameters of vcpu(s) */
+static int sched_rtds_vcpu_set(libxl__gc *gc, uint32_t domid,
+                               const libxl_vcpu_sched_params *scinfo)
+{
+    int r, rc;
+    int i;
+    uint16_t max_vcpuid;
+    xc_dominfo_t info;
+    struct xen_domctl_schedparam_vcpu *vcpus;
+
+    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+    if (r < 0) {
+        LOGED(ERROR, domid, "Getting domain info");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    max_vcpuid = info.max_vcpu_id;
+
+    if (scinfo->num_vcpus <= 0) {
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    for (i = 0; i < scinfo->num_vcpus; i++) {
+        if (scinfo->vcpus[i].vcpuid < 0 ||
+            scinfo->vcpus[i].vcpuid > max_vcpuid) {
+            LOGD(ERROR, domid, "Invalid VCPU %d: valid range is [0, %d]",
+                        scinfo->vcpus[i].vcpuid, max_vcpuid);
+            rc = ERROR_INVAL;
+            goto out;
+        }
+        rc = sched_rtds_validate_params(gc, scinfo->vcpus[i].period,
+                                        scinfo->vcpus[i].budget);
+        if (rc) {
+            rc = ERROR_INVAL;
+            goto out;
+        }
+    }
+    GCNEW_ARRAY(vcpus, scinfo->num_vcpus);
+    for (i = 0; i < scinfo->num_vcpus; i++) {
+        vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
+        vcpus[i].u.rtds.period = scinfo->vcpus[i].period;
+        vcpus[i].u.rtds.budget = scinfo->vcpus[i].budget;
+        if (scinfo->vcpus[i].extratime)
+            vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
+        else
+            vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
+    }
+
+    r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
+                               vcpus, scinfo->num_vcpus);
+    if (r != 0) {
+        LOGED(ERROR, domid, "Setting vcpu sched rtds");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = 0;
+out:
+    return rc;
+}
+
+/* Set the RTDS scheduling parameters of all vcpus of a domain */
+static int sched_rtds_vcpu_set_all(libxl__gc *gc, uint32_t domid,
+                                   const libxl_vcpu_sched_params *scinfo)
+{
+    int r, rc;
+    int i;
+    uint16_t max_vcpuid;
+    xc_dominfo_t info;
+    struct xen_domctl_schedparam_vcpu *vcpus;
+    uint32_t num_vcpus;
+
+    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
+    if (r < 0) {
+        LOGED(ERROR, domid, "Getting domain info");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    max_vcpuid = info.max_vcpu_id;
+
+    if (scinfo->num_vcpus != 1) {
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    if (sched_rtds_validate_params(gc, scinfo->vcpus[0].period,
+                                   scinfo->vcpus[0].budget)) {
+        rc = ERROR_INVAL;
+        goto out;
+    }
+    num_vcpus = max_vcpuid + 1;
+    GCNEW_ARRAY(vcpus, num_vcpus);
+    for (i = 0; i < num_vcpus; i++) {
+        vcpus[i].vcpuid = i;
+        vcpus[i].u.rtds.period = scinfo->vcpus[0].period;
+        vcpus[i].u.rtds.budget = scinfo->vcpus[0].budget;
+        if (scinfo->vcpus[0].extratime)
+            vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
+        else
+            vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
+    }
+
+    r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
+                               vcpus, num_vcpus);
+    if (r != 0) {
+        LOGED(ERROR, domid, "Setting vcpu sched rtds");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    rc = 0;
+out:
+    return rc;
+}
+
+static int sched_rtds_domain_get(libxl__gc *gc, uint32_t domid,
+                               libxl_domain_sched_params *scinfo)
+{
+    struct xen_domctl_sched_rtds sdom;
+    int rc;
+
+    rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
+    if (rc != 0) {
+        LOGED(ERROR, domid, "Getting domain sched rtds");
+        return ERROR_FAIL;
+    }
+
+    libxl_domain_sched_params_init(scinfo);
+
+    scinfo->sched = LIBXL_SCHEDULER_RTDS;
+    scinfo->period = sdom.period;
+    scinfo->budget = sdom.budget;
+
+    return 0;
+}
+
+static int sched_rtds_domain_set(libxl__gc *gc, uint32_t domid,
+                               const libxl_domain_sched_params *scinfo)
+{
+    struct xen_domctl_sched_rtds sdom;
+    int rc;
+
+    rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
+    if (rc != 0) {
+        LOGED(ERROR, domid, "Getting domain sched rtds");
+        return ERROR_FAIL;
+    }
+    if (scinfo->period != LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT)
+        sdom.period = scinfo->period;
+    if (scinfo->budget != LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT)
+        sdom.budget = scinfo->budget;
+    /* Set extratime by default */
+    if (scinfo->extratime)
+        sdom.flags |= XEN_DOMCTL_SCHEDRT_extra;
+    else
+        sdom.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
+    if (sched_rtds_validate_params(gc, sdom.period, sdom.budget))
+        return ERROR_INVAL;
+
+    rc = xc_sched_rtds_domain_set(CTX->xch, domid, &sdom);
+    if (rc < 0) {
+        LOGED(ERROR, domid, "Setting domain sched rtds");
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+                                  const libxl_domain_sched_params *scinfo)
+{
+    GC_INIT(ctx);
+    libxl_scheduler sched = scinfo->sched;
+    int ret;
+
+    if (sched == LIBXL_SCHEDULER_UNKNOWN)
+        sched = libxl__domain_scheduler(gc, domid);
+
+    switch (sched) {
+    case LIBXL_SCHEDULER_SEDF:
+        LOGD(ERROR, domid, "SEDF scheduler no longer available");
+        ret=ERROR_FEATURE_REMOVED;
+        break;
+    case LIBXL_SCHEDULER_CREDIT:
+        ret=sched_credit_domain_set(gc, domid, scinfo);
+        break;
+    case LIBXL_SCHEDULER_CREDIT2:
+        ret=sched_credit2_domain_set(gc, domid, scinfo);
+        break;
+    case LIBXL_SCHEDULER_ARINC653:
+        ret=sched_arinc653_domain_set(gc, domid, scinfo);
+        break;
+    case LIBXL_SCHEDULER_RTDS:
+        ret=sched_rtds_domain_set(gc, domid, scinfo);
+        break;
+    case LIBXL_SCHEDULER_NULL:
+        ret=sched_null_domain_set(gc, domid, scinfo);
+        break;
+    default:
+        LOGD(ERROR, domid, "Unknown scheduler");
+        ret=ERROR_INVAL;
+        break;
+    }
+
+    GC_FREE;
+    return ret;
+}
+
+int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
+                                const libxl_vcpu_sched_params *scinfo)
+{
+    GC_INIT(ctx);
+    libxl_scheduler sched = scinfo->sched;
+    int rc;
+
+    if (sched == LIBXL_SCHEDULER_UNKNOWN)
+        sched = libxl__domain_scheduler(gc, domid);
+
+    switch (sched) {
+    case LIBXL_SCHEDULER_SEDF:
+        LOGD(ERROR, domid, "SEDF scheduler no longer available");
+        rc = ERROR_FEATURE_REMOVED;
+        break;
+    case LIBXL_SCHEDULER_CREDIT:
+    case LIBXL_SCHEDULER_CREDIT2:
+    case LIBXL_SCHEDULER_ARINC653:
+    case LIBXL_SCHEDULER_NULL:
+        LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
+        rc = ERROR_INVAL;
+        break;
+    case LIBXL_SCHEDULER_RTDS:
+        rc = sched_rtds_vcpu_set(gc, domid, scinfo);
+        break;
+    default:
+        LOGD(ERROR, domid, "Unknown scheduler");
+        rc = ERROR_INVAL;
+        break;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
+                                    const libxl_vcpu_sched_params *scinfo)
+{
+    GC_INIT(ctx);
+    libxl_scheduler sched = scinfo->sched;
+    int rc;
+
+    if (sched == LIBXL_SCHEDULER_UNKNOWN)
+        sched = libxl__domain_scheduler(gc, domid);
+
+    switch (sched) {
+    case LIBXL_SCHEDULER_SEDF:
+        LOGD(ERROR, domid, "SEDF scheduler no longer available");
+        rc = ERROR_FEATURE_REMOVED;
+        break;
+    case LIBXL_SCHEDULER_CREDIT:
+    case LIBXL_SCHEDULER_CREDIT2:
+    case LIBXL_SCHEDULER_ARINC653:
+    case LIBXL_SCHEDULER_NULL:
+        LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
+        rc = ERROR_INVAL;
+        break;
+    case LIBXL_SCHEDULER_RTDS:
+        rc = sched_rtds_vcpu_set_all(gc, domid, scinfo);
+        break;
+    default:
+        LOGD(ERROR, domid, "Unknown scheduler");
+        rc = ERROR_INVAL;
+        break;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+                                  libxl_domain_sched_params *scinfo)
+{
+    GC_INIT(ctx);
+    int ret;
+
+    libxl_domain_sched_params_init(scinfo);
+
+    scinfo->sched = libxl__domain_scheduler(gc, domid);
+
+    switch (scinfo->sched) {
+    case LIBXL_SCHEDULER_SEDF:
+        LOGD(ERROR, domid, "SEDF scheduler no longer available");
+        ret=ERROR_FEATURE_REMOVED;
+        break;
+    case LIBXL_SCHEDULER_CREDIT:
+        ret=sched_credit_domain_get(gc, domid, scinfo);
+        break;
+    case LIBXL_SCHEDULER_CREDIT2:
+        ret=sched_credit2_domain_get(gc, domid, scinfo);
+        break;
+    case LIBXL_SCHEDULER_RTDS:
+        ret=sched_rtds_domain_get(gc, domid, scinfo);
+        break;
+    case LIBXL_SCHEDULER_NULL:
+        ret=sched_null_domain_get(gc, domid, scinfo);
+        break;
+    default:
+        LOGD(ERROR, domid, "Unknown scheduler");
+        ret=ERROR_INVAL;
+        break;
+    }
+
+    GC_FREE;
+    return ret;
+}
+
+int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
+                                libxl_vcpu_sched_params *scinfo)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    scinfo->sched = libxl__domain_scheduler(gc, domid);
+
+    switch (scinfo->sched) {
+    case LIBXL_SCHEDULER_SEDF:
+        LOGD(ERROR, domid, "SEDF scheduler is no longer available");
+        rc = ERROR_FEATURE_REMOVED;
+        break;
+    case LIBXL_SCHEDULER_CREDIT:
+    case LIBXL_SCHEDULER_CREDIT2:
+    case LIBXL_SCHEDULER_ARINC653:
+    case LIBXL_SCHEDULER_NULL:
+        LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
+        rc = ERROR_INVAL;
+        break;
+    case LIBXL_SCHEDULER_RTDS:
+        rc = sched_rtds_vcpu_get(gc, domid, scinfo);
+        break;
+    default:
+        LOGD(ERROR, domid, "Unknown scheduler");
+        rc = ERROR_INVAL;
+        break;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
+                                    libxl_vcpu_sched_params *scinfo)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    scinfo->sched = libxl__domain_scheduler(gc, domid);
+
+    switch (scinfo->sched) {
+    case LIBXL_SCHEDULER_SEDF:
+        LOGD(ERROR, domid, "SEDF scheduler is no longer available");
+        rc = ERROR_FEATURE_REMOVED;
+        break;
+    case LIBXL_SCHEDULER_CREDIT:
+    case LIBXL_SCHEDULER_CREDIT2:
+    case LIBXL_SCHEDULER_ARINC653:
+    case LIBXL_SCHEDULER_NULL:
+        LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
+        rc = ERROR_INVAL;
+        break;
+    case LIBXL_SCHEDULER_RTDS:
+        rc = sched_rtds_vcpu_get_all(gc, domid, scinfo);
+        break;
+    default:
+        LOGD(ERROR, domid, "Unknown scheduler");
+        rc = ERROR_INVAL;
+        break;
+    }
+
+    GC_FREE;
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_sr_stream_format.h b/tools/libs/light/libxl_sr_stream_format.h
new file mode 100644 (file)
index 0000000..75f5190
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef LIBXL__SR_STREAM_FORMAT_H
+#define LIBXL__SR_STREAM_FORMAT_H
+
+/*
+ * C structures for the Migration v2 stream format.
+ * See docs/specs/libxl-migration-stream.pandoc
+ */
+
+#include <stdint.h>
+
+typedef struct libxl__sr_hdr
+{
+    uint64_t ident;
+    uint32_t version;
+    uint32_t options;
+} libxl__sr_hdr;
+
+#define RESTORE_STREAM_IDENT         0x4c6962786c466d74UL
+#define RESTORE_STREAM_VERSION       0x00000002U
+
+#define RESTORE_OPT_BIG_ENDIAN       (1u << 0)
+#define RESTORE_OPT_LEGACY           (1u << 1)
+
+
+typedef struct libxl__sr_rec_hdr
+{
+    uint32_t type;
+    uint32_t length;
+} libxl__sr_rec_hdr;
+
+/* All records must be aligned up to an 8 octet boundary */
+#define REC_ALIGN_ORDER              3U
+
+#define REC_TYPE_END                    0x00000000U
+#define REC_TYPE_LIBXC_CONTEXT          0x00000001U
+#define REC_TYPE_EMULATOR_XENSTORE_DATA 0x00000002U
+#define REC_TYPE_EMULATOR_CONTEXT       0x00000003U
+#define REC_TYPE_CHECKPOINT_END         0x00000004U
+#define REC_TYPE_CHECKPOINT_STATE       0x00000005U
+
+typedef struct libxl__sr_emulator_hdr
+{
+    uint32_t id;
+    uint32_t index;
+} libxl__sr_emulator_hdr;
+
+#define EMULATOR_UNKNOWN             0x00000000U
+#define EMULATOR_QEMU_TRADITIONAL    0x00000001U
+#define EMULATOR_QEMU_UPSTREAM       0x00000002U
+
+typedef struct libxl_sr_checkpoint_state
+{
+    uint32_t id;
+} libxl_sr_checkpoint_state;
+
+#define CHECKPOINT_NEW               0x00000000U
+#define CHECKPOINT_SVM_SUSPENDED     0x00000001U
+#define CHECKPOINT_SVM_READY         0x00000002U
+#define CHECKPOINT_SVM_RESUMED       0x00000003U
+
+#endif /* LIBXL__SR_STREAM_FORMAT_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_stream_read.c b/tools/libs/light/libxl_stream_read.c
new file mode 100644 (file)
index 0000000..514f6d9
--- /dev/null
@@ -0,0 +1,977 @@
+/*
+ * Copyright (C) 2015      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*
+ * Infrastructure for reading and acting on the contents of a libxl
+ * migration stream. There are a lot of moving parts here.
+ *
+ * The logic revolves around two actions; reading another record from
+ * the stream, and processing the records.  The stream_continue()
+ * function is responsible for choosing the next action to perform.
+ *
+ * The exact order of reading and processing is controlled by 'phase'.
+ * All complete records are held in the record_queue before being
+ * processed, and all records will be processed in queue order.
+ *
+ * Internal states:
+ *           running  phase       in_         record   incoming
+ *                                checkpoint  _queue   _record
+ *
+ * Undefined    undef  undef        undef       undef    undef
+ * Idle         false  undef        false       0        0
+ * Active       true   NORMAL       false       0/1      0/partial
+ * Active       true   BUFFERING    true        any      0/partial
+ * Active       true   UNBUFFERING  true        any      0
+ *
+ * While reading data from the stream, 'dc' is active and a callback
+ * is expected.  Most actions in process_record() start a callback of
+ * their own.  Those which don't return out and stream_continue() sets
+ * up the next action.
+ *
+ * PHASE_NORMAL:
+ *   This phase is used for regular migration or resume from file.
+ *   Records are read one at time and immediately processed.  (The
+ *   record queue will not contain more than a single record.)
+ *
+ * PHASE_BUFFERING:
+ *   This phase is used in checkpointed streams, when libxc signals
+ *   the presence of a checkpoint in the stream.  Records are read and
+ *   buffered until a CHECKPOINT_END record has been read.
+ *
+ * PHASE_UNBUFFERING:
+ *   Once a CHECKPOINT_END record has been read, all buffered records
+ *   are processed.
+ *
+ * Note:
+ *   Record buffers are not allocated from a GC; they are allocated
+ *   and tracked manually.  This is to avoid OOM with Remus where the
+ *   AO lives for the lifetime of the process.  Per-checkpoint AO's
+ *   might be an avenue to explore.
+ *
+ * Entry points from outside:
+ *  - libxl__stream_read_init()
+ *     - Initialises state.  Must be called once before _start()
+ *  - libxl__stream_read_start()
+ *     - Starts reading records from the stream, and acting on them.
+ *  - libxl__stream_read_start_checkpoint()
+ *     - Starts buffering records at a checkpoint.  Must be called on
+ *       a running stream.
+ *
+ * There are several chains of event:
+ *
+ * 1) Starting a stream follows:
+ *    - libxl__stream_read_start()
+ *    - stream_header_done()
+ *    - stream_continue()
+ *
+ * 2) Reading a record follows:
+ *    - stream_continue()
+ *    - record_header_done()
+ *    - record_body_done()
+ *    - stream_continue()
+ *
+ * 3) Processing a record had several chains to follow, depending on
+ *    the record in question.
+ * 3a) "Simple" record:
+ *    - process_record()
+ *    - stream_continue()
+ * 3b) LIBXC record:
+ *    - process_record()
+ *    - libxl__xc_domain_restore()
+ *    - libxl__xc_domain_restore_done()
+ *    - stream_continue()
+ * 3c) EMULATOR record:
+ *    - process_record()
+ *    - stream_write_emulator()
+ *    - stream_write_emulator_done()
+ *    - stream_continue()
+ *
+ * Depending on the contents of the stream, there are likely to be several
+ * parallel tasks being managed.  check_all_finished() is used to join all
+ * tasks in both success and error cases.
+ *
+ * Failover for remus
+ *  - We buffer all records until a CHECKPOINT_END record is received
+ *  - We will consume the buffered records when a CHECKPOINT_END record
+ *    is received
+ *  - If we find some internal error, then rc or retval is not 0 in
+ *    libxl__xc_domain_restore_done(). In this case, we don't resume the
+ *    guest
+ *  - If we need to do failover from primary, then rc and retval are both
+ *    0 in libxl__xc_domain_restore_done(). In this case, the buffered
+ *    state will be dropped, because we haven't received a CHECKPOINT_END
+ *    record, and therefore the buffered state is inconsistent. In
+ *    libxl__xc_domain_restore_done(), we just complete the stream and
+ *    stream->completion_callback() will be called to resume the guest
+ *
+ * For back channel stream:
+ * - libxl__stream_read_start()
+ *    - Set up the stream to running state
+ *
+ * - libxl__stream_read_continue()
+ *     - Set up reading the next record from a started stream.
+ *       Add some codes to process_record() to handle the record.
+ *       Then call stream->checkpoint_callback() to return.
+ */
+
+/* Success/error/cleanup handling. */
+static void stream_complete(libxl__egc *egc,
+                            libxl__stream_read_state *stream, int rc);
+static void checkpoint_done(libxl__egc *egc,
+                            libxl__stream_read_state *stream, int rc);
+static void stream_done(libxl__egc *egc,
+                        libxl__stream_read_state *stream, int rc);
+static void conversion_done(libxl__egc *egc,
+                            libxl__conversion_helper_state *chs, int rc);
+static void check_all_finished(libxl__egc *egc,
+                               libxl__stream_read_state *stream, int rc);
+
+/* Event chain for first iteration, from _start(). */
+static void stream_header_done(libxl__egc *egc,
+                               libxl__datacopier_state *dc,
+                               int rc, int onwrite, int errnoval);
+static void stream_continue(libxl__egc *egc,
+                            libxl__stream_read_state *stream);
+static void setup_read_record(libxl__egc *egc,
+                              libxl__stream_read_state *stream);
+static void record_header_done(libxl__egc *egc,
+                               libxl__datacopier_state *dc,
+                               int rc, int onwrite, int errnoval);
+static void record_body_done(libxl__egc *egc,
+                             libxl__datacopier_state *dc,
+                             int rc, int onwrite, int errnoval);
+static bool process_record(libxl__egc *egc,
+                           libxl__stream_read_state *stream);
+
+/* Event chain for processing an emulator blob. */
+static void write_emulator_blob(libxl__egc *egc,
+                                libxl__stream_read_state *stream,
+                                libxl__sr_record_buf *rec);
+static void write_emulator_done(libxl__egc *egc,
+                                libxl__datacopier_state *dc,
+                                int rc, int onwrite, int errnoval);
+
+/* Handlers for checkpoint state mini-loop */
+static void checkpoint_state_done(libxl__egc *egc,
+                                  libxl__stream_read_state *stream, int rc);
+
+/*----- Helpers -----*/
+
+/* Helper to set up reading some data from the stream. */
+static int setup_read(libxl__stream_read_state *stream,
+                      const char *what, void *ptr, size_t nr_bytes,
+                      libxl__datacopier_callback cb)
+{
+    libxl__datacopier_state *dc = &stream->dc;
+
+    dc->readwhat      = what;
+    dc->readbuf       = ptr;
+    dc->bytes_to_read = nr_bytes;
+    dc->used          = 0;
+    dc->callback      = cb;
+
+    return libxl__datacopier_start(dc);
+}
+
+static void free_record(libxl__sr_record_buf *rec)
+{
+    if (rec) {
+        free(rec->body);
+        free(rec);
+    }
+}
+
+/*----- Entrypoints -----*/
+
+void libxl__stream_read_init(libxl__stream_read_state *stream)
+{
+    assert(stream->ao);
+
+    stream->shs.ao = stream->ao;
+    libxl__save_helper_init(&stream->shs);
+
+    stream->chs.ao = stream->ao;
+    libxl__conversion_helper_init(&stream->chs);
+
+    stream->rc = 0;
+    stream->running = false;
+    stream->in_checkpoint = false;
+    stream->sync_teardown = false;
+    FILLZERO(stream->dc);
+    FILLZERO(stream->hdr);
+    LIBXL_STAILQ_INIT(&stream->record_queue);
+    stream->phase = SRS_PHASE_NORMAL;
+    stream->recursion_guard = false;
+    stream->incoming_record = NULL;
+    FILLZERO(stream->emu_dc);
+    stream->emu_carefd = NULL;
+}
+
+void libxl__stream_read_start(libxl__egc *egc,
+                              libxl__stream_read_state *stream)
+{
+    libxl__datacopier_state *dc = &stream->dc;
+    STATE_AO_GC(stream->ao);
+    int rc = 0;
+
+    libxl__stream_read_init(stream);
+
+    stream->running = true;
+    stream->phase   = SRS_PHASE_NORMAL;
+
+    if (stream->legacy) {
+        /*
+         * Convert the legacy stream.
+         *
+         * This results in a fork()/exec() of conversion helper script.  It is
+         * passed the exiting stream->fd as an input, and returns the
+         * transformed stream via a new pipe.  The fd of this new pipe then
+         * replaces stream->fd, to make the rest of the stream read code
+         * agnostic to whether legacy conversion is happening or not.
+         */
+        libxl__conversion_helper_state *chs = &stream->chs;
+
+        chs->legacy_fd = stream->fd;
+        chs->hvm =
+            (stream->dcs->guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM);
+        chs->completion_callback = conversion_done;
+
+        rc = libxl__convert_legacy_stream(egc, &stream->chs);
+
+        if (rc) {
+            LOG(ERROR, "Failed to start the legacy stream conversion helper");
+            goto err;
+        }
+
+        /* There should be no interaction of COLO backchannels and legacy
+         * stream conversion. */
+        assert(!stream->back_channel);
+
+        /* Confirm *dc is still zeroed out, while we shuffle stream->fd. */
+        assert(dc->ao == NULL);
+        assert(stream->chs.v2_carefd);
+        stream->fd = libxl__carefd_fd(stream->chs.v2_carefd);
+        stream->dcs->libxc_fd = stream->fd;
+    }
+    /* stream->fd is now a v2 stream. */
+
+    dc->ao       = stream->ao;
+    dc->copywhat = "restore v2 stream";
+    dc->readfd   = stream->fd;
+    dc->writefd  = -1;
+
+    if (stream->back_channel)
+        return;
+
+    /* Start reading the stream header. */
+    rc = setup_read(stream, "stream header",
+                    &stream->hdr, sizeof(stream->hdr),
+                    stream_header_done);
+    if (rc)
+        goto err;
+
+    assert(!rc);
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+void libxl__stream_read_start_checkpoint(libxl__egc *egc,
+                                         libxl__stream_read_state *stream)
+{
+    assert(stream->running);
+    assert(!stream->in_checkpoint);
+
+    stream->in_checkpoint = true;
+    stream->phase = SRS_PHASE_BUFFERING;
+
+    /*
+     * Libxc has handed control of the fd to us.  Start reading some
+     * libxl records out of it.
+     */
+    stream_continue(egc, stream);
+}
+
+void libxl__stream_read_abort(libxl__egc *egc,
+                              libxl__stream_read_state *stream, int rc)
+{
+    assert(rc);
+
+    if (stream->running)
+        stream_complete(egc, stream, rc);
+}
+
+/*----- Event logic -----*/
+
+static void stream_header_done(libxl__egc *egc,
+                               libxl__datacopier_state *dc,
+                               int rc, int onwrite, int errnoval)
+{
+    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
+    libxl__sr_hdr *hdr = &stream->hdr;
+    STATE_AO_GC(dc->ao);
+
+    if (rc)
+        goto err;
+
+    hdr->ident   = be64toh(hdr->ident);
+    hdr->version = be32toh(hdr->version);
+    hdr->options = be32toh(hdr->options);
+
+    if (hdr->ident != RESTORE_STREAM_IDENT) {
+        rc = ERROR_FAIL;
+        LOG(ERROR,
+            "Invalid ident: expected 0x%016"PRIx64", got 0x%016"PRIx64,
+            RESTORE_STREAM_IDENT, hdr->ident);
+        goto err;
+    }
+    if (hdr->version != RESTORE_STREAM_VERSION) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "Unexpected Version: expected %"PRIu32", got %"PRIu32,
+            RESTORE_STREAM_VERSION, hdr->version);
+        goto err;
+    }
+    if (hdr->options & RESTORE_OPT_BIG_ENDIAN) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "Unable to handle big endian streams");
+        goto err;
+    }
+
+    LOG(DEBUG, "Stream v%"PRIu32"%s", hdr->version,
+        hdr->options & RESTORE_OPT_LEGACY ? " (from legacy)" : "");
+
+    stream_continue(egc, stream);
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+static void stream_continue(libxl__egc *egc,
+                            libxl__stream_read_state *stream)
+{
+    STATE_AO_GC(stream->ao);
+
+    /*
+     * Must not mutually recurse with process_record().
+     *
+     * For records whose processing function is synchronous
+     * (e.g. TOOLSTACK), process_record() does not start another async
+     * operation, and a further operation should be started.
+     *
+     * A naive solution, which would function in general, would be for
+     * process_record() to call stream_continue().  However, this
+     * would allow the content of the stream to cause mutual
+     * recursion, and possibly for us to fall off our stack.
+     *
+     * Instead, process_record() indicates with its return value
+     * whether a further operation needs to start, and the
+     * recursion_guard is in place to catch any code paths which get
+     * this wrong.
+     */
+    assert(stream->recursion_guard == false);
+    stream->recursion_guard = true;
+
+    switch (stream->phase) {
+    case SRS_PHASE_NORMAL:
+        /*
+         * Normal phase (regular migration or restore from file):
+         *
+         * logically:
+         *   do { read_record(); process_record(); } while ( not END );
+         *
+         * Alternate between reading a record from the stream, and
+         * processing the record.  There should never be two records
+         * in the queue.
+         */
+        if (LIBXL_STAILQ_EMPTY(&stream->record_queue))
+            setup_read_record(egc, stream);
+        else {
+            if (process_record(egc, stream))
+                setup_read_record(egc, stream);
+
+            /*
+             * process_record() had better have consumed the one and
+             * only record in the queue.
+             */
+            assert(LIBXL_STAILQ_EMPTY(&stream->record_queue));
+        }
+        break;
+
+    case SRS_PHASE_BUFFERING: {
+        /*
+         * Buffering phase (checkpointed streams only):
+         *
+         * logically:
+         *   do { read_record(); } while ( not CHECKPOINT_END );
+         *
+         * Read and buffer all records from the stream until a
+         * CHECKPOINT_END record is encountered.  We need to peek at
+         * the tail to spot the CHECKPOINT_END record, and switch to
+         * the unbuffering phase.
+         */
+        libxl__sr_record_buf *rec = LIBXL_STAILQ_LAST(
+            &stream->record_queue, libxl__sr_record_buf, entry);
+
+        assert(stream->in_checkpoint);
+
+        if (!rec || (rec->hdr.type != REC_TYPE_CHECKPOINT_END)) {
+            setup_read_record(egc, stream);
+            break;
+        }
+
+        /*
+         * There are now some number of buffered records, with a
+         * CHECKPOINT_END at the end. Start processing them all.
+         */
+        stream->phase = SRS_PHASE_UNBUFFERING;
+    }
+        /* FALLTHROUGH */
+    case SRS_PHASE_UNBUFFERING:
+        /*
+         * Unbuffering phase (checkpointed streams only):
+         *
+         * logically:
+         *   do { process_record(); } while ( not CHECKPOINT_END );
+         *
+         * Process all records collected during the buffering phase.
+         */
+        assert(stream->in_checkpoint);
+
+        while (process_record(egc, stream))
+            ; /*
+               * Nothing! process_record() helpfully tells us if no specific
+               * futher actions have been set up, in which case we want to go
+               * ahead and process the next record.
+               */
+        break;
+
+    default:
+        abort();
+    }
+
+    assert(stream->recursion_guard == true);
+    stream->recursion_guard = false;
+}
+
+static void setup_read_record(libxl__egc *egc,
+                              libxl__stream_read_state *stream)
+{
+    libxl__sr_record_buf *rec = NULL;
+    STATE_AO_GC(stream->ao);
+    int rc;
+
+    assert(stream->incoming_record == NULL);
+    stream->incoming_record = rec = libxl__zalloc(NOGC, sizeof(*rec));
+
+    rc = setup_read(stream, "record header",
+                    &rec->hdr, sizeof(rec->hdr),
+                    record_header_done);
+    if (rc)
+        goto err;
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+static void record_header_done(libxl__egc *egc,
+                               libxl__datacopier_state *dc,
+                               int rc, int onwrite, int errnoval)
+{
+    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
+    libxl__sr_record_buf *rec = stream->incoming_record;
+    STATE_AO_GC(dc->ao);
+
+    if (rc)
+        goto err;
+
+    /* No body? All done. */
+    if (rec->hdr.length == 0) {
+        record_body_done(egc, dc, 0, 0, 0);
+        return;
+    }
+
+    size_t bytes_to_read = ROUNDUP(rec->hdr.length, REC_ALIGN_ORDER);
+    rec->body = libxl__malloc(NOGC, bytes_to_read);
+
+    rc = setup_read(stream, "record body",
+                    rec->body, bytes_to_read,
+                    record_body_done);
+    if (rc)
+        goto err;
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+static void record_body_done(libxl__egc *egc,
+                             libxl__datacopier_state *dc,
+                             int rc, int onwrite, int errnoval)
+{
+    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
+    libxl__sr_record_buf *rec = stream->incoming_record;
+    STATE_AO_GC(dc->ao);
+
+    if (rc)
+        goto err;
+
+    LIBXL_STAILQ_INSERT_TAIL(&stream->record_queue, rec, entry);
+    stream->incoming_record = NULL;
+
+    stream_continue(egc, stream);
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+/*
+ * Returns a boolean indicating whether a further action should be set
+ * up by the caller.  This is needed to prevent mutual recursion with
+ * stream_continue().
+ *
+ * It is a bug for this function to ever call stream_continue() or
+ * setup_read_record().
+ */
+static bool process_record(libxl__egc *egc,
+                           libxl__stream_read_state *stream)
+{
+    STATE_AO_GC(stream->ao);
+    libxl__domain_create_state *dcs = stream->dcs;
+    libxl__sr_record_buf *rec;
+    libxl_sr_checkpoint_state *srcs;
+    bool further_action_needed = false;
+    int rc = 0;
+
+    /* Pop a record from the head of the queue. */
+    assert(!LIBXL_STAILQ_EMPTY(&stream->record_queue));
+    rec = LIBXL_STAILQ_FIRST(&stream->record_queue);
+    LIBXL_STAILQ_REMOVE_HEAD(&stream->record_queue, entry);
+
+    LOG(DEBUG, "Record: %u, length %u", rec->hdr.type, rec->hdr.length);
+
+    switch (rec->hdr.type) {
+
+    case REC_TYPE_END:
+        stream_complete(egc, stream, 0);
+        break;
+
+    case REC_TYPE_LIBXC_CONTEXT:
+        libxl__xc_domain_restore(egc, dcs, &stream->shs);
+        break;
+
+    case REC_TYPE_EMULATOR_XENSTORE_DATA:
+        if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
+            rc = ERROR_FAIL;
+            LOG(ERROR,
+                "Received a xenstore emulator record when none was expected");
+            goto err;
+        }
+
+        if (rec->hdr.length < sizeof(libxl__sr_emulator_hdr)) {
+            rc = ERROR_FAIL;
+            LOG(ERROR,
+                "Emulator xenstore data record too short to contain header");
+            goto err;
+        }
+
+        rc = libxl__restore_emulator_xenstore_data(dcs,
+            rec->body + sizeof(libxl__sr_emulator_hdr),
+            rec->hdr.length - sizeof(libxl__sr_emulator_hdr));
+        if (rc)
+            goto err;
+
+        /*
+         * libxl__restore_emulator_xenstore_data() is a synchronous function.
+         * Request that our caller queues another action for us.
+         */
+        further_action_needed = true;
+        break;
+
+    case REC_TYPE_EMULATOR_CONTEXT:
+        if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
+            rc = ERROR_FAIL;
+            LOG(ERROR,
+                "Received an emulator context record when none was expected");
+            goto err;
+        }
+
+        write_emulator_blob(egc, stream, rec);
+        break;
+
+    case REC_TYPE_CHECKPOINT_END:
+        if (!stream->in_checkpoint) {
+            LOG(ERROR, "Unexpected CHECKPOINT_END record in stream");
+            rc = ERROR_FAIL;
+            goto err;
+        }
+        checkpoint_done(egc, stream, 0);
+        break;
+
+    case REC_TYPE_CHECKPOINT_STATE:
+        if (!stream->in_checkpoint_state) {
+            LOG(ERROR, "Unexpected CHECKPOINT_STATE record in stream");
+            rc = ERROR_FAIL;
+            goto err;
+        }
+
+        srcs = rec->body;
+        checkpoint_state_done(egc, stream, srcs->id);
+        break;
+
+    default:
+        LOG(ERROR, "Unrecognised record 0x%08x", rec->hdr.type);
+        rc = ERROR_FAIL;
+        goto err;
+    }
+
+    assert(!rc);
+    free_record(rec);
+    return further_action_needed;
+
+ err:
+    assert(rc);
+    free_record(rec);
+    stream_complete(egc, stream, rc);
+    return false;
+}
+
+static void write_emulator_blob(libxl__egc *egc,
+                                libxl__stream_read_state *stream,
+                                libxl__sr_record_buf *rec)
+{
+    libxl__domain_create_state *dcs = stream->dcs;
+    libxl__datacopier_state *dc = &stream->emu_dc;
+    libxl__sr_emulator_hdr *emu_hdr;
+    STATE_AO_GC(stream->ao);
+    char path[256];
+    int rc = 0, writefd;
+
+    if (rec->hdr.length < sizeof(*emu_hdr)) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "Emulator record too short to contain header");
+        goto err;
+    }
+    emu_hdr = rec->body;
+
+    sprintf(path, LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", dcs->guest_domid);
+
+    assert(stream->emu_carefd == NULL);
+    libxl__carefd_begin();
+    writefd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    stream->emu_carefd = libxl__carefd_opened(CTX, writefd);
+
+    if (writefd == -1) {
+        rc = ERROR_FAIL;
+        LOGE(ERROR, "unable to open %s", path);
+        goto err;
+    }
+
+    FILLZERO(*dc);
+    dc->ao         = stream->ao;
+    dc->writewhat  = "qemu save file";
+    dc->copywhat   = "restore v2 stream";
+    dc->writefd    = writefd;
+    dc->readfd     = -1;
+    dc->maxsz      = -1;
+    dc->callback   = write_emulator_done;
+
+    rc = libxl__datacopier_start(dc);
+    if (rc)
+        goto err;
+
+    libxl__datacopier_prefixdata(egc, dc,
+                                 rec->body + sizeof(*emu_hdr),
+                                 rec->hdr.length - sizeof(*emu_hdr));
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+static void write_emulator_done(libxl__egc *egc,
+                                libxl__datacopier_state *dc,
+                                int rc, int onwrite, int errnoval)
+{
+    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
+    STATE_AO_GC(dc->ao);
+
+    libxl__carefd_close(stream->emu_carefd);
+    stream->emu_carefd = NULL;
+
+    if (rc)
+        goto err;
+
+    stream_continue(egc, stream);
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+/*----- Success/error/cleanup handling. -----*/
+
+static void stream_complete(libxl__egc *egc,
+                            libxl__stream_read_state *stream, int rc)
+{
+    assert(stream->running);
+
+    if (stream->in_checkpoint) {
+        assert(rc);
+
+        /*
+         * If an error is encountered while in a checkpoint, pass it
+         * back to libxc.  The failure will come back around to us via
+         * libxl__xc_domain_restore_done()
+         */
+        checkpoint_done(egc, stream, rc);
+        return;
+    }
+
+    if (stream->in_checkpoint_state) {
+        assert(rc);
+
+        /*
+         * If an error is encountered while in a checkpoint, pass it
+         * back to libxc.  The failure will come back around to us via
+         * 1. normal stream
+         *    libxl__xc_domain_restore_done()
+         * 2. back_channel stream
+         *    libxl__stream_read_abort()
+         */
+        checkpoint_state_done(egc, stream, rc);
+        return;
+    }
+
+    stream_done(egc, stream, rc);
+}
+
+static void checkpoint_done(libxl__egc *egc,
+                            libxl__stream_read_state *stream, int rc)
+{
+    int ret;
+
+    assert(stream->in_checkpoint);
+
+    if (rc == 0)
+        ret = XGR_CHECKPOINT_SUCCESS;
+    else if (stream->phase == SRS_PHASE_BUFFERING)
+        ret = XGR_CHECKPOINT_FAILOVER;
+    else
+        ret = XGR_CHECKPOINT_ERROR;
+
+    stream->checkpoint_callback(egc, stream, ret);
+
+    stream->in_checkpoint = false;
+    stream->phase = SRS_PHASE_NORMAL;
+}
+
+static void stream_done(libxl__egc *egc,
+                        libxl__stream_read_state *stream, int rc)
+{
+    libxl__sr_record_buf *rec, *trec;
+
+    assert(stream->running);
+    assert(!stream->in_checkpoint);
+    assert(!stream->in_checkpoint_state);
+    stream->running = false;
+
+    if (stream->incoming_record)
+        free_record(stream->incoming_record);
+
+    if (stream->emu_carefd)
+        libxl__carefd_close(stream->emu_carefd);
+
+    /* If we started a conversion helper, we took ownership of its carefd. */
+    if (stream->chs.v2_carefd)
+        libxl__carefd_close(stream->chs.v2_carefd);
+
+    /* The record queue had better be empty if the stream believes
+     * itself to have been successful. */
+    assert(LIBXL_STAILQ_EMPTY(&stream->record_queue) || stream->rc);
+
+    LIBXL_STAILQ_FOREACH_SAFE(rec, &stream->record_queue, entry, trec)
+        free_record(rec);
+
+    if (!stream->back_channel) {
+        /*
+         * 1. In stream_done(), stream->running is set to false, so
+         *    the stream itself is not in use.
+         * 2. Read stream is a back channel stream, this means it is
+         *    only used by primary(save side) to read records sent by
+         *    secondary(restore side), so it doesn't have restore helper.
+         * 3. Back channel stream doesn't support legacy stream, so
+         *    there is no conversion helper.
+         * So we don't need invoke check_all_finished here
+         */
+        check_all_finished(egc, stream, rc);
+    }
+}
+
+void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
+                                   int rc, int retval, int errnoval)
+{
+    libxl__domain_create_state *dcs = dcs_void;
+    libxl__stream_read_state *stream = &dcs->srs;
+    STATE_AO_GC(dcs->ao);
+
+    /* convenience aliases */
+    const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
+
+    if (rc)
+        goto err;
+
+    if (retval) {
+        LOGEV(ERROR, errnoval, "restoring domain");
+        rc = ERROR_FAIL;
+        goto err;
+    }
+
+ err:
+    check_all_finished(egc, stream, rc);
+
+    /*
+     * This function is the callback associated with the save helper
+     * task, not the stream task.  We do not know whether the stream is
+     * alive, and check_all_finished() may have torn it down around us.
+     * If the stream is not still alive, we must not continue any work.
+     */
+    if (libxl__stream_read_inuse(stream)) {
+        switch (checkpointed_stream) {
+        case LIBXL_CHECKPOINTED_STREAM_COLO:
+            if (stream->completion_callback) {
+                /*
+                 * restore, just build the secondary vm, don't close
+                 * the stream
+                 */
+                stream->completion_callback(egc, stream, 0);
+            } else {
+                /* failover, just close the stream */
+                stream_complete(egc, stream, 0);
+            }
+            break;
+        case LIBXL_CHECKPOINTED_STREAM_REMUS:
+            /*
+             * Failover from primary. Domain state is currently at a
+             * consistent checkpoint, complete the stream, and call
+             * stream->completion_callback() to resume the guest.
+             */
+            stream_complete(egc, stream, 0);
+            break;
+        case LIBXL_CHECKPOINTED_STREAM_NONE:
+            /*
+             * Libxc has indicated that it is done with the stream.
+             * Resume reading libxl records from it.
+             */
+            stream_continue(egc, stream);
+            break;
+        }
+    }
+}
+
+static void conversion_done(libxl__egc *egc,
+                            libxl__conversion_helper_state *chs, int rc)
+{
+    libxl__stream_read_state *stream = CONTAINER_OF(chs, *stream, chs);
+
+    check_all_finished(egc, stream, rc);
+}
+
+static void check_all_finished(libxl__egc *egc,
+                               libxl__stream_read_state *stream, int rc)
+{
+    STATE_AO_GC(stream->ao);
+
+    /*
+     * In the case of a failure, the _abort()'s below might cancel
+     * synchronously on top of us, or asynchronously at a later point.
+     *
+     * We must avoid the situation where all _abort() cancel
+     * synchronously and the completion_callback() gets called twice;
+     * once by the first error and once by the final stacked abort(),
+     * both of whom will find that all of the tasks have stopped.
+     *
+     * To avoid this problem, any stacked re-entry into this function is
+     * ineligible to fire the completion callback.  The outermost
+     * instance will take care of completing, once the stack has
+     * unwound.
+     */
+    if (stream->sync_teardown)
+        return;
+
+    if (!stream->rc && rc) {
+        /* First reported failure. Tear everything down. */
+        stream->rc = rc;
+        stream->sync_teardown = true;
+
+        libxl__stream_read_abort(egc, stream, rc);
+        libxl__save_helper_abort(egc, &stream->shs);
+        libxl__conversion_helper_abort(egc, &stream->chs, rc);
+
+        stream->sync_teardown = false;
+    }
+
+    /* Don't fire the callback until all our parallel tasks have stopped. */
+    if (libxl__stream_read_inuse(stream) ||
+        libxl__save_helper_inuse(&stream->shs) ||
+        libxl__conversion_helper_inuse(&stream->chs))
+        return;
+
+    if (stream->completion_callback)
+        /* back channel stream doesn't have completion_callback() */
+        stream->completion_callback(egc, stream, stream->rc);
+}
+
+/*----- Checkpoint state handlers -----*/
+
+void libxl__stream_read_checkpoint_state(libxl__egc *egc,
+                                         libxl__stream_read_state *stream)
+{
+    assert(stream->running);
+    assert(!stream->in_checkpoint);
+    assert(!stream->in_checkpoint_state);
+    stream->in_checkpoint_state = true;
+
+    setup_read_record(egc, stream);
+}
+
+static void checkpoint_state_done(libxl__egc *egc,
+                                  libxl__stream_read_state *stream, int rc)
+{
+    assert(stream->in_checkpoint_state);
+    stream->in_checkpoint_state = false;
+    stream->checkpoint_callback(egc, stream, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_stream_write.c b/tools/libs/light/libxl_stream_write.c
new file mode 100644 (file)
index 0000000..634f324
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2015      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*
+ * Infrastructure for writing a domain to a libxl migration v2 stream.
+ *
+ * Entry points from outside:
+ *  - libxl__stream_write_start()
+ *     - Start writing a stream from the start.
+ *  - libxl__stream_write_start_checkpoint()
+ *     - Write the records which form a checkpoint into a stream.
+ *
+ * In normal operation, there are two tasks running at once; this
+ * stream processing, and the libxl-save-helper.  check_all_finished()
+ * is used to join all the tasks in both success and error cases.
+ *
+ * Nomenclature for event callbacks:
+ *  - $FOO_done(): Completion callback for $FOO
+ *  - write_$FOO(): Set up the datacopier to write a $FOO
+ *  - $BAR_header(): A $BAR record header only
+ *  - $BAR_record(): A complete $BAR record with header and content
+ *
+ * The main loop for a plain VM writes:
+ *  - Stream header
+ *  - Libxc record
+ *  - (optional) Emulator xenstore record
+ *  - if (hvm)
+ *      - Emulator context record
+ *  - End record
+ *
+ * For checkpointed stream, there is a second loop which is triggered by a
+ * save-helper checkpoint callback.  It writes:
+ *  - (optional) Emulator xenstore record
+ *  - if (hvm)
+ *      - Emulator context record
+ *  - Checkpoint end record
+ *
+ * For back channel stream:
+ * - libxl__stream_write_start()
+ *    - Set up the stream to running state
+ *
+ * - Use libxl__stream_write_checkpoint_state to write the record. When the
+ *   record is written out, call stream->checkpoint_callback() to return.
+ */
+
+/* Success/error/cleanup handling. */
+static void stream_success(libxl__egc *egc,
+                           libxl__stream_write_state *stream);
+static void stream_complete(libxl__egc *egc,
+                            libxl__stream_write_state *stream, int rc);
+static void stream_done(libxl__egc *egc,
+                        libxl__stream_write_state *stream, int rc);
+static void checkpoint_done(libxl__egc *egc,
+                            libxl__stream_write_state *stream,
+                            int rc);
+static void check_all_finished(libxl__egc *egc,
+                               libxl__stream_write_state *stream, int rc);
+
+/* Event chain for a plain VM. */
+static void stream_header_done(libxl__egc *egc,
+                               libxl__datacopier_state *dc,
+                               int rc, int onwrite, int errnoval);
+static void libxc_header_done(libxl__egc *egc,
+                              libxl__stream_write_state *stream);
+/* libxl__xc_domain_save_done() lives here, event-order wise. */
+static void write_emulator_xenstore_record(libxl__egc *egc,
+                                           libxl__stream_write_state *stream);
+static void emulator_xenstore_record_done(libxl__egc *egc,
+                                          libxl__stream_write_state *stream);
+static void write_emulator_context_record(libxl__egc *egc,
+                                          libxl__stream_write_state *stream);
+static void emulator_context_read_done(libxl__egc *egc,
+                                       libxl__datacopier_state *dc,
+                                       int rc, int onwrite, int errnoval);
+static void emulator_context_record_done(libxl__egc *egc,
+                                         libxl__stream_write_state *stream);
+static void write_end_record(libxl__egc *egc,
+                             libxl__stream_write_state *stream);
+
+/* Event chain unique to checkpointed streams. */
+static void write_checkpoint_end_record(libxl__egc *egc,
+                                        libxl__stream_write_state *stream);
+static void checkpoint_end_record_done(libxl__egc *egc,
+                                       libxl__stream_write_state *stream);
+
+/* checkpoint state */
+static void write_checkpoint_state_done(libxl__egc *egc,
+                                        libxl__stream_write_state *stream);
+static void checkpoint_state_done(libxl__egc *egc,
+                                  libxl__stream_write_state *stream, int rc);
+
+/*----- Helpers -----*/
+
+static void write_done(libxl__egc *egc,
+                       libxl__datacopier_state *dc,
+                       int rc, int onwrite, int errnoval);
+
+/* Generic helper to set up writing some data to the stream. */
+static void setup_generic_write(libxl__egc *egc,
+                                libxl__stream_write_state *stream,
+                                const char *what,
+                                libxl__sr_rec_hdr *hdr,
+                                libxl__sr_emulator_hdr *emu_hdr,
+                                void *body,
+                                sws_record_done_cb cb)
+{
+    static const uint8_t zero_padding[1U << REC_ALIGN_ORDER] = { 0 };
+
+    libxl__datacopier_state *dc = &stream->dc;
+    int rc;
+
+    assert(stream->record_done_callback == NULL);
+
+    dc->writewhat = what;
+    dc->used      = 0;
+    dc->callback  = write_done;
+    rc = libxl__datacopier_start(dc);
+
+    if (rc) {
+        stream_complete(egc, stream, rc);
+        return;
+    }
+
+    size_t padsz = ROUNDUP(hdr->length, REC_ALIGN_ORDER) - hdr->length;
+    uint32_t length = hdr->length;
+
+    /* Insert header */
+    libxl__datacopier_prefixdata(egc, dc, hdr, sizeof(*hdr));
+
+    /* Optional emulator sub-header */
+    if (emu_hdr) {
+        assert(length >= sizeof(*emu_hdr));
+        libxl__datacopier_prefixdata(egc, dc, emu_hdr, sizeof(*emu_hdr));
+        length -= sizeof(*emu_hdr);
+    }
+
+    /* Optional body */
+    if (body)
+        libxl__datacopier_prefixdata(egc, dc, body, length);
+
+    /* Any required padding */
+    if (padsz > 0)
+        libxl__datacopier_prefixdata(egc, dc,
+                                     zero_padding, padsz);
+    stream->record_done_callback = cb;
+}
+
+/* Helper to set up writing a regular record to the stream. */
+static void setup_write(libxl__egc *egc,
+                        libxl__stream_write_state *stream,
+                        const char *what,
+                        libxl__sr_rec_hdr *hdr,
+                        void *body,
+                        sws_record_done_cb cb)
+{
+    setup_generic_write(egc, stream, what, hdr, NULL, body, cb);
+}
+
+/* Helper to set up writing a record with an emulator prefix to the stream. */
+static void setup_emulator_write(libxl__egc *egc,
+                                 libxl__stream_write_state *stream,
+                                 const char *what,
+                                 libxl__sr_rec_hdr *hdr,
+                                 libxl__sr_emulator_hdr *emu_hdr,
+                                 void *body,
+                                 sws_record_done_cb cb)
+{
+    assert(stream->emu_sub_hdr.id != EMULATOR_UNKNOWN);
+    setup_generic_write(egc, stream, what, hdr, emu_hdr, body, cb);
+}
+
+
+static void write_done(libxl__egc *egc,
+                       libxl__datacopier_state *dc,
+                       int rc, int onwrite, int errnoval)
+{
+    libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
+    STATE_AO_GC(stream->ao);
+    sws_record_done_cb cb = stream->record_done_callback;
+
+    stream->record_done_callback = NULL;
+
+    if (onwrite || errnoval)
+        stream_complete(egc, stream, rc ?: ERROR_FAIL);
+    else
+        cb(egc, stream);
+}
+
+/*----- Entrypoints -----*/
+
+void libxl__stream_write_init(libxl__stream_write_state *stream)
+{
+    assert(stream->ao);
+
+    stream->shs.ao = stream->ao;
+    libxl__save_helper_init(&stream->shs);
+
+    stream->rc = 0;
+    stream->running = false;
+    stream->in_checkpoint = false;
+    stream->sync_teardown = false;
+    FILLZERO(stream->dc);
+    stream->record_done_callback = NULL;
+    FILLZERO(stream->emu_dc);
+    stream->emu_carefd = NULL;
+    FILLZERO(stream->emu_rec_hdr);
+    FILLZERO(stream->emu_sub_hdr);
+    stream->emu_body = NULL;
+    stream->device_model_version = LIBXL_DEVICE_MODEL_VERSION_UNKNOWN;
+}
+
+void libxl__stream_write_start(libxl__egc *egc,
+                               libxl__stream_write_state *stream)
+{
+    libxl__datacopier_state *dc = &stream->dc;
+    libxl__domain_save_state *dss = stream->dss;
+    STATE_AO_GC(stream->ao);
+    struct libxl__sr_hdr hdr;
+    int rc = 0;
+
+    libxl__stream_write_init(stream);
+
+    stream->running = true;
+
+    dc->ao        = ao;
+    dc->readfd    = -1;
+    dc->writewhat = "stream header";
+    dc->copywhat  = "save v2 stream";
+    dc->writefd   = stream->fd;
+    dc->maxsz     = -1;
+    dc->callback  = stream_header_done;
+
+    if (stream->back_channel)
+        return;
+
+    if (dss->type == LIBXL_DOMAIN_TYPE_HVM) {
+        stream->device_model_version =
+            libxl__device_model_version_running(gc, dss->domid);
+        switch (stream->device_model_version) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            stream->emu_sub_hdr.id = EMULATOR_QEMU_TRADITIONAL;
+            break;
+
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            stream->emu_sub_hdr.id = EMULATOR_QEMU_UPSTREAM;
+            break;
+
+        default:
+            rc = ERROR_FAIL;
+            LOGD(ERROR, dss->domid, "Unknown emulator for HVM domain");
+            goto err;
+        }
+        stream->emu_sub_hdr.index = 0;
+    }
+
+    rc = libxl__datacopier_start(dc);
+    if (rc)
+        goto err;
+
+    FILLZERO(hdr);
+    hdr.ident   = htobe64(RESTORE_STREAM_IDENT);
+    hdr.version = htobe32(RESTORE_STREAM_VERSION);
+    hdr.options = htobe32(0);
+
+    libxl__datacopier_prefixdata(egc, dc, &hdr, sizeof(hdr));
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+void libxl__stream_write_start_checkpoint(libxl__egc *egc,
+                                          libxl__stream_write_state *stream)
+{
+    assert(stream->running);
+    assert(!stream->in_checkpoint);
+    assert(!stream->back_channel);
+    stream->in_checkpoint = true;
+
+    write_emulator_xenstore_record(egc, stream);
+}
+
+void libxl__stream_write_abort(libxl__egc *egc,
+                               libxl__stream_write_state *stream, int rc)
+{
+    assert(rc);
+
+    if (stream->running)
+        stream_complete(egc, stream, rc);
+}
+
+/*----- Event logic -----*/
+
+static void stream_header_done(libxl__egc *egc,
+                               libxl__datacopier_state *dc,
+                               int rc, int onwrite, int errnoval)
+{
+    libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
+    STATE_AO_GC(stream->ao);
+    struct libxl__sr_rec_hdr rec;
+
+    if (rc || errnoval) {
+        stream_complete(egc, stream, rc ?: ERROR_FAIL);
+        return;
+    }
+
+    FILLZERO(rec);
+    rec.type = REC_TYPE_LIBXC_CONTEXT;
+
+    setup_write(egc, stream, "libxc header",
+                &rec, NULL, libxc_header_done);
+}
+
+static void libxc_header_done(libxl__egc *egc,
+                              libxl__stream_write_state *stream)
+{
+    libxl__xc_domain_save(egc, stream->dss, &stream->shs);
+}
+
+void libxl__xc_domain_save_done(libxl__egc *egc, void *dss_void,
+                                int rc, int retval, int errnoval)
+{
+    libxl__domain_save_state *dss = dss_void;
+    libxl__stream_write_state *stream = &dss->sws;
+    STATE_AO_GC(dss->ao);
+
+    if (rc)
+        goto err;
+
+    if (retval) {
+        LOGEVD(ERROR, errnoval, dss->domid, "saving domain: %s",
+              dss->dsps.guest_responded ?
+              "domain responded to suspend request" :
+              "domain did not respond to suspend request");
+        if (!dss->dsps.guest_responded)
+            rc = ERROR_GUEST_TIMEDOUT;
+        else if (dss->rc)
+            rc = dss->rc;
+        else
+            rc = ERROR_FAIL;
+        goto err;
+    }
+
+ err:
+    check_all_finished(egc, stream, rc);
+
+    /*
+     * This function is the callback associated with the save helper
+     * task, not the stream task.  We do not know whether the stream is
+     * alive, and check_all_finished() may have torn it down around us.
+     * If the stream is not still alive, we must not continue any work.
+     */
+    if (libxl__stream_write_inuse(stream)) {
+        if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE)
+            /*
+             * For remus, if libxl__xc_domain_save_done() completes,
+             * there was an error sending data to the secondary.
+             * Resume the primary ASAP. The caller doesn't care of the
+             * return value (Please refer to libxl__remus_teardown())
+             */
+            stream_complete(egc, stream, 0);
+        else
+            write_emulator_xenstore_record(egc, stream);
+    }
+}
+
+static void write_emulator_xenstore_record(libxl__egc *egc,
+                                           libxl__stream_write_state *stream)
+{
+    libxl__domain_save_state *dss = stream->dss;
+    STATE_AO_GC(stream->ao);
+    struct libxl__sr_rec_hdr rec;
+    int rc;
+    char *buf = NULL;
+    uint32_t len = 0;
+
+    if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
+        emulator_xenstore_record_done(egc, stream);
+        return;
+    }
+
+    rc = libxl__save_emulator_xenstore_data(dss, &buf, &len);
+    if (rc)
+        goto err;
+
+    /* No record? - All done. */
+    if (len == 0) {
+        emulator_xenstore_record_done(egc, stream);
+        return;
+    }
+
+    FILLZERO(rec);
+    rec.type = REC_TYPE_EMULATOR_XENSTORE_DATA;
+    rec.length = len + sizeof(stream->emu_sub_hdr);
+
+    setup_emulator_write(egc, stream, "emulator xenstore record",
+                         &rec, &stream->emu_sub_hdr, buf,
+                         emulator_xenstore_record_done);
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+static void emulator_xenstore_record_done(libxl__egc *egc,
+                                          libxl__stream_write_state *stream)
+{
+    libxl__domain_save_state *dss = stream->dss;
+
+    if (dss->type == LIBXL_DOMAIN_TYPE_HVM)
+        write_emulator_context_record(egc, stream);
+    else {
+        if (stream->in_checkpoint)
+            write_checkpoint_end_record(egc, stream);
+        else
+            write_end_record(egc, stream);
+    }
+}
+
+static void write_emulator_context_record(libxl__egc *egc,
+                                          libxl__stream_write_state *stream)
+{
+    libxl__domain_save_state *dss = stream->dss;
+    libxl__datacopier_state *dc = &stream->emu_dc;
+    STATE_AO_GC(stream->ao);
+    struct libxl__sr_rec_hdr *rec = &stream->emu_rec_hdr;
+    struct stat st;
+    int rc;
+
+    if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
+        emulator_context_record_done(egc, stream);
+        return;
+    }
+
+    /* Convenience aliases */
+    const char *const filename = dss->dsps.dm_savefile;
+
+    libxl__carefd_begin();
+    int readfd = open(filename, O_RDONLY);
+    stream->emu_carefd = libxl__carefd_opened(CTX, readfd);
+    if (readfd == -1) {
+        rc = ERROR_FAIL;
+        LOGED(ERROR, dss->domid, "unable to open %s", filename);
+        goto err;
+    }
+
+    if (fstat(readfd, &st)) {
+        rc = ERROR_FAIL;
+        LOGED(ERROR, dss->domid, "unable to fstat %s", filename);
+        goto err;
+    }
+
+    if (!S_ISREG(st.st_mode)) {
+        rc = ERROR_FAIL;
+        LOGD(ERROR, dss->domid, "%s is not a plain file!", filename);
+        goto err;
+    }
+
+    rec->type = REC_TYPE_EMULATOR_CONTEXT;
+    rec->length = st.st_size + sizeof(stream->emu_sub_hdr);
+    stream->emu_body = libxl__malloc(NOGC, st.st_size);
+
+    FILLZERO(*dc);
+    dc->ao            = stream->ao;
+    dc->readwhat      = "qemu save file";
+    dc->copywhat      = "save v2 stream";
+    dc->readfd        = readfd;
+    dc->writefd       = -1;
+    dc->maxsz         = -1;
+    dc->readbuf       = stream->emu_body;
+    dc->bytes_to_read = st.st_size;
+    dc->callback      = emulator_context_read_done;
+
+    rc = libxl__datacopier_start(dc);
+    if (rc)
+        goto err;
+
+    return;
+
+ err:
+    assert(rc);
+    stream_complete(egc, stream, rc);
+}
+
+static void emulator_context_read_done(libxl__egc *egc,
+                                       libxl__datacopier_state *dc,
+                                       int rc, int onwrite, int errnoval)
+{
+    libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
+    STATE_AO_GC(stream->ao);
+
+    if (rc || onwrite || errnoval) {
+        stream_complete(egc, stream, rc ?: ERROR_FAIL);
+        return;
+    }
+
+    libxl__carefd_close(stream->emu_carefd);
+    stream->emu_carefd = NULL;
+
+    setup_emulator_write(egc, stream, "emulator record",
+                         &stream->emu_rec_hdr,
+                         &stream->emu_sub_hdr,
+                         stream->emu_body,
+                         emulator_context_record_done);
+}
+
+static void emulator_context_record_done(libxl__egc *egc,
+                                         libxl__stream_write_state *stream)
+{
+    free(stream->emu_body);
+    stream->emu_body = NULL;
+
+    if (stream->in_checkpoint)
+        write_checkpoint_end_record(egc, stream);
+    else
+        write_end_record(egc, stream);
+}
+
+static void write_end_record(libxl__egc *egc,
+                             libxl__stream_write_state *stream)
+{
+    struct libxl__sr_rec_hdr rec;
+
+    FILLZERO(rec);
+    rec.type = REC_TYPE_END;
+
+    setup_write(egc, stream, "end record",
+                &rec, NULL, stream_success);
+}
+
+static void write_checkpoint_end_record(libxl__egc *egc,
+                                        libxl__stream_write_state *stream)
+{
+    struct libxl__sr_rec_hdr rec;
+
+    FILLZERO(rec);
+    rec.type = REC_TYPE_CHECKPOINT_END;
+
+    setup_write(egc, stream, "checkpoint end record",
+                &rec, NULL, checkpoint_end_record_done);
+}
+
+static void checkpoint_end_record_done(libxl__egc *egc,
+                                       libxl__stream_write_state *stream)
+{
+    checkpoint_done(egc, stream, 0);
+}
+
+/*----- Success/error/cleanup handling. -----*/
+
+static void stream_success(libxl__egc *egc, libxl__stream_write_state *stream)
+{
+    stream_complete(egc, stream, 0);
+}
+
+static void stream_complete(libxl__egc *egc,
+                            libxl__stream_write_state *stream, int rc)
+{
+    assert(stream->running);
+
+    if (stream->in_checkpoint) {
+        assert(rc);
+
+        /*
+         * If an error is encountered while in a checkpoint, pass it
+         * back to libxc.  The failure will come back around to us via
+         * libxl__xc_domain_save_done()
+         */
+        checkpoint_done(egc, stream, rc);
+        return;
+    }
+
+    if (stream->in_checkpoint_state) {
+        assert(rc);
+
+        /*
+         * If an error is encountered while in a checkpoint, pass it
+         * back to libxc.  The failure will come back around to us via
+         * 1. normal stream
+         *    libxl__xc_domain_save_done()
+         * 2. back_channel stream
+         *    libxl__stream_write_abort()
+         */
+        checkpoint_state_done(egc, stream, rc);
+        return;
+    }
+
+    stream_done(egc, stream, rc);
+}
+
+static void stream_done(libxl__egc *egc,
+                        libxl__stream_write_state *stream, int rc)
+{
+    assert(stream->running);
+    assert(!stream->in_checkpoint_state);
+    stream->running = false;
+
+    if (stream->emu_carefd)
+        libxl__carefd_close(stream->emu_carefd);
+    free(stream->emu_body);
+
+    if (!stream->back_channel) {
+        /*
+         * 1. In stream_done(), stream->running is set to false, so
+         *    the stream itself is not in use.
+         * 2. Write stream is a back channel stream, this means it
+         *    is only used by secondary(restore side) to send records
+         *    back, so it doesn't have save helper.
+         * So we don't need invoke check_all_finished here
+         */
+         check_all_finished(egc, stream, rc);
+    }
+}
+
+static void checkpoint_done(libxl__egc *egc,
+                            libxl__stream_write_state *stream,
+                            int rc)
+{
+    assert(stream->in_checkpoint);
+
+    stream->in_checkpoint = false;
+    stream->checkpoint_callback(egc, stream, rc);
+}
+
+static void check_all_finished(libxl__egc *egc,
+                               libxl__stream_write_state *stream,
+                               int rc)
+{
+    STATE_AO_GC(stream->ao);
+
+    /*
+     * In the case of a failure, the _abort()'s below might cancel
+     * synchronously on top of us, or asynchronously at a later point.
+     *
+     * We must avoid the situation where all _abort() cancel
+     * synchronously and the completion_callback() gets called twice;
+     * once by the first error and once by the final stacked abort(),
+     * both of whom will find that all of the tasks have stopped.
+     *
+     * To avoid this problem, any stacked re-entry into this function is
+     * ineligible to fire the completion callback.  The outermost
+     * instance will take care of completing, once the stack has
+     * unwound.
+     */
+    if (stream->sync_teardown)
+        return;
+
+    if (!stream->rc && rc) {
+        /* First reported failure. Tear everything down. */
+        stream->rc = rc;
+        stream->sync_teardown = true;
+
+        libxl__stream_write_abort(egc, stream, rc);
+        libxl__save_helper_abort(egc, &stream->shs);
+
+        stream->sync_teardown = false;
+    }
+
+    /* Don't fire the callback until all our parallel tasks have stopped. */
+    if (libxl__stream_write_inuse(stream) ||
+        libxl__save_helper_inuse(&stream->shs))
+        return;
+
+    if (stream->completion_callback)
+        /* back channel stream doesn't have completion_callback() */
+        stream->completion_callback(egc, stream, stream->rc);
+}
+
+/*----- checkpoint state -----*/
+
+void libxl__stream_write_checkpoint_state(libxl__egc *egc,
+                                          libxl__stream_write_state *stream,
+                                          libxl_sr_checkpoint_state *srcs)
+{
+    struct libxl__sr_rec_hdr rec;
+
+    assert(stream->running);
+    assert(!stream->in_checkpoint);
+    assert(!stream->in_checkpoint_state);
+    stream->in_checkpoint_state = true;
+
+    FILLZERO(rec);
+    rec.type = REC_TYPE_CHECKPOINT_STATE;
+    rec.length = sizeof(*srcs);
+
+    setup_write(egc, stream, "checkpoint state", &rec,
+                srcs, write_checkpoint_state_done);
+}
+
+static void write_checkpoint_state_done(libxl__egc *egc,
+                                        libxl__stream_write_state *stream)
+{
+    checkpoint_state_done(egc, stream, 0);
+}
+
+static void checkpoint_state_done(libxl__egc *egc,
+                                  libxl__stream_write_state *stream, int rc)
+{
+    assert(stream->in_checkpoint_state);
+    stream->in_checkpoint_state = false;
+    stream->checkpoint_callback(egc, stream, rc);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_test_fdevent.c b/tools/libs/light/libxl_test_fdevent.c
new file mode 100644 (file)
index 0000000..2d875d9
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * fdevent test helpr for the libxl event system
+ */
+
+#include "libxl_internal.h"
+
+#include "libxl_test_fdevent.h"
+
+typedef struct {
+    libxl__ao *ao;
+    libxl__ev_fd fd;
+    libxl__ao_abortable abrt;
+} libxl__test_fdevent;
+
+static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
+                             int rc);
+
+static void tfe_init(libxl__test_fdevent *tfe, libxl__ao *ao)
+{
+    tfe->ao = ao;
+    libxl__ev_fd_init(&tfe->fd);
+    libxl__ao_abortable_init(&tfe->abrt);
+}
+
+static void tfe_cleanup(libxl__gc *gc, libxl__test_fdevent *tfe)
+{
+    libxl__ev_fd_deregister(gc, &tfe->fd);
+    libxl__ao_abortable_deregister(&tfe->abrt);
+}
+
+static void tfe_fd_cb(libxl__egc *egc, libxl__ev_fd *ev,
+                      int fd, short events, short revents)
+{
+    libxl__test_fdevent *tfe = CONTAINER_OF(ev,*tfe,fd);
+    STATE_AO_GC(tfe->ao);
+    fdevent_complete(egc, tfe, 0);
+}
+
+static void tfe_abrt_cb(libxl__egc *egc, libxl__ao_abortable *abrt,
+                        int rc)
+{
+    libxl__test_fdevent *tfe = CONTAINER_OF(abrt,*tfe,abrt);
+    STATE_AO_GC(tfe->ao);
+    fdevent_complete(egc, tfe, rc);
+}
+
+static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
+                             int rc)
+{
+    STATE_AO_GC(tfe->ao);
+    tfe_cleanup(gc, tfe);
+    libxl__ao_complete(egc, ao, rc);
+}
+
+int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
+                       libxl_asyncop_how *ao_how)
+{
+    int rc;
+    libxl__test_fdevent *tfe;
+
+    AO_CREATE(ctx, 0, ao_how);
+    GCNEW(tfe);
+
+    tfe_init(tfe, ao);
+
+    rc = libxl__ev_fd_register(gc, &tfe->fd, tfe_fd_cb, fd, events);
+    if (rc) goto out;
+
+    tfe->abrt.ao = ao;
+    tfe->abrt.callback = tfe_abrt_cb;
+    rc = libxl__ao_abortable_register(&tfe->abrt);
+    if (rc) goto out;
+
+    return AO_INPROGRESS;
+
+ out:
+    tfe_cleanup(gc, tfe);
+    return AO_CREATE_FAIL(rc);
+}
diff --git a/tools/libs/light/libxl_test_fdevent.h b/tools/libs/light/libxl_test_fdevent.h
new file mode 100644 (file)
index 0000000..82a307e
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef TEST_FDEVENT_H
+#define TEST_FDEVENT_H
+
+#include <pthread.h>
+
+int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
+                       libxl_asyncop_how *ao_how)
+                       LIBXL_EXTERNAL_CALLERS_ONLY;
+/* This operation waits for one of the poll events to occur on fd, and
+ * then completes successfully.  (Or, it can be aborted.) */
+
+#endif /*TEST_FDEVENT_H*/
diff --git a/tools/libs/light/libxl_test_timedereg.c b/tools/libs/light/libxl_test_timedereg.c
new file mode 100644 (file)
index 0000000..a567db6
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * timedereg test case for the libxl event system
+ *
+ * To run this test:
+ *    ./test_timedereg
+ * Success:
+ *    program takes a few seconds, prints some debugging output and exits 0
+ * Failure:
+ *    crash
+ *
+ * set up [0]-group timeouts 0 1 2
+ * wait for timeout 1 to occur
+ * deregister 0 and 2.  1 is supposed to be deregistered already
+ * register [1]-group 0 1 2
+ * deregister 1 (should be a no-op)
+ * wait for [1]-group 0 1 2 in turn
+ * on final callback assert that all have been deregistered
+ */
+
+#include "libxl_internal.h"
+
+#include "libxl_test_timedereg.h"
+
+#define NTIMES 3
+static const int ms[2][NTIMES] = { { 2000,1000,2000 }, { 1000,2000,3000 } };
+static libxl__ev_time et[2][NTIMES];
+static libxl__ao *tao;
+static int seq;
+
+static void occurs(libxl__egc *egc, libxl__ev_time *ev,
+                   const struct timeval *requested_abs, int rc);
+
+static void regs(libxl__ao *ao, int j)
+{
+    AO_GC;
+    int rc, i;
+    LOG(DEBUG,"regs(%d)", j);
+    for (i=0; i<NTIMES; i++) {
+        rc = libxl__ev_time_register_rel(ao, &et[j][i], occurs, ms[j][i]);
+        assert(!rc);
+    }    
+}
+
+int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
+{
+    int i;
+    AO_CREATE(ctx, 0, ao_how);
+
+    tao = ao;
+
+    for (i=0; i<NTIMES; i++) {
+        libxl__ev_time_init(&et[0][i]);
+        libxl__ev_time_init(&et[1][i]);
+    }
+
+    regs(ao, 0);
+
+    return AO_INPROGRESS;
+}
+
+static void occurs(libxl__egc *egc, libxl__ev_time *ev,
+                   const struct timeval *requested_abs, int rc)
+{
+    EGC_GC;
+    int i;
+
+    int off = ev - &et[0][0];
+    LOG(DEBUG,"occurs[%d][%d] seq=%d rc=%d", off/NTIMES, off%NTIMES, seq, rc);
+
+    assert(rc == ERROR_TIMEDOUT);
+
+    switch (seq) {
+    case 0:
+        assert(ev == &et[0][1]);
+        libxl__ev_time_deregister(gc, &et[0][0]);
+        libxl__ev_time_deregister(gc, &et[0][2]);
+        regs(tao, 1);
+        libxl__ev_time_deregister(gc, &et[0][1]);
+        break;
+
+    case 1:
+    case 2:
+        assert(ev == &et[1][seq-1]);
+        break;
+        
+    case 3:
+        assert(ev == &et[1][2]);
+        for (i=0; i<NTIMES; i++) {
+            assert(!libxl__ev_time_isregistered(&et[0][i]));
+            assert(!libxl__ev_time_isregistered(&et[1][i]));
+        }
+        libxl__ao_complete(egc, tao, 0);
+        return;
+
+    default:
+        abort();
+    }
+
+    seq++;
+}
diff --git a/tools/libs/light/libxl_test_timedereg.h b/tools/libs/light/libxl_test_timedereg.h
new file mode 100644 (file)
index 0000000..9547dba
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef TEST_TIMEDEREG_H
+#define TEST_TIMEDEREG_H
+
+#include <pthread.h>
+
+int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
+    LIBXL_EXTERNAL_CALLERS_ONLY;
+
+#endif /*TEST_TIMEDEREG_H*/
diff --git a/tools/libs/light/libxl_tmem.c b/tools/libs/light/libxl_tmem.c
new file mode 100644 (file)
index 0000000..a553b39
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009-2017 Citrix Ltd and other contributors
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+/* TMEM is gone. Leave some stubs here. */
+
+char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long)
+{
+    GC_INIT(ctx);
+    LOGED(ERROR, domid, "Can not get tmem list");
+    GC_FREE;
+    return NULL;
+}
+
+int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid)
+{
+    GC_INIT(ctx);
+    LOGED(ERROR, domid, "Can not freeze tmem pools");
+    GC_FREE;
+    return ERROR_FAIL;
+}
+
+int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid)
+{
+    GC_INIT(ctx);
+    LOGED(ERROR, domid, "Can not thaw tmem pools");
+    GC_FREE;
+    return ERROR_FAIL;
+}
+
+int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name, uint32_t set)
+{
+    GC_INIT(ctx);
+    LOGED(ERROR, domid, "Can not set tmem %s", name);
+    GC_FREE;
+    return ERROR_FAIL;
+}
+
+int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid,
+                           char* uuid, int auth)
+{
+    GC_INIT(ctx);
+    LOGED(ERROR, domid, "Can not set tmem shared auth");
+    GC_FREE;
+    return ERROR_FAIL;
+}
+
+int libxl_tmem_freeable(libxl_ctx *ctx)
+{
+    GC_INIT(ctx);
+    LOGE(ERROR, "Can not get tmem freeable memory");
+    GC_FREE;
+    return ERROR_FAIL;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
new file mode 100644 (file)
index 0000000..9d3f05f
--- /dev/null
@@ -0,0 +1,1224 @@
+# -*- python -*-
+#
+# Builtin libxl types
+#
+
+namespace("libxl_")
+
+libxl_defbool = Builtin("defbool", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, copy_fn=None,
+                        check_default_fn="libxl__defbool_is_default")
+libxl_domid = Builtin("domid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__uint32_parse_json",
+                      json_parse_type = "JSON_INTEGER", autogenerate_json = False, copy_fn=None)
+libxl_devid = Builtin("devid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__int_parse_json",
+                      json_parse_type = "JSON_INTEGER", autogenerate_json = False, signed = True, init_val="-1",
+                      copy_fn=None)
+libxl_uuid = Builtin("uuid", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl_uuid_is_nil",
+                     copy_fn="libxl_uuid_copy")
+libxl_mac = Builtin("mac", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl__mac_is_default",
+                    copy_fn="libxl_mac_copy")
+libxl_bitmap = Builtin("bitmap", json_parse_type="JSON_ARRAY", dispose_fn="libxl_bitmap_dispose", passby=PASS_BY_REFERENCE,
+                       check_default_fn="libxl_bitmap_is_empty", copy_fn="libxl_bitmap_copy_alloc")
+libxl_cpuid_policy_list = Builtin("cpuid_policy_list", dispose_fn="libxl_cpuid_dispose", passby=PASS_BY_REFERENCE,
+                                  json_parse_type="JSON_ARRAY", check_default_fn="libxl__cpuid_policy_is_empty",
+                                  copy_fn="libxl_cpuid_policy_list_copy")
+
+libxl_string_list = Builtin("string_list", dispose_fn="libxl_string_list_dispose", passby=PASS_BY_REFERENCE,
+                            json_parse_type="JSON_ARRAY", check_default_fn="libxl__string_list_is_empty",
+                            copy_fn="libxl_string_list_copy")
+libxl_key_value_list = Builtin("key_value_list", dispose_fn="libxl_key_value_list_dispose", passby=PASS_BY_REFERENCE,
+                               json_parse_type="JSON_MAP", check_default_fn="libxl__key_value_list_is_empty",
+                               copy_fn="libxl_key_value_list_copy")
+libxl_hwcap = Builtin("hwcap", passby=PASS_BY_REFERENCE, json_parse_type="JSON_ARRAY",
+                      check_default_fn="libxl__hwcap_is_default", copy_fn="libxl_hwcap_copy")
+libxl_ms_vm_genid = Builtin("ms_vm_genid", passby=PASS_BY_REFERENCE, check_default_fn="libxl_ms_vm_genid_is_zero",
+                            copy_fn="libxl_ms_vm_genid_copy")
+
+#
+# Specific integer types
+#
+
+MemKB = UInt(64, init_val = "LIBXL_MEMKB_DEFAULT", json_gen_fn = "libxl__uint64_gen_json")
+
+#
+# Constants / Enumerations
+#
+
+libxl_error = Enumeration("error", [
+    (-1, "NONSPECIFIC"),
+    (-2, "VERSION"),
+    (-3, "FAIL"),
+    (-4, "NI"),
+    (-5, "NOMEM"),
+    (-6, "INVAL"),
+    (-7, "BADFAIL"),
+    (-8, "GUEST_TIMEDOUT"),
+    (-9, "TIMEDOUT"),
+    (-10, "NOPARAVIRT"),
+    (-11, "NOT_READY"),
+    (-12, "OSEVENT_REG_FAIL"),
+    (-13, "BUFFERFULL"),
+    (-14, "UNKNOWN_CHILD"),
+    (-15, "LOCK_FAIL"),
+    (-16, "JSON_CONFIG_EMPTY"),
+    (-17, "DEVICE_EXISTS"),
+    (-18, "CHECKPOINT_DEVOPS_DOES_NOT_MATCH"),
+    (-19, "CHECKPOINT_DEVICE_NOT_SUPPORTED"),
+    (-20, "VNUMA_CONFIG_INVALID"),
+    (-21, "DOMAIN_NOTFOUND"),
+    (-22, "ABORTED"),
+    (-23, "NOTFOUND"),
+    (-24, "DOMAIN_DESTROYED"), # Target domain ceased to exist during op
+    (-25, "FEATURE_REMOVED"), # For functionality that has been removed
+    (-26, "PROTOCOL_ERROR_QMP"),
+    (-27, "UNKNOWN_QMP_ERROR"),
+    (-28, "QMP_GENERIC_ERROR"), # unspecified qmp error
+    (-29, "QMP_COMMAND_NOT_FOUND"), # the requested command has not been found
+    (-30, "QMP_DEVICE_NOT_ACTIVE"), # a device has failed to be become active
+    (-31, "QMP_DEVICE_NOT_FOUND"), # the requested device has not been found
+    (-32, "QEMU_API"), # QEMU's replies don't contains expected members
+    ], value_namespace = "")
+
+libxl_domain_type = Enumeration("domain_type", [
+    (-1, "INVALID"),
+    (1, "HVM"),
+    (2, "PV"),
+    (3, "PVH"),
+    ], init_val = "LIBXL_DOMAIN_TYPE_INVALID")
+
+libxl_rdm_reserve_strategy = Enumeration("rdm_reserve_strategy", [
+    (0, "ignore"),
+    (1, "host"),
+    ])
+
+libxl_rdm_reserve_policy = Enumeration("rdm_reserve_policy", [
+    (-1, "invalid"),
+    (0, "strict"),
+    (1, "relaxed"),
+    ], init_val = "LIBXL_RDM_RESERVE_POLICY_INVALID")
+
+libxl_channel_connection = Enumeration("channel_connection", [
+    (0, "UNKNOWN"),
+    (1, "PTY"),
+    (2, "SOCKET"), # a listening Unix domain socket
+    ])
+
+libxl_device_model_version = Enumeration("device_model_version", [
+    (0, "UNKNOWN"),
+    (1, "QEMU_XEN_TRADITIONAL"), # Historical qemu-xen device model (qemu-dm)
+    (2, "QEMU_XEN"),             # Upstream based qemu-xen device model
+    ])
+
+libxl_console_type = Enumeration("console_type", [
+    (0, "UNKNOWN"),
+    (1, "SERIAL"),
+    (2, "PV"),
+    (3, "VUART"),
+    ])
+
+libxl_disk_format = Enumeration("disk_format", [
+    (0, "UNKNOWN"),
+    (1, "QCOW"),
+    (2, "QCOW2"),
+    (3, "VHD"),
+    (4, "RAW"),
+    (5, "EMPTY"),
+    (6, "QED"),
+    ])
+
+libxl_disk_backend = Enumeration("disk_backend", [
+    (0, "UNKNOWN"),
+    (1, "PHY"),
+    (2, "TAP"),
+    (3, "QDISK"),
+    ])
+
+libxl_nic_type = Enumeration("nic_type", [
+    (0, "UNKNOWN"),
+    (1, "VIF_IOEMU"),
+    (2, "VIF"),
+    ])
+
+libxl_action_on_shutdown = Enumeration("action_on_shutdown", [
+    (1, "DESTROY"),
+
+    (2, "RESTART"),
+    (3, "RESTART_RENAME"),
+
+    (4, "PRESERVE"),
+
+    (5, "COREDUMP_DESTROY"),
+    (6, "COREDUMP_RESTART"),
+
+    (7, "SOFT_RESET"),
+    ], init_val = "LIBXL_ACTION_ON_SHUTDOWN_DESTROY")
+
+libxl_trigger = Enumeration("trigger", [
+    (0, "UNKNOWN"),
+    (1, "POWER"),
+    (2, "SLEEP"),
+    (3, "NMI"),
+    (4, "INIT"),
+    (5, "RESET"),
+    (6, "S3RESUME"),
+    ])
+
+libxl_tsc_mode = Enumeration("tsc_mode", [
+    (0, "default"),
+    (1, "always_emulate"),
+    (2, "native"),
+    (3, "native_paravirt"),
+    ])
+
+libxl_gfx_passthru_kind = Enumeration("gfx_passthru_kind", [
+    (0, "default"),
+    (1, "igd"),
+    ])
+
+# Consistent with the values defined for HVM_PARAM_TIMER_MODE.
+libxl_timer_mode = Enumeration("timer_mode", [
+    (-1, "unknown"),
+    (0, "delay_for_missed_ticks"),
+    (1, "no_delay_for_missed_ticks"),
+    (2, "no_missed_ticks_pending"),
+    (3, "one_missed_tick_pending"),
+    ], init_val = "LIBXL_TIMER_MODE_DEFAULT",
+       check_default_fn = "libxl__timer_mode_is_default")
+
+libxl_bios_type = Enumeration("bios_type", [
+    (0, "unknown"),
+    (1, "rombios"),
+    (2, "seabios"),
+    (3, "ovmf"),
+    ])
+
+# Consistent with values defined in domctl.h
+# Except unknown which we have made up
+libxl_scheduler = Enumeration("scheduler", [
+    (0, "unknown"),
+    (4, "sedf"),
+    (5, "credit"),
+    (6, "credit2"),
+    (7, "arinc653"),
+    (8, "rtds"),
+    (9, "null"),
+    ])
+
+# Consistent with SHUTDOWN_* in sched.h (apart from UNKNOWN)
+libxl_shutdown_reason = Enumeration("shutdown_reason", [
+    (-1, "unknown"),
+    (0, "poweroff"),
+    (1, "reboot"),
+    (2, "suspend"),
+    (3, "crash"),
+    (4, "watchdog"),
+    (5, "soft_reset"),
+    ], init_val = "LIBXL_SHUTDOWN_REASON_UNKNOWN")
+
+libxl_vga_interface_type = Enumeration("vga_interface_type", [
+    (0, "UNKNOWN"),
+    (1, "CIRRUS"),
+    (2, "STD"),
+    (3, "NONE"),
+    (4, "QXL"),
+    ], init_val = "LIBXL_VGA_INTERFACE_TYPE_UNKNOWN")
+
+libxl_vendor_device = Enumeration("vendor_device", [
+    (0, "NONE"),
+    (1, "XENSERVER"),
+    ])
+
+libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [
+    (0, "base"),
+    (1, "freq"),
+    (2, "time_ref_count"),
+    (3, "reference_tsc"),
+    (4, "hcall_remote_tlb_flush"),
+    (5, "apic_assist"),
+    (6, "crash_ctl"),
+    (7, "synic"),
+    (8, "stimer"),
+    (9, "hcall_ipi"),
+    ])
+
+libxl_hdtype = Enumeration("hdtype", [
+    (1, "IDE"),
+    (2, "AHCI"),
+    ], init_val = "LIBXL_HDTYPE_IDE")
+
+# Consistent with the values defined for migration_stream.
+libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
+    (0, "NONE"),
+    (1, "REMUS"),
+    (2, "COLO"),
+    ])
+
+libxl_vuart_type = Enumeration("vuart_type", [
+    (0, "unknown"),
+    (1, "sbsa_uart"),
+    ])
+
+libxl_vkb_backend = Enumeration("vkb_backend", [
+    (0, "UNKNOWN"),
+    (1, "QEMU"),
+    (2, "LINUX")
+    ])
+
+libxl_passthrough = Enumeration("passthrough", [
+    (0, "default"),
+    (1, "disabled"),
+    (2, "enabled"), # becomes {sync,share}_pt once defaults are evaluated
+    (3, "sync_pt"),
+    (4, "share_pt"),
+    ])
+
+#
+# Complex libxl types
+#
+
+libxl_ioport_range = Struct("ioport_range", [
+    ("first", uint32),
+    ("number", uint32),
+    ])
+
+libxl_iomem_range = Struct("iomem_range", [
+    # start host frame number to be mapped to the guest
+    ("start", uint64),
+    # number of frames to be mapped
+    ("number", uint64),
+    # guest frame number used as a start for the mapping
+    ("gfn", uint64, {'init_val': "LIBXL_INVALID_GFN"}),
+    ])
+
+libxl_vga_interface_info = Struct("vga_interface_info", [
+    ("kind",    libxl_vga_interface_type),
+    ])
+
+libxl_vnc_info = Struct("vnc_info", [
+    ("enable",        libxl_defbool),
+    # "address:port" that should be listened on
+    ("listen",        string),
+    ("passwd",        string),
+    ("display",       integer),
+    # If set then try to find an unused port
+    ("findunused",    libxl_defbool),
+    ])
+
+libxl_spice_info = Struct("spice_info", [
+    ("enable",      libxl_defbool),
+    # At least one of spice port or spicetls_post must be given
+    ("port",        integer),
+    ("tls_port",    integer),
+    # Interface to bind to
+    ("host",        string),
+    # enable client connection with no password
+    ("disable_ticketing", libxl_defbool),
+    ("passwd",      string),
+    ("agent_mouse", libxl_defbool),
+    ("vdagent",     libxl_defbool),
+    ("clipboard_sharing", libxl_defbool),
+    ("usbredirection", integer),
+    ("image_compression", string),
+    ("streaming_video", string),
+    ])
+
+libxl_sdl_info = Struct("sdl_info", [
+    ("enable",        libxl_defbool),
+    ("opengl",        libxl_defbool),
+    ("display",       string),
+    ("xauthority",    string),
+    ])
+
+libxl_dominfo = Struct("dominfo",[
+    ("uuid",        libxl_uuid),
+    ("domid",       libxl_domid),
+    ("ssidref",     uint32),
+    ("ssid_label",  string),
+    ("running",     bool),
+    ("blocked",     bool),
+    ("paused",      bool),
+    ("shutdown",    bool),
+    ("dying",       bool),
+    ("never_stop",  bool),
+
+    # Valid iff ->shutdown is true.
+    #
+    # Otherwise set to a value guaranteed not to clash with any valid
+    # LIBXL_SHUTDOWN_REASON_* constant.
+    ("shutdown_reason", libxl_shutdown_reason),
+    ("outstanding_memkb",  MemKB),
+    ("current_memkb",   MemKB),
+    ("shared_memkb", MemKB),
+    ("paged_memkb", MemKB),
+    ("max_memkb",   MemKB),
+    ("cpu_time",    uint64),
+    ("vcpu_max_id", uint32),
+    ("vcpu_online", uint32),
+    ("cpupool",     uint32),
+    ("domain_type", libxl_domain_type),
+    ], dir=DIR_OUT)
+
+libxl_cpupoolinfo = Struct("cpupoolinfo", [
+    ("poolid",      uint32),
+    ("pool_name",   string),
+    ("sched",       libxl_scheduler),
+    ("n_dom",       uint32),
+    ("cpumap",      libxl_bitmap)
+    ], dir=DIR_OUT)
+
+libxl_channelinfo = Struct("channelinfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("state", integer),
+    ("evtch", integer),
+    ("rref", integer),
+    ("u", KeyedUnion(None, libxl_channel_connection, "connection",
+           [("unknown", None),
+            ("pty", Struct(None, [("path", string),])),
+            ("socket", None),
+           ])),
+    ], dir=DIR_OUT)
+
+libxl_vminfo = Struct("vminfo", [
+    ("uuid", libxl_uuid),
+    ("domid", libxl_domid),
+    ], dir=DIR_OUT)
+
+libxl_version_info = Struct("version_info", [
+    ("xen_version_major", integer),
+    ("xen_version_minor", integer),
+    ("xen_version_extra", string),
+    ("compiler",          string),
+    ("compile_by",        string),
+    ("compile_domain",    string),
+    ("compile_date",      string),
+    ("capabilities",      string),
+    ("changeset",         string),
+    ("virt_start",        uint64),
+    ("pagesize",          integer),
+    ("commandline",       string),
+    ("build_id",          string),
+    ], dir=DIR_OUT)
+
+libxl_domain_create_info = Struct("domain_create_info",[
+    ("type",         libxl_domain_type),
+    ("hap",          libxl_defbool),
+    ("oos",          libxl_defbool),
+    ("ssidref",      uint32),
+    ("ssid_label",   string),
+    ("name",         string),
+    ("domid",        libxl_domid),
+    ("uuid",         libxl_uuid),
+    ("xsdata",       libxl_key_value_list),
+    ("platformdata", libxl_key_value_list),
+    ("poolid",       uint32),
+    ("pool_name",    string),
+    ("run_hotplug_scripts",libxl_defbool),
+    ("driver_domain",libxl_defbool),
+    ("passthrough",  libxl_passthrough),
+    ("xend_suspend_evtchn_compat",libxl_defbool),
+    ], dir=DIR_IN)
+
+libxl_domain_restore_params = Struct("domain_restore_params", [
+    ("checkpointed_stream", integer),
+    ("stream_version", uint32, {'init_val': '1'}),
+    ("colo_proxy_script", string),
+    ("userspace_colo_proxy", libxl_defbool),
+    ])
+
+libxl_sched_params = Struct("sched_params",[
+    ("vcpuid",       integer, {'init_val': 'LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT'}),
+    ("weight",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
+    ("cap",          integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
+    ("period",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
+    ("extratime",    integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
+    ("budget",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
+    ])
+
+libxl_vcpu_sched_params = Struct("vcpu_sched_params",[
+    ("sched",        libxl_scheduler),
+    ("vcpus",        Array(libxl_sched_params, "num_vcpus")),
+    ])
+
+libxl_domain_sched_params = Struct("domain_sched_params",[
+    ("sched",        libxl_scheduler),
+    ("weight",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
+    ("cap",          integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
+    ("period",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
+    ("budget",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
+    ("extratime",    integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
+
+    # The following three parameters ('slice' and 'latency') are deprecated,
+    # and will have no effect if used, since the SEDF scheduler has been removed.
+    # Note that 'period' and 'extratime' was an SDF parameter too, but it is still effective
+    # as they are now used (together with 'budget') by the RTDS scheduler.
+    ("slice",        integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT'}),
+    ("latency",      integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT'}),
+    ])
+
+libxl_vnode_info = Struct("vnode_info", [
+    ("memkb", MemKB),
+    ("distances", Array(uint32, "num_distances")), # distances from this node to other nodes
+    ("pnode", uint32), # physical node of this node
+    ("vcpus", libxl_bitmap), # vcpus in this node
+    ])
+
+libxl_gic_version = Enumeration("gic_version", [
+    (0, "DEFAULT"),
+    (0x20, "v2"),
+    (0x30, "v3")
+    ], init_val = "LIBXL_GIC_VERSION_DEFAULT")
+
+libxl_tee_type = Enumeration("tee_type", [
+    (0, "none"),
+    (1, "optee")
+    ], init_val = "LIBXL_TEE_TYPE_NONE")
+
+libxl_rdm_reserve = Struct("rdm_reserve", [
+    ("strategy",    libxl_rdm_reserve_strategy),
+    ("policy",      libxl_rdm_reserve_policy),
+    ])
+
+# Consistent with the values defined for HVM_PARAM_ALTP2M
+libxl_altp2m_mode = Enumeration("altp2m_mode", [
+    (0, "disabled"),
+    (1, "mixed"),
+    (2, "external"),
+    (3, "limited"),
+    ], init_val = "LIBXL_ALTP2M_MODE_DISABLED")
+
+libxl_domain_build_info = Struct("domain_build_info",[
+    ("max_vcpus",       integer),
+    ("avail_vcpus",     libxl_bitmap),
+    ("cpumap",          libxl_bitmap),
+    ("nodemap",         libxl_bitmap),
+    ("vcpu_hard_affinity", Array(libxl_bitmap, "num_vcpu_hard_affinity")),
+    ("vcpu_soft_affinity", Array(libxl_bitmap, "num_vcpu_soft_affinity")),
+    ("numa_placement",  libxl_defbool),
+    ("tsc_mode",        libxl_tsc_mode),
+    ("max_memkb",       MemKB),
+    ("target_memkb",    MemKB),
+    ("video_memkb",     MemKB),
+    ("shadow_memkb",    MemKB),
+    ("iommu_memkb",     MemKB),
+    ("rtc_timeoffset",  uint32),
+    ("exec_ssidref",    uint32),
+    ("exec_ssid_label", string),
+    ("localtime",       libxl_defbool),
+    ("disable_migrate", libxl_defbool),
+    ("cpuid",           libxl_cpuid_policy_list),
+    ("blkdev_start",    string),
+
+    ("vnuma_nodes", Array(libxl_vnode_info, "num_vnuma_nodes")),
+
+    ("max_grant_frames",    uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
+    ("max_maptrack_frames", uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
+    
+    ("device_model_version", libxl_device_model_version),
+    ("device_model_stubdomain", libxl_defbool),
+    ("stubdomain_memkb",   MemKB),
+    ("stubdomain_kernel",  string),
+    ("stubdomain_ramdisk", string),
+    # if you set device_model you must set device_model_version too
+    ("device_model",     string),
+    ("device_model_ssidref", uint32),
+    ("device_model_ssid_label", string),
+    ("device_model_user", string),
+
+    # extra parameters pass directly to qemu, NULL terminated
+    ("extra",            libxl_string_list),
+    # extra parameters pass directly to qemu for PV guest, NULL terminated
+    ("extra_pv",         libxl_string_list),
+    # extra parameters pass directly to qemu for HVM guest, NULL terminated
+    ("extra_hvm",        libxl_string_list),
+    #  parameters for all type of scheduler
+    ("sched_params",     libxl_domain_sched_params),
+
+    ("ioports",          Array(libxl_ioport_range, "num_ioports")),
+    ("irqs",             Array(uint32, "num_irqs")),
+    ("iomem",            Array(libxl_iomem_range, "num_iomem")),
+    ("claim_mode",          libxl_defbool),
+    ("event_channels",   uint32),
+    ("kernel",           string),
+    ("cmdline",          string),
+    ("ramdisk",          string),
+    # Given the complexity of verifying the validity of a device tree,
+    # libxl doesn't do any security check on it. It's the responsibility
+    # of the caller to provide only trusted device tree.
+    # Note that the partial device tree should avoid to use the phandle
+    # 65000 which is reserved by the toolstack.
+    ("device_tree",      string),
+    ("acpi",             libxl_defbool),
+    ("bootloader",       string),
+    ("bootloader_args",  libxl_string_list),
+    ("timer_mode",       libxl_timer_mode),
+    ("nested_hvm",       libxl_defbool),
+    ("apic",             libxl_defbool),
+    ("dm_restrict",      libxl_defbool),
+    ("tee",              libxl_tee_type),
+    ("u", KeyedUnion(None, libxl_domain_type, "type",
+                [("hvm", Struct(None, [("firmware",         string),
+                                       ("bios",             libxl_bios_type),
+                                       ("pae",              libxl_defbool),
+                                       ("apic",             libxl_defbool, {'deprecated_by': 'apic'}),
+                                       # The following acpi field is deprecated.
+                                       # Please use the unified acpi field above
+                                       # which works for both x86 and ARM.
+                                       ("acpi",             libxl_defbool),
+                                       ("acpi_s3",          libxl_defbool),
+                                       ("acpi_s4",          libxl_defbool),
+                                       ("acpi_laptop_slate",libxl_defbool),
+                                       ("nx",               libxl_defbool),
+                                       ("viridian",         libxl_defbool),
+                                       ("viridian_enable",  libxl_bitmap),
+                                       ("viridian_disable", libxl_bitmap),
+                                       ("timeoffset",       string),
+                                       ("hpet",             libxl_defbool),
+                                       ("vpt_align",        libxl_defbool),
+                                       ("mmio_hole_memkb",  MemKB),
+                                       ("timer_mode",       libxl_timer_mode, {'deprecated_by': 'timer_mode'}),
+                                       ("nested_hvm",       libxl_defbool, {'deprecated_by': 'nested_hvm'}),
+                                       # The u.hvm.altp2m field is used solely
+                                       # for x86 HVM guests and is maintained
+                                       # for legacy purposes.
+                                       ("altp2m",           libxl_defbool),
+                                       ("system_firmware",  string),
+                                       ("smbios_firmware",  string),
+                                       ("acpi_firmware",    string),
+                                       ("hdtype",           libxl_hdtype),
+                                       ("nographic",        libxl_defbool),
+                                       ("vga",              libxl_vga_interface_info),
+                                       ("vnc",              libxl_vnc_info),
+                                       # keyboard layout, default is en-us keyboard
+                                       ("keymap",           string),
+                                       ("sdl",              libxl_sdl_info),
+                                       ("spice",            libxl_spice_info),
+                                       
+                                       ("gfx_passthru",     libxl_defbool),
+                                       ("gfx_passthru_kind", libxl_gfx_passthru_kind),
+                                       
+                                       ("serial",           string),
+                                       ("boot",             string),
+                                       ("usb",              libxl_defbool),
+                                       ("usbversion",       integer),
+                                       # usbdevice:
+                                       # - "tablet" for absolute mouse,
+                                       # - "mouse" for PS/2 protocol relative mouse
+                                       ("usbdevice",        string),
+                                       ("vkb_device",       libxl_defbool),
+                                       ("soundhw",          string),
+                                       ("xen_platform_pci", libxl_defbool),
+                                       ("usbdevice_list",   libxl_string_list),
+                                       ("vendor_device",    libxl_vendor_device),
+                                       # See libxl_ms_vm_genid_generate()
+                                       ("ms_vm_genid",      libxl_ms_vm_genid),
+                                       ("serial_list",      libxl_string_list),
+                                       ("rdm", libxl_rdm_reserve),
+                                       ("rdm_mem_boundary_memkb", MemKB),
+                                       ("mca_caps",         uint64),
+                                       ])),
+                 ("pv", Struct(None, [("kernel", string, {'deprecated_by': 'kernel'}),
+                                      ("slack_memkb", MemKB),
+                                      ("bootloader", string, {'deprecated_by': 'bootloader'}),
+                                      ("bootloader_args", libxl_string_list, {'deprecated_by': 'bootloader_args'}),
+                                      ("cmdline", string, {'deprecated_by': 'cmdline'}),
+                                      ("ramdisk", string, {'deprecated_by': 'ramdisk'}),
+                                      ("features", string, {'const': True}),
+                                      # Use host's E820 for PCI passthrough.
+                                      ("e820_host", libxl_defbool),
+                                      ])),
+                 ("pvh", Struct(None, [("pvshim", libxl_defbool),
+                                       ("pvshim_path", string),
+                                       ("pvshim_cmdline", string),
+                                       ("pvshim_extra", string), # eg "loglvl=all guest_loglvl=all apic_verbosity=debug e820-verbose"
+                                       ])),
+                 ("invalid", None),
+                 ], keyvar_init_val = "LIBXL_DOMAIN_TYPE_INVALID")),
+
+
+    ("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
+                               ("vuart", libxl_vuart_type),
+                              ])),
+    # Alternate p2m is not bound to any architecture or guest type, as it is
+    # supported by x86 HVM and ARM support is planned.
+    ("altp2m", libxl_altp2m_mode),
+
+    ], dir=DIR_IN,
+       copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
+)
+
+libxl_device_vfb = Struct("device_vfb", [
+    ("backend_domid", libxl_domid),
+    ("backend_domname",string),
+    ("devid",         libxl_devid),
+    ("vnc",           libxl_vnc_info),
+    ("sdl",           libxl_sdl_info),
+    # set keyboard layout, default is en-us keyboard
+    ("keymap",        string),
+    ])
+
+libxl_device_vkb = Struct("device_vkb", [
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+    ("devid", libxl_devid),
+    ("backend_type", libxl_vkb_backend),
+    ("unique_id", string),
+    ("feature_disable_keyboard", bool),
+    ("feature_disable_pointer", bool),
+    ("feature_abs_pointer", bool),
+    ("feature_raw_pointer", bool),
+    ("feature_multi_touch", bool),
+    ("width", uint32),
+    ("height", uint32),
+    ("multi_touch_width", uint32),
+    ("multi_touch_height", uint32),
+    ("multi_touch_num_contacts", uint32)
+    ])
+
+libxl_device_disk = Struct("device_disk", [
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+    ("pdev_path", string),
+    ("vdev", string),
+    ("backend", libxl_disk_backend),
+    ("format", libxl_disk_format),
+    ("script", string),
+    ("removable", integer),
+    ("readwrite", integer),
+    ("is_cdrom", integer),
+    ("direct_io_safe", bool),
+    ("discard_enable", libxl_defbool),
+    # Note that the COLO configuration settings should be considered unstable.
+    # They may change incompatibly in future versions of Xen.
+    ("colo_enable", libxl_defbool),
+    ("colo_restore_enable", libxl_defbool),
+    ("colo_host", string),
+    ("colo_port", integer),
+    ("colo_export", string),
+    ("active_disk", string),
+    ("hidden_disk", string)
+    ])
+
+libxl_device_nic = Struct("device_nic", [
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+    ("devid", libxl_devid),
+    ("mtu", integer),
+    ("model", string),
+    ("mac", libxl_mac),
+    ("ip", string),
+    ("bridge", string),
+    ("ifname", string),
+    ("script", string),
+    ("nictype", libxl_nic_type),
+    ("rate_bytes_per_interval", uint64),
+    ("rate_interval_usecs", uint32),
+    ("gatewaydev", string),
+    # Note that the COLO configuration settings should be considered unstable.
+    # They may change incompatibly in future versions of Xen.
+    ("coloft_forwarddev", string),
+    ("colo_sock_mirror_id", string),
+    ("colo_sock_mirror_ip", string),
+    ("colo_sock_mirror_port", string),
+    ("colo_sock_compare_pri_in_id", string),
+    ("colo_sock_compare_pri_in_ip", string),
+    ("colo_sock_compare_pri_in_port", string),
+    ("colo_sock_compare_sec_in_id", string),
+    ("colo_sock_compare_sec_in_ip", string),
+    ("colo_sock_compare_sec_in_port", string),
+    ("colo_sock_compare_notify_id", string),
+    ("colo_sock_compare_notify_ip", string),
+    ("colo_sock_compare_notify_port", string),
+    ("colo_sock_redirector0_id", string),
+    ("colo_sock_redirector0_ip", string),
+    ("colo_sock_redirector0_port", string),
+    ("colo_sock_redirector1_id", string),
+    ("colo_sock_redirector1_ip", string),
+    ("colo_sock_redirector1_port", string),
+    ("colo_sock_redirector2_id", string),
+    ("colo_sock_redirector2_ip", string),
+    ("colo_sock_redirector2_port", string),
+    ("colo_filter_mirror_queue", string),
+    ("colo_filter_mirror_outdev", string),
+    ("colo_filter_redirector0_queue", string),
+    ("colo_filter_redirector0_indev", string),
+    ("colo_filter_redirector0_outdev", string),
+    ("colo_filter_redirector1_queue", string),
+    ("colo_filter_redirector1_indev", string),
+    ("colo_filter_redirector1_outdev", string),
+    ("colo_compare_pri_in", string),
+    ("colo_compare_sec_in", string),
+    ("colo_compare_out", string),
+    ("colo_compare_notify_dev", string),
+    ("colo_sock_sec_redirector0_id", string),
+    ("colo_sock_sec_redirector0_ip", string),
+    ("colo_sock_sec_redirector0_port", string),
+    ("colo_sock_sec_redirector1_id", string),
+    ("colo_sock_sec_redirector1_ip", string),
+    ("colo_sock_sec_redirector1_port", string),
+    ("colo_filter_sec_redirector0_queue", string),
+    ("colo_filter_sec_redirector0_indev", string),
+    ("colo_filter_sec_redirector0_outdev", string),
+    ("colo_filter_sec_redirector1_queue", string),
+    ("colo_filter_sec_redirector1_indev", string),
+    ("colo_filter_sec_redirector1_outdev", string),
+    ("colo_filter_sec_rewriter0_queue", string),
+    ("colo_checkpoint_host", string),
+    ("colo_checkpoint_port", string)
+    ])
+
+libxl_device_pci = Struct("device_pci", [
+    ("func",      uint8),
+    ("dev",       uint8),
+    ("bus",       uint8),
+    ("domain",    integer),
+    ("vdevfn",    uint32),
+    ("vfunc_mask", uint32),
+    ("msitranslate", bool),
+    ("power_mgmt", bool),
+    ("permissive", bool),
+    ("seize", bool),
+    ("rdm_policy",      libxl_rdm_reserve_policy),
+    ])
+
+libxl_device_rdm = Struct("device_rdm", [
+    ("start", uint64),
+    ("size", uint64),
+    ("policy", libxl_rdm_reserve_policy),
+    ])
+
+libxl_usbctrl_type = Enumeration("usbctrl_type", [
+    (0, "AUTO"),
+    (1, "PV"),
+    (2, "DEVICEMODEL"),
+    (3, "QUSB"),
+    ])
+
+libxl_usbdev_type = Enumeration("usbdev_type", [
+    (1, "hostdev"),
+    ])
+
+libxl_device_usbctrl = Struct("device_usbctrl", [
+    ("type", libxl_usbctrl_type),
+    ("devid", libxl_devid),
+    ("version", integer),
+    ("ports", integer),
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+   ])
+
+libxl_device_usbdev = Struct("device_usbdev", [
+    ("ctrl", libxl_devid),
+    ("port", integer),
+    ("u", KeyedUnion(None, libxl_usbdev_type, "type",
+           [("hostdev", Struct(None, [
+                 ("hostbus",   uint8),
+                 ("hostaddr",  uint8)])),
+           ])),
+    ])
+
+libxl_device_dtdev = Struct("device_dtdev", [
+    ("path", string),
+    ])
+
+libxl_device_vtpm = Struct("device_vtpm", [
+    ("backend_domid",    libxl_domid),
+    ("backend_domname",  string),
+    ("devid",            libxl_devid),
+    ("uuid",             libxl_uuid),
+])
+
+libxl_device_p9 = Struct("device_p9", [
+    ("backend_domid",    libxl_domid),
+    ("backend_domname",  string),
+    ("tag",              string),
+    ("path",             string),
+    ("security_model",   string),
+    ("devid",            libxl_devid),
+])
+
+libxl_device_pvcallsif = Struct("device_pvcallsif", [
+    ("backend_domid",    libxl_domid),
+    ("backend_domname",  string),
+    ("devid",            libxl_devid),
+])
+
+libxl_device_channel = Struct("device_channel", [
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+    ("devid", libxl_devid),
+    ("name", string),
+    ("u", KeyedUnion(None, libxl_channel_connection, "connection",
+           [("unknown", None),
+            ("pty", None),
+            ("socket", Struct(None, [("path", string)])),
+           ])),
+])
+
+libxl_connector_param = Struct("connector_param", [
+    ("unique_id", string),
+    ("width", uint32),
+    ("height", uint32)
+    ])
+
+libxl_device_vdispl = Struct("device_vdispl", [
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+    ("devid", libxl_devid),
+    ("be_alloc", bool),
+    ("connectors", Array(libxl_connector_param, "num_connectors"))
+    ])
+
+libxl_vsnd_pcm_format = Enumeration("vsnd_pcm_format", [
+    (1,  "S8"),
+    (2,  "U8"),
+    (3,  "S16_LE"),
+    (4,  "S16_BE"),
+    (5,  "U16_LE"),
+    (6,  "U16_BE"),
+    (7,  "S24_LE"),
+    (8,  "S24_BE"),
+    (9,  "U24_LE"),
+    (10, "U24_BE"),
+    (11, "S32_LE"),
+    (12, "S32_BE"),
+    (13, "U32_LE"),
+    (14, "U32_BE"),
+    (15, "F32_LE"),
+    (16, "F32_BE"),
+    (17, "F64_LE"),
+    (18, "F64_BE"),
+    (19, "IEC958_SUBFRAME_LE"),
+    (20, "IEC958_SUBFRAME_BE"),
+    (21, "MU_LAW"),
+    (22, "A_LAW"),
+    (23, "IMA_ADPCM"),
+    (24, "MPEG"),
+    (25, "GSM")
+    ])
+
+libxl_vsnd_params = Struct("vsnd_params", [
+    ("sample_rates", Array(uint32, "num_sample_rates")),
+    ("sample_formats", Array(libxl_vsnd_pcm_format, "num_sample_formats")),
+    ("channels_min", uint32),
+    ("channels_max", uint32),
+    ("buffer_size", uint32)
+    ])
+
+libxl_vsnd_stream_type = Enumeration("vsnd_stream_type", [
+    (1, "P"),
+    (2, "C")
+    ])
+
+libxl_vsnd_stream = Struct("vsnd_stream", [
+    ("unique_id", string),
+    ("type", libxl_vsnd_stream_type),
+    ("params", libxl_vsnd_params)
+    ])
+
+libxl_vsnd_pcm = Struct("vsnd_pcm", [
+    ("name", string),
+    ("params", libxl_vsnd_params),
+    ("streams", Array(libxl_vsnd_stream, "num_vsnd_streams"))
+    ])
+
+libxl_device_vsnd = Struct("device_vsnd", [
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+    ("devid", libxl_devid),
+    ("short_name", string),
+    ("long_name", string),
+    ("params", libxl_vsnd_params),
+    ("pcms", Array(libxl_vsnd_pcm, "num_vsnd_pcms"))
+    ])
+
+libxl_domain_config = Struct("domain_config", [
+    ("c_info", libxl_domain_create_info),
+    ("b_info", libxl_domain_build_info),
+
+    ("disks", Array(libxl_device_disk, "num_disks")),
+    ("nics", Array(libxl_device_nic, "num_nics")),
+    ("pcidevs", Array(libxl_device_pci, "num_pcidevs")),
+    ("rdms", Array(libxl_device_rdm, "num_rdms")),
+    ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")),
+    ("vfbs", Array(libxl_device_vfb, "num_vfbs")),
+    ("vkbs", Array(libxl_device_vkb, "num_vkbs")),
+    ("vtpms", Array(libxl_device_vtpm, "num_vtpms")),
+    ("p9s", Array(libxl_device_p9, "num_p9s")),
+    ("pvcallsifs", Array(libxl_device_pvcallsif, "num_pvcallsifs")),
+    ("vdispls", Array(libxl_device_vdispl, "num_vdispls")),
+    ("vsnds", Array(libxl_device_vsnd, "num_vsnds")),
+    # a channel manifests as a console with a name,
+    # see docs/misc/channels.txt
+    ("channels", Array(libxl_device_channel, "num_channels")),
+    ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")),
+    ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")),
+
+    ("on_poweroff", libxl_action_on_shutdown),
+    ("on_reboot", libxl_action_on_shutdown),
+    ("on_watchdog", libxl_action_on_shutdown),
+    ("on_crash", libxl_action_on_shutdown),
+    ("on_soft_reset", libxl_action_on_shutdown),
+    ], dir=DIR_IN)
+
+libxl_diskinfo = Struct("diskinfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("state", integer),
+    ("evtch", integer),
+    ("rref", integer),
+    ], dir=DIR_OUT)
+
+libxl_nicinfo = Struct("nicinfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("state", integer),
+    ("evtch", integer),
+    ("rref_tx", integer),
+    ("rref_rx", integer),
+    ], dir=DIR_OUT)
+
+libxl_vtpminfo = Struct("vtpminfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("state", integer),
+    ("evtch", integer),
+    ("rref", integer),
+    ("uuid", libxl_uuid),
+    ], dir=DIR_OUT)
+
+libxl_usbctrlinfo = Struct("usbctrlinfo", [
+    ("type", libxl_usbctrl_type),
+    ("devid", libxl_devid),
+    ("version", integer),
+    ("ports", integer),
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("state", integer),
+    ("evtch", integer),
+    ("ref_urb", integer),
+    ("ref_conn", integer),
+    ], dir=DIR_OUT)
+
+libxl_vcpuinfo = Struct("vcpuinfo", [
+    ("vcpuid", uint32),
+    ("cpu", uint32),
+    ("online", bool),
+    ("blocked", bool),
+    ("running", bool),
+    ("vcpu_time", uint64), # total vcpu time ran (ns)
+    ("cpumap", libxl_bitmap), # current hard cpu affinity
+    ("cpumap_soft", libxl_bitmap), # current soft cpu affinity
+    ], dir=DIR_OUT)
+
+libxl_physinfo = Struct("physinfo", [
+    ("threads_per_core", uint32),
+    ("cores_per_socket", uint32),
+
+    ("max_cpu_id", uint32),
+    ("nr_cpus", uint32),
+    ("cpu_khz", uint32),
+
+    ("total_pages", uint64),
+    ("free_pages", uint64),
+    ("scrub_pages", uint64),
+    ("outstanding_pages", uint64),
+    ("sharing_freed_pages", uint64),
+    ("sharing_used_frames", uint64),
+    ("max_possible_mfn", uint64),
+
+    ("nr_nodes", uint32),
+    ("hw_cap", libxl_hwcap),
+
+    ("cap_hvm", bool),
+    ("cap_pv", bool),
+    ("cap_hvm_directio", bool), # No longer HVM specific
+    ("cap_hap", bool),
+    ("cap_shadow", bool),
+    ("cap_iommu_hap_pt_share", bool),
+    ], dir=DIR_OUT)
+
+libxl_connectorinfo = Struct("connectorinfo", [
+    ("unique_id", string),
+    ("width", uint32),
+    ("height", uint32),
+    ("req_evtch", integer),
+    ("req_rref", integer),
+    ("evt_evtch", integer),
+    ("evt_rref", integer),
+    ], dir=DIR_OUT)
+
+libxl_vdisplinfo = Struct("vdisplinfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("state", integer),
+    ("be_alloc", bool),
+    ("connectors", Array(libxl_connectorinfo, "num_connectors"))
+    ], dir=DIR_OUT)
+
+libxl_streaminfo = Struct("streaminfo", [
+    ("req_evtch", integer),
+    ("req_rref", integer)
+    ])
+
+libxl_pcminfo = Struct("pcminfo", [
+    ("streams", Array(libxl_streaminfo, "num_vsnd_streams"))
+    ])
+
+libxl_vsndinfo = Struct("vsndinfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("state", integer),
+    ("pcms", Array(libxl_pcminfo, "num_vsnd_pcms"))
+    ])
+
+libxl_vkbinfo = Struct("vkbinfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("state", integer),
+    ("evtch", integer),
+    ("rref", integer)
+    ], dir=DIR_OUT)
+
+# NUMA node characteristics: size and free are how much memory it has, and how
+# much of it is free, respectively. dists is an array of distances from this
+# node to each other node.
+libxl_numainfo = Struct("numainfo", [
+    ("size", uint64),
+    ("free", uint64),
+    ("dists", Array(uint32, "num_dists")),
+    ], dir=DIR_OUT)
+
+libxl_cputopology = Struct("cputopology", [
+    ("core", uint32),
+    ("socket", uint32),
+    ("node", uint32),
+    ], dir=DIR_OUT)
+
+libxl_pcitopology = Struct("pcitopology", [
+    ("seg", uint16),
+    ("bus", uint8),
+    ("devfn", uint8),
+    ("node", uint32),
+    ], dir=DIR_OUT)
+
+libxl_sched_credit_params = Struct("sched_credit_params", [
+    ("tslice_ms", integer),
+    ("ratelimit_us", integer),
+    ("vcpu_migr_delay_us", integer),
+    ], dispose_fn=None)
+
+libxl_sched_credit2_params = Struct("sched_credit2_params", [
+    ("ratelimit_us", integer),
+    ], dispose_fn=None)
+
+libxl_domain_remus_info = Struct("domain_remus_info",[
+    ("interval",             integer),
+    ("allow_unsafe",         libxl_defbool),
+    ("blackhole",            libxl_defbool),
+    ("compression",          libxl_defbool),
+    ("netbuf",               libxl_defbool),
+    ("netbufscript",         string),
+    ("diskbuf",              libxl_defbool),
+    ("colo",                 libxl_defbool),
+    ("userspace_colo_proxy", libxl_defbool)
+    ])
+
+libxl_event_type = Enumeration("event_type", [
+    (1, "DOMAIN_SHUTDOWN"),
+    (2, "DOMAIN_DEATH"),
+    (3, "DISK_EJECT"),
+    (4, "OPERATION_COMPLETE"),
+    (5, "DOMAIN_CREATE_CONSOLE_AVAILABLE"),
+    ])
+
+libxl_ev_user = UInt(64)
+
+libxl_ev_link = Builtin("ev_link", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, private=True)
+
+libxl_event = Struct("event",[
+    ("link",     libxl_ev_link),
+     # for use by libxl; caller may use this once the event has been
+     #   returned by libxl_event_{check,wait}
+    ("domid",    libxl_domid),
+    ("domuuid",  libxl_uuid),
+    ("for_user", libxl_ev_user),
+    ("u", KeyedUnion(None, libxl_event_type, "type",
+          [("domain_shutdown", Struct(None, [
+                                             ("shutdown_reason", uint8),
+                                      ])),
+           ("domain_death", None),
+           ("disk_eject", Struct(None, [
+                                        ("vdev", string),
+                                        ("disk", libxl_device_disk),
+                                 ])),
+           ("operation_complete", Struct(None, [
+                                        ("rc", integer),
+                                 ])),
+           ("domain_create_console_available", None),
+           ]))])
+
+libxl_psr_cmt_type = Enumeration("psr_cmt_type", [
+    (1, "CACHE_OCCUPANCY"),
+    (2, "TOTAL_MEM_COUNT"),
+    (3, "LOCAL_MEM_COUNT"),
+    ])
+
+libxl_psr_cbm_type = Enumeration("psr_cbm_type", [
+    (0, "UNKNOWN"),
+    (1, "L3_CBM"),
+    (2, "L3_CBM_CODE"),
+    (3, "L3_CBM_DATA"),
+    (4, "L2_CBM"),
+    (5, "MBA_THRTL"),
+    ])
+
+libxl_psr_cat_info = Struct("psr_cat_info", [
+    ("id", uint32),
+    ("cos_max", uint32),
+    ("cbm_len", uint32),
+    ("cdp_enabled", bool),
+    ])
+
+libxl_psr_feat_type = Enumeration("psr_feat_type", [
+    (1, "CAT"),
+    (2, "MBA"),
+    ])
+
+libxl_psr_hw_info = Struct("psr_hw_info", [
+    ("id", uint32),
+    ("u", KeyedUnion(None, libxl_psr_feat_type, "type",
+          [("cat", Struct(None, [
+                                    ("cos_max",     uint32),
+                                    ("cbm_len",     uint32),
+                                    ("cdp_enabled", bool),
+                               ])),
+           ("mba", Struct(None, [
+                                    ("cos_max",     uint32),
+                                    ("thrtl_max",   uint32),
+                                    ("linear",      bool),
+                               ])),
+          ]))
+    ], dir=DIR_OUT)
diff --git a/tools/libs/light/libxl_types_internal.idl b/tools/libs/light/libxl_types_internal.idl
new file mode 100644 (file)
index 0000000..3593e21
--- /dev/null
@@ -0,0 +1,57 @@
+namespace("libxl__")
+hidden(True)
+
+libxl_domid = Builtin("domid", namespace="libxl_", json_gen_fn = "yajl_gen_integer",
+                     json_parse_fn = "libxl__uint32_parse_json", json_parse_type = "JSON_INTEGER",
+                     autogenerate_json = False, copy_fn = None)
+
+libxl__qmp_message_type = Enumeration("qmp_message_type", [
+    (1, "QMP"),
+    (2, "return"),
+    (3, "error"),
+    (4, "event"),
+    (5, "invalid"),
+    ])
+
+# Consider adding to QEMU_BACKEND in libxl_internal.h
+libxl__device_kind = Enumeration("device_kind", [
+    (0, "NONE"),
+    (1, "VIF"),
+    (2, "VBD"),
+    (3, "QDISK"),
+    (4, "PCI"),
+    (5, "VFB"),
+    (6, "VKBD"),
+    (7, "CONSOLE"),
+    (8, "VTPM"),
+    (9, "VUSB"),
+    (10, "QUSB"),
+    (11, "9PFS"),
+    (12, "VDISPL"),
+    (13, "VUART"),
+    (14, "PVCALLS"),
+    (15, "VSND"),
+    (16, "VINPUT"),
+    ])
+
+libxl__console_backend = Enumeration("console_backend", [
+    (1, "XENCONSOLED"),
+    (2, "IOEMU"),
+    ])
+
+libxl__device_console = Struct("device_console", [
+    ("backend_domid", libxl_domid),
+    ("devid", integer),
+    ("consback", libxl__console_backend),
+    ("output", string),
+    # A regular console has no name.
+    # A console with a name is called a 'channel', see docs/misc/channels.txt
+    ("name", string),
+    ("connection", string),
+    ("path", string),
+    ])
+
+libxl__device_action = Enumeration("device_action", [
+    (1, "ADD"),
+    (2, "REMOVE"),
+    ])
diff --git a/tools/libs/light/libxl_usb.c b/tools/libs/light/libxl_usb.c
new file mode 100644 (file)
index 0000000..171bb04
--- /dev/null
@@ -0,0 +1,2158 @@
+/*
+ * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ * Author Chunyan Liu <cyliu@suse.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+#include <inttypes.h>
+#include <xen/io/usbif.h>
+
+#define USBBACK_INFO_PATH "/libxl/usbback"
+
+#define USBHUB_CLASS_CODE 9
+
+static int usbback_is_loaded(libxl__gc *gc)
+{
+    int r;
+    struct stat st;
+
+    r = lstat(SYSFS_USBBACK_DRIVER, &st);
+
+    if (r == 0)
+        return 1;
+    if (r < 0 && errno == ENOENT)
+        return 0;
+    LOGE(ERROR, "Accessing %s", SYSFS_USBBACK_DRIVER);
+    return ERROR_FAIL;
+}
+
+static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid,
+                                            libxl_device_usbctrl *usbctrl,
+                                            bool hotplug)
+{
+    int rc;
+    libxl_domain_type domtype = libxl__domain_type(gc, domid);
+
+    if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) {
+        if (domtype != LIBXL_DOMAIN_TYPE_HVM) {
+            rc = usbback_is_loaded(gc);
+            if (rc < 0)
+                goto out;
+            usbctrl->type = rc ? LIBXL_USBCTRL_TYPE_PV
+                               : LIBXL_USBCTRL_TYPE_QUSB;
+        } else {
+            /* FIXME: See if we can detect PV frontend */
+            usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL;
+        }
+    }
+
+    switch (usbctrl->type) {
+    case LIBXL_USBCTRL_TYPE_PV:
+    case LIBXL_USBCTRL_TYPE_QUSB:
+        if (!usbctrl->version)
+            usbctrl->version = 2;
+        if (usbctrl->version < 1 || usbctrl->version > 2) {
+            LOG(ERROR,
+                "USB version for paravirtualized devices must be 1 or 2");
+            rc = ERROR_INVAL;
+            goto out;
+        }
+        if (!usbctrl->ports)
+            usbctrl->ports = 8;
+        if (usbctrl->ports < 1 || usbctrl->ports > USBIF_MAX_PORTNR) {
+            LOG(ERROR, "Number of ports for USB controller is limited to %u",
+                USBIF_MAX_PORTNR);
+            rc = ERROR_INVAL;
+            goto out;
+        }
+        break;
+    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+        if (!usbctrl->version)
+            usbctrl->version = 2;
+        switch (usbctrl->version) {
+        case 1:
+            /* uhci controller in qemu has fixed number of ports. */
+            if (usbctrl->ports && usbctrl->ports != 2) {
+                LOG(ERROR,
+                    "Number of ports for USB controller of version 1 is always 2");
+                rc = ERROR_INVAL;
+                goto out;
+            }
+            usbctrl->ports = 2;
+            break;
+        case 2:
+            /* ehci controller in qemu has fixed number of ports. */
+            if (usbctrl->ports && usbctrl->ports != 6) {
+                LOG(ERROR,
+                    "Number of ports for USB controller of version 2 is always 6");
+                rc = ERROR_INVAL;
+                goto out;
+            }
+            usbctrl->ports = 6;
+            break;
+        case 3:
+            if (!usbctrl->ports)
+                usbctrl->ports = 8;
+            /* xhci controller in qemu supports up to 15 ports. */
+            if (usbctrl->ports > 15) {
+                LOG(ERROR,
+                    "Number of ports for USB controller of version 3 is limited to 15");
+                rc = ERROR_INVAL;
+                goto out;
+            }
+            break;
+        default:
+            LOG(ERROR, "Illegal USB version");
+            rc = ERROR_INVAL;
+            goto out;
+        }
+        break;
+    default:
+        break;
+    }
+
+    rc = libxl__resolve_domid(gc, usbctrl->backend_domname,
+                              &usbctrl->backend_domid);
+
+out:
+    return rc;
+}
+
+static int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid,
+                                      libxl_device_usbctrl *usbctrl,
+                                      libxl__device *device)
+{
+    device->backend_devid   = usbctrl->devid;
+    device->backend_domid   = usbctrl->backend_domid;
+    switch (usbctrl->type) {
+    case LIBXL_USBCTRL_TYPE_PV:
+        device->backend_kind = LIBXL__DEVICE_KIND_VUSB;
+        break;
+    case LIBXL_USBCTRL_TYPE_QUSB:
+        device->backend_kind = LIBXL__DEVICE_KIND_QUSB;
+        break;
+    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+        device->backend_kind = LIBXL__DEVICE_KIND_NONE;
+        break;
+    default:
+        abort(); /* can't really happen. */
+    }
+    device->devid           = usbctrl->devid;
+    device->domid           = domid;
+    device->kind            = LIBXL__DEVICE_KIND_VUSB;
+
+    return 0;
+}
+
+static const char *vusb_be_from_xs_libxl_type(libxl__gc *gc,
+                                              const char *libxl_path,
+                                              libxl_usbctrl_type type)
+{
+    const char *be_path = NULL, *tmp;
+    int r;
+
+    if (type == LIBXL_USBCTRL_TYPE_AUTO) {
+        r = libxl__xs_read_checked(gc, XBT_NULL,
+                                   GCSPRINTF("%s/type", libxl_path), &tmp);
+        if (r || libxl_usbctrl_type_from_string(tmp, &type))
+            goto out;
+    }
+
+    if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+        be_path = libxl_path;
+        goto out;
+    }
+
+    r = libxl__xs_read_checked(gc, XBT_NULL,
+                               GCSPRINTF("%s/backend", libxl_path),
+                               &be_path);
+    if (r)
+        be_path = NULL;
+
+out:
+    return be_path;
+}
+
+/* Add usbctrl information to xenstore.
+ *
+ * Adding a usb controller will add a new 'qusb' or 'vusb' device in xenstore,
+ * and add corresponding frontend, backend information to it. According to
+ * "update_json", decide whether to update json config file.
+ */
+static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t domid,
+                                              libxl_device_usbctrl *usbctrl,
+                                              bool update_json)
+{
+    libxl__device *device;
+    flexarray_t *front = NULL;
+    flexarray_t *back;
+    xs_transaction_t t = XBT_NULL;
+    int i, rc;
+    libxl_domain_config d_config;
+    libxl_device_usbctrl usbctrl_saved;
+    libxl__flock *lock = NULL;
+
+    libxl_domain_config_init(&d_config);
+    libxl_device_usbctrl_init(&usbctrl_saved);
+    libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl);
+
+    GCNEW(device);
+    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
+    if (rc) goto out;
+
+    back = flexarray_make(gc, 12, 1);
+
+    if (device->backend_kind != LIBXL__DEVICE_KIND_NONE) {
+        front = flexarray_make(gc, 4, 1);
+
+        flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+        flexarray_append_pair(back, "online", "1");
+        flexarray_append_pair(back, "state",
+                              GCSPRINTF("%d", XenbusStateInitialising));
+        flexarray_append_pair(front, "backend-id",
+                              GCSPRINTF("%d", usbctrl->backend_domid));
+        flexarray_append_pair(front, "state",
+                              GCSPRINTF("%d", XenbusStateInitialising));
+    }
+
+    flexarray_append_pair(back, "type",
+                          (char *)libxl_usbctrl_type_to_string(usbctrl->type));
+    flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version));
+    flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports));
+    flexarray_append_pair(back, "port", "");
+    for (i = 0; i < usbctrl->ports; i++)
+        flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), "");
+
+    if (update_json) {
+        lock = libxl__lock_domain_userdata(gc, domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+            goto out;
+        }
+
+        rc = libxl__get_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        device_add_domain_config(gc, &d_config, &libxl__usbctrl_devtype,
+                                 &usbctrl_saved);
+
+        rc = libxl__dm_check_start(gc, &d_config, domid);
+        if (rc) goto out;
+
+        if (usbctrl->type == LIBXL_USBCTRL_TYPE_QUSB) {
+            if (!libxl__query_qemu_backend(gc, domid, usbctrl->backend_domid,
+                                           "qusb", false)) {
+                LOGD(ERROR, domid, "backend type not supported by device model");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+    }
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        rc = libxl__device_exists(gc, t, device);
+        if (rc < 0) goto out;
+        if (rc == 1) {
+            /* already exists in xenstore */
+            LOGD(ERROR, domid, "device already exists in xenstore");
+            rc = ERROR_DEVICE_EXISTS;
+            goto out;
+        }
+
+        if (update_json) {
+            rc = libxl__set_domain_configuration(gc, domid, &d_config);
+            if (rc) goto out;
+        }
+
+        libxl__device_generic_add(gc, t, device,
+                                  libxl__xs_kvs_of_flexarray(gc, back),
+                                  libxl__xs_kvs_of_flexarray(gc, front),
+                                  NULL);
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    if (lock) libxl__unlock_file(lock);
+    libxl_device_usbctrl_dispose(&usbctrl_saved);
+    libxl_domain_config_dispose(&d_config);
+    return rc;
+}
+
+static const char *vusb_be_from_xs_libxl(libxl__gc *gc, const char *libxl_path)
+{
+    return vusb_be_from_xs_libxl_type(gc, libxl_path, LIBXL_USBCTRL_TYPE_AUTO);
+}
+
+static void libxl__device_usbctrl_del_xenstore(libxl__gc *gc, uint32_t domid,
+                                               libxl_device_usbctrl *usbctrl)
+{
+    const char *libxl_path, *be_path;
+    xs_transaction_t t = XBT_NULL;
+    int rc;
+
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
+                                                 LIBXL__DEVICE_KIND_VUSB);
+    be_path = vusb_be_from_xs_libxl_type(gc, libxl_path, usbctrl->type);
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        libxl__xs_path_cleanup(gc, t, be_path);
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    return;
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+}
+
+static char *pvusb_get_device_type(libxl_usbctrl_type type)
+{
+    switch (type) {
+    case LIBXL_USBCTRL_TYPE_PV:
+        return "vusb";
+    case LIBXL_USBCTRL_TYPE_QUSB:
+        return "qusb";
+    default:
+        return NULL;
+    }
+}
+
+/* Send qmp commands to create a usb controller in qemu.
+ *
+ * Depending on the speed (usbctrl->version) we create:
+ * - piix3-usb-uhci (version=1), always 2 ports
+ * - usb-ehci       (version=2), always 6 ports
+ * - nec-usb-xhci   (version=3), up to 15 ports
+ */
+static int libxl__device_usbctrl_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
+                                         libxl_device_usbctrl *usbctrl)
+{
+    EGC_GC;
+    libxl__json_object *qmp_args = NULL;
+
+    switch (usbctrl->version) {
+    case 1:
+        libxl__qmp_param_add_string(gc, &qmp_args,
+                                    "driver", "piix3-usb-uhci");
+        break;
+    case 2:
+        libxl__qmp_param_add_string(gc, &qmp_args,
+                                    "driver", "usb-ehci");
+        break;
+    case 3:
+        libxl__qmp_param_add_string(gc, &qmp_args,
+                                    "driver", "nec-usb-xhci");
+        libxl__qmp_param_add_string(gc, &qmp_args, "p2",
+                                    GCSPRINTF("%d", usbctrl->ports));
+        libxl__qmp_param_add_string(gc, &qmp_args, "p3",
+                                    GCSPRINTF("%d", usbctrl->ports));
+        break;
+    default:
+        abort(); /* Should not be possible. */
+    }
+
+    libxl__qmp_param_add_string(gc, &qmp_args, "id",
+                                GCSPRINTF("xenusb-%d", usbctrl->devid));
+
+    return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
+}
+
+/* Send qmp commands to delete a usb controller in qemu.  */
+static int libxl__device_usbctrl_del_hvm(libxl__egc *egc,
+                                         libxl__ev_qmp *qmp,
+                                         int devid)
+{
+    EGC_GC;
+    libxl__json_object *qmp_args = NULL;
+
+    libxl__qmp_param_add_string(gc, &qmp_args,
+                                "id", GCSPRINTF("xenusb-%d", devid));
+
+    return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
+}
+
+/* Send qmp commands to create a usb device in qemu. */
+static int libxl__device_usbdev_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
+                                        libxl_device_usbdev *usbdev)
+{
+    EGC_GC;
+    libxl__json_object *qmp_args = NULL;
+
+    libxl__qmp_param_add_string(gc, &qmp_args, "id",
+        GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
+                  usbdev->u.hostdev.hostaddr));
+    libxl__qmp_param_add_string(gc, &qmp_args, "driver", "usb-host");
+    libxl__qmp_param_add_string(gc, &qmp_args, "bus",
+        GCSPRINTF("xenusb-%d.0", usbdev->ctrl));
+    libxl__qmp_param_add_string(gc, &qmp_args, "port",
+        GCSPRINTF("%d", usbdev->port));
+    libxl__qmp_param_add_string(gc, &qmp_args, "hostbus",
+        GCSPRINTF("%d", usbdev->u.hostdev.hostbus));
+    libxl__qmp_param_add_string(gc, &qmp_args, "hostaddr",
+        GCSPRINTF("%d", usbdev->u.hostdev.hostaddr));
+
+    return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
+}
+
+/* Send qmp commands to delete a usb device in qemu. */
+static int libxl__device_usbdev_del_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
+                                        libxl_device_usbdev *usbdev)
+{
+    EGC_GC;
+    libxl__json_object *qmp_args = NULL;
+
+    libxl__qmp_param_add_string(gc, &qmp_args, "id",
+        GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
+                  usbdev->u.hostdev.hostaddr));
+
+    return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(usbctrl)
+
+static void device_usbctrl_add_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
+static void device_usbctrl_add_done(libxl__egc *egc,
+    libxl__ao_device *aodev, int rc);
+
+/* AO operation to add a usb controller.
+ *
+ * Generally, it does:
+ * 1) fill in necessary usb controler information with default value
+ * 2) write usb controller frontend/backend info to xenstore, update json
+ *    config file if necessary.
+ * 3) wait for device connection. PVUSB frontend and backend driver will
+ *    probe xenstore paths and build connection between frontend and backend.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, aodev->update_json, ...
+ */
+static void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
+                                      libxl_device_usbctrl *usbctrl,
+                                      libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl__device *device;
+    int rc;
+
+    /* Store *usbctrl to be used by callbacks */
+    aodev->device_config = usbctrl;
+    aodev->device_type = &libxl__usbctrl_devtype;
+
+    rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
+                                          aodev->update_json);
+    if (rc < 0) goto out;
+
+    rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
+    if (rc) goto out;
+
+    rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
+                                            aodev->update_json);
+    if (rc) goto out;
+
+    GCNEW(device);
+    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
+    if (rc) goto outrm;
+    aodev->dev = device;
+
+    if (device->backend_kind == LIBXL__DEVICE_KIND_NONE) {
+        libxl__ev_qmp *const qmp = &aodev->qmp;
+
+        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+                                         device_usbctrl_add_timeout,
+                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
+        if (rc) goto outrm;
+
+        qmp->ao = ao;
+        qmp->domid = domid;
+        qmp->payload_fd = -1;
+        qmp->callback = device_usbctrl_add_qmp_cb;
+        rc = libxl__device_usbctrl_add_hvm(egc, qmp, usbctrl);
+        if (rc) goto outrm;
+        return;
+    }
+
+    aodev->action = LIBXL__DEVICE_ACTION_ADD;
+    libxl__wait_device_connection(egc, aodev);
+    return;
+
+outrm:
+    libxl__device_usbctrl_del_xenstore(gc, domid, usbctrl);
+out:
+    device_usbctrl_add_done(egc, aodev, rc);
+}
+
+static void device_usbctrl_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
+                                       const struct timeval *requested_abs,
+                                       int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+    if (rc == ERROR_TIMEDOUT)
+        LOGD(ERROR, aodev->dev->domid, "Adding usbctrl to QEMU timed out");
+    device_usbctrl_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
+                                      libxl__ev_qmp *qmp,
+                                      const libxl__json_object *r,
+                                      int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+    libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+    if (rc)
+        libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
+
+    device_usbctrl_add_done(egc, aodev, rc);
+}
+
+static void device_usbctrl_add_done(libxl__egc *egc,
+                                    libxl__ao_device *aodev,
+                                    int rc)
+{
+    EGC_GC;
+    libxl__ev_qmp_dispose(gc, &aodev->qmp);
+    libxl__ev_time_deregister(gc, &aodev->timeout);
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+LIBXL_DEFINE_DEVICE_ADD(usbctrl)
+static LIBXL_DEFINE_DEVICES_ADD(usbctrl)
+LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(usbctrl)
+
+static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t domid,
+                                                 libxl_devid usbctrl,
+                                                 libxl_device_usbdev **usbdevs,
+                                                 int *num);
+
+static void libxl__device_usbdev_remove(libxl__egc *egc,
+    uint32_t domid, libxl_device_usbdev *usbdev, libxl__ao_device *aodev);
+
+static void device_usbctrl_usbdevs_removed(libxl__egc *,
+    libxl__multidev *, int rc);
+static void device_usbctrl_remove_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *resp, int rc);
+static void device_usbctrl_remove_done(libxl__egc *egc,
+    libxl__ao_device *, int rc);
+
+typedef struct {
+    libxl__multidev multidev;
+    libxl__ao_device *aodev;
+} usbctrl_remove_state;
+
+/* AO function to remove a usb controller.
+ *
+ * Generally, it does:
+ * 1) check if the usb controller exists or not
+ * 2) remove all usb devices under controller
+ * 3) remove usb controller information from xenstore
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->dev, aodev->callback, ...
+ */
+void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
+                                           libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl_device_usbdev *usbdevs = NULL;
+    int num_usbdev = 0;
+    int i, rc;
+    uint32_t domid = aodev->dev->domid;
+    int usbctrl_devid = aodev->dev->devid;
+    libxl_device_usbctrl *usbctrl;
+    usbctrl_remove_state *ucrs;
+
+    GCNEW(ucrs);
+    ucrs->aodev = aodev;
+    ucrs->multidev.callback = device_usbctrl_usbdevs_removed;
+    libxl__multidev_begin(ao, &ucrs->multidev);
+
+    GCNEW(usbctrl);
+    libxl_device_usbctrl_init(usbctrl);
+    rc = libxl_devid_to_device_usbctrl(CTX, domid, usbctrl_devid,
+                                       usbctrl);
+    if (rc) goto out;
+
+    /* Store *usbctrl to be used by callbacks */
+    aodev->device_config = usbctrl;
+    aodev->device_type = &libxl__usbctrl_devtype;
+
+    /* Remove usb devices first */
+    rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid,
+                                               &usbdevs, &num_usbdev);
+    if (rc) goto out;
+
+    for (i = 0; i < num_usbdev; i++) {
+        libxl__ao_device *usbdev_aodev =
+            libxl__multidev_prepare(&ucrs->multidev);
+        usbdev_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+        libxl__device_usbdev_remove(egc, domid, &usbdevs[i], usbdev_aodev);
+    }
+
+out:
+    libxl__multidev_prepared(egc, &ucrs->multidev, rc); /* must be last */
+}
+
+static void device_usbctrl_usbdevs_removed(libxl__egc *egc,
+                                           libxl__multidev *multidev,
+                                           int rc)
+{
+    usbctrl_remove_state *ucrs =
+        CONTAINER_OF(multidev, *ucrs, multidev);
+    libxl__ao_device *aodev = ucrs->aodev;
+    STATE_AO_GC(aodev->ao);
+    libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+    if (rc) goto out;
+
+    /* Remove usbctrl */
+    if (usbctrl->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+        libxl__ev_qmp *const qmp = &aodev->qmp;
+
+        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+                                         device_usbctrl_remove_timeout,
+                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
+        if (rc) goto out;
+
+        qmp->ao = ao;
+        qmp->domid = aodev->dev->domid;
+        qmp->callback = device_usbctrl_remove_qmp_cb;
+        qmp->payload_fd = -1;
+        rc = libxl__device_usbctrl_del_hvm(egc, qmp, aodev->dev->devid);
+        if (rc) goto out;
+        return;
+    }
+
+    libxl_device_usbctrl_dispose(usbctrl);
+
+    /* Remove usbctrl */
+    libxl__initiate_device_generic_remove(egc, aodev); /* must be last */
+    return;
+out:
+    device_usbctrl_remove_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbctrl_remove_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+    if (rc == ERROR_TIMEDOUT)
+        LOGD(ERROR, aodev->dev->domid,
+             "Removing usbctrl from QEMU timed out");
+    device_usbctrl_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
+                                         libxl__ev_qmp *qmp,
+                                         const libxl__json_object *resp,
+                                         int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+    libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+    if (!rc)
+        libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
+
+    device_usbctrl_remove_done(egc, aodev, rc);
+}
+
+static void device_usbctrl_remove_done(libxl__egc *egc,
+                                       libxl__ao_device *aodev,
+                                       int rc)
+{
+    EGC_GC;
+    libxl_device_usbctrl *const usbctrl = aodev->device_config;
+
+    libxl_device_usbctrl_dispose(usbctrl);
+    libxl__ev_qmp_dispose(gc, &aodev->qmp);
+    libxl__ev_time_deregister(gc, &aodev->timeout);
+
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+static int libxl__usbctrl_from_xenstore(libxl__gc *gc,
+                                        const char *libxl_path,
+                                        libxl_devid devid,
+                                        libxl_device_usbctrl *usbctrl_r)
+{
+    int rc;
+    const char *tmp;
+    const char *be_path;
+
+#define READ_SUBPATH(path, subpath) ({                                  \
+        rc = libxl__xs_read_checked(gc, XBT_NULL,                      \
+                                     GCSPRINTF("%s/" subpath, path),    \
+                                     &tmp);                             \
+        if (rc) goto out;                                              \
+        (char *)tmp;                                                    \
+    })
+
+#define READ_SUBPATH_INT(path, subpath) ({                              \
+        rc = libxl__xs_read_checked(gc, XBT_NULL,                      \
+                                     GCSPRINTF("%s/" subpath, path),    \
+                                     &tmp);                             \
+        if (rc) goto out;                                              \
+        tmp ? atoi(tmp) : -1;                                           \
+    })
+
+    usbctrl_r->devid = devid;
+    libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
+                                   &usbctrl_r->type);
+    if (usbctrl_r->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+        be_path = libxl_path;
+        rc = libxl__get_domid(gc, &usbctrl_r->backend_domid);
+    } else {
+        be_path = READ_SUBPATH(libxl_path, "backend");
+        if (!be_path) goto out;
+        rc = libxl__backendpath_parse_domid(gc, be_path,
+                                             &usbctrl_r->backend_domid);
+    }
+    if (rc) goto out;
+    usbctrl_r->version = READ_SUBPATH_INT(be_path, "usb-ver");
+    usbctrl_r->ports = READ_SUBPATH_INT(be_path, "num-ports");
+
+#undef READ_SUBPATH
+#undef READ_SUBPATH_INT
+out:
+    if (rc)
+        libxl_device_usbctrl_dispose(usbctrl_r);
+    return rc;
+}
+
+int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                 const libxl_device_usbctrl *usbctrl,
+                                 libxl_usbctrlinfo *usbctrlinfo)
+{
+    GC_INIT(ctx);
+    const char *fe_path, *be_path, *tmp;
+    const char *libxl_path;
+    int rc;
+
+    usbctrlinfo->devid = usbctrl->devid;
+
+#define READ_SUBPATH(path, subpath) ({                                  \
+        rc = libxl__xs_read_mandatory(gc, XBT_NULL,                     \
+                                      GCSPRINTF("%s/" subpath, path),   \
+                                      &tmp);                            \
+        if (rc) goto out;                                               \
+        (char *)tmp;                                                    \
+    })
+
+#define READ_SUBPATH_INT(path, subpath) ({                              \
+        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \
+                                    GCSPRINTF("%s/" subpath, path),     \
+                                    &tmp);                              \
+        if (rc) goto out;                                               \
+        tmp ? atoi(tmp) : -1;                                           \
+    })
+
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
+                                                 LIBXL__DEVICE_KIND_VUSB);
+    libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
+                                   &usbctrlinfo->type);
+
+    if (usbctrlinfo->type != LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
+        fe_path = libxl__domain_device_frontend_path(gc, domid, usbctrl->devid,
+                                                     LIBXL__DEVICE_KIND_VUSB);
+        be_path = READ_SUBPATH(libxl_path, "backend");
+        usbctrlinfo->backend = libxl__strdup(NOGC, be_path);
+        rc = libxl__backendpath_parse_domid(gc, be_path,
+                                            &usbctrlinfo->backend_id);
+        if (rc) goto out;
+        usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state");
+        usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel");
+        usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
+        usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
+        usbctrlinfo->frontend = libxl__strdup(NOGC, fe_path);
+        usbctrlinfo->frontend_id = domid;
+        usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports");
+        usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver");
+    } else {
+        usbctrlinfo->ports = READ_SUBPATH_INT(libxl_path, "num-ports");
+        usbctrlinfo->version = READ_SUBPATH_INT(libxl_path, "usb-ver");
+        rc = libxl__get_domid(gc, &usbctrlinfo->backend_id);
+        if (rc) goto out;
+    }
+
+#undef READ_SUBPATH
+#undef READ_SUBPATH_INT
+
+    rc = 0;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+
+static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr)
+{
+    DIR *dir;
+    char *busid = NULL;
+    struct dirent *de;
+
+    /* invalid hostbus or hostaddr */
+    if (bus < 1 || addr < 1)
+        return NULL;
+
+    dir = opendir(SYSFS_USB_DEV);
+    if (!dir) {
+        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
+        return NULL;
+    }
+
+    for (;;) {
+        char *filename;
+        void *buf;
+        int busnum = -1;
+        int devnum = -1;
+
+        errno = 0;
+        de = readdir(dir);
+        if (!de && errno) {
+            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
+            break;
+        }
+        if (!de)
+            break;
+
+        if (!strcmp(de->d_name, ".") ||
+            !strcmp(de->d_name, ".."))
+            continue;
+
+        filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", de->d_name);
+        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+            devnum = atoi(buf);
+
+        filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", de->d_name);
+        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+            busnum = atoi(buf);
+
+        if (bus == busnum && addr == devnum) {
+            busid = libxl__strdup(gc, de->d_name);
+            break;
+        }
+    }
+
+    closedir(dir);
+    return busid;
+}
+
+static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid,
+                                     uint8_t *bus, uint8_t *addr)
+{
+    char *filename;
+    void *buf;
+
+    filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", busid);
+    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+        *bus = atoi(buf);
+    else
+        return ERROR_FAIL;
+
+    filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", busid);
+    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+        *addr = atoi(buf);
+    else
+        return ERROR_FAIL;
+
+    return 0;
+}
+
+static int get_assigned_devices(libxl__gc *gc,
+                                libxl_device_usbdev **list, int *num)
+{
+    char **domlist;
+    unsigned int ndom = 0;
+    int i, j, k;
+    int rc;
+
+    *list = NULL;
+    *num = 0;
+
+    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom);
+    for (i = 0; i < ndom; i++) {
+        char *libxl_vusbs_path;
+        char **usbctrls;
+        unsigned int nc = 0;
+        uint32_t domid = atoi(domlist[i]);
+
+        libxl_vusbs_path = GCSPRINTF("%s/device/%s",
+                                     libxl__xs_libxl_path(gc, domid),
+                                     libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
+        usbctrls = libxl__xs_directory(gc, XBT_NULL,
+                                       libxl_vusbs_path, &nc);
+
+        for (j = 0; j < nc; j++) {
+            libxl_device_usbdev *tmp = NULL;
+            int nd = 0;
+
+            rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
+                                                       atoi(usbctrls[j]),
+                                                       &tmp, &nd);
+            if (rc) goto out;
+
+            if (!nd) continue;
+
+            GCREALLOC_ARRAY(*list, *num + nd);
+            for (k = 0; k < nd; k++) {
+                libxl_device_usbdev_copy(CTX, *list + *num, tmp + k);
+                (*num)++;
+            }
+        }
+    }
+
+    return 0;
+
+out:
+    LOG(ERROR, "fail to get assigned devices");
+    return rc;
+}
+
+static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num,
+                               libxl_device_usbdev *usbdev)
+{
+    int i;
+
+    for (i = 0; i < num; i++) {
+        if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus &&
+            usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr)
+            return true;
+    }
+
+    return false;
+}
+
+/* check if USB device type is assignable */
+static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev *usbdev)
+{
+    int classcode;
+    char *filename;
+    void *buf = NULL;
+    char *busid = NULL;
+
+    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+                                    usbdev->u.hostdev.hostaddr);
+    if (!busid) return false;
+
+    filename = GCSPRINTF(SYSFS_USB_DEV "/%s/bDeviceClass", busid);
+    if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+        return false;
+
+    classcode = atoi(buf);
+    return classcode != USBHUB_CLASS_CODE;
+}
+
+/* get usb devices under certain usb controller */
+static int
+libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc,
+                                      uint32_t domid,
+                                      libxl_devid usbctrl,
+                                      libxl_device_usbdev **usbdevs,
+                                      int *num)
+{
+    const char *libxl_path, *be_path, *num_devs;
+    int n, i, rc;
+
+    *usbdevs = NULL;
+    *num = 0;
+
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl,
+                                                 LIBXL__DEVICE_KIND_VUSB);
+
+    be_path = vusb_be_from_xs_libxl(gc, libxl_path);
+    if (!be_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/num-ports", be_path),
+                                &num_devs);
+    if (rc) goto out;
+
+    n = num_devs ? atoi(num_devs) : 0;
+
+    for (i = 0; i < n; i++) {
+        const char *busid;
+        libxl_device_usbdev *usbdev;
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/port/%d", be_path, i + 1),
+                                    &busid);
+        if (rc) goto out;
+
+        if (busid && strcmp(busid, "")) {
+            GCREALLOC_ARRAY(*usbdevs, *num + 1);
+            usbdev = *usbdevs + *num;
+            (*num)++;
+            libxl_device_usbdev_init(usbdev);
+            usbdev->ctrl = usbctrl;
+            usbdev->port = i + 1;
+            usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+            rc = usbdev_busaddr_from_busid(gc, busid,
+                                           &usbdev->u.hostdev.hostbus,
+                                           &usbdev->u.hostdev.hostaddr);
+            if (rc) goto out;
+        }
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/* get all usb devices of the domain */
+libxl_device_usbdev *
+libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usbdev *usbdevs = NULL;
+    const char *libxl_vusbs_path;
+    char **usbctrls;
+    unsigned int nc = 0;
+    int i, j;
+
+    *num = 0;
+
+    libxl_vusbs_path = GCSPRINTF("%s/device/%s",
+                                 libxl__xs_libxl_path(gc, domid),
+                                 libxl__device_kind_to_string(
+                                 LIBXL__DEVICE_KIND_VUSB));
+    usbctrls = libxl__xs_directory(gc, XBT_NULL, libxl_vusbs_path, &nc);
+
+    for (i = 0; i < nc; i++) {
+        int rc, nd = 0;
+        libxl_device_usbdev *tmp = NULL;
+
+        rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
+                                                  atoi(usbctrls[i]),
+                                                  &tmp, &nd);
+        if (rc || !nd) continue;
+
+        usbdevs = libxl__realloc(NOGC, usbdevs,
+                                 sizeof(*usbdevs) * (*num + nd));
+        for (j = 0; j < nd; j++) {
+            libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j);
+            (*num)++;
+        }
+    }
+
+    GC_FREE;
+    return usbdevs;
+}
+
+static char *vusb_get_port_path(libxl__gc *gc, uint32_t domid,
+                                libxl_usbctrl_type type, int ctrl, int port)
+{
+    char *path;
+
+    if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL)
+        path = GCSPRINTF("%s/device/%s", libxl__xs_libxl_path(gc, domid),
+                         libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
+    else
+        path = GCSPRINTF("%s/backend/%s/%d",
+                         libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                         pvusb_get_device_type(type), domid);
+
+    return GCSPRINTF("%s/%d/port/%d", path, ctrl, port);
+}
+
+/* find first unused controller:port and give that to usb device */
+static int
+libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid,
+                                         libxl_device_usbdev *usbdev)
+{
+    libxl_device_usbctrl *usbctrls = NULL;
+    int numctrl = 0;
+    int i, j, rc;
+
+    usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl);
+    if (!numctrl || !usbctrls) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    for (i = 0; i < numctrl; i++) {
+        for (j = 0; j < usbctrls[i].ports; j++) {
+            const char *path, *tmp;
+
+            path = vusb_get_port_path(gc, domid, usbctrls[i].type,
+                                      usbctrls[i].devid, j + 1);
+            rc = libxl__xs_read_checked(gc, XBT_NULL, path, &tmp);
+            if (rc) goto out;
+
+            if (tmp && !strcmp(tmp, "")) {
+                usbdev->ctrl = usbctrls[i].devid;
+                usbdev->port = j + 1;
+                rc = 0;
+                goto out;
+            }
+        }
+    }
+
+    /* no available controller:port */
+    rc = ERROR_FAIL;
+
+out:
+    libxl_device_usbctrl_list_free(usbctrls, numctrl);
+    return rc;
+}
+
+/* Fill in usb information with default value.
+ *
+ * Generally, it does:
+ * 1) if "controller" is not specified:
+ *    - if "port" is not specified, try to find an available controller:port,
+ *      if found, use that; otherwise, create a new controller, use this
+ *      controller and its first port
+ *    - if "port" is specified, report error.
+ * 2) if "controller" is specified, but port is not specified:
+ *    try to find an available port under this controller, if found, use
+ *    that, otherwise, report error.
+ * 3) if both "controller" and "port" are specified:
+ *    check the controller:port is available, if not, report error.
+ */
+static int libxl__device_usbdev_setdefault(libxl__gc *gc,
+                                           uint32_t domid,
+                                           libxl_device_usbdev *usbdev,
+                                           bool update_json)
+{
+    int rc;
+
+    if (!usbdev->type)
+        usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+
+    if (usbdev->ctrl == -1) {
+        if (usbdev->port) {
+            LOGD(ERROR, domid,
+                 "USB controller must be specified if you specify port");
+            return ERROR_INVAL;
+        }
+
+        rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev);
+        /* If no existing controller to host this usb device, add a new one */
+        if (rc) {
+            libxl_device_usbctrl *usbctrl;
+
+            GCNEW(usbctrl);
+            libxl_device_usbctrl_init(usbctrl);
+            rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
+                                                  update_json);
+            if (rc < 0) goto out;
+
+            rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
+            if (rc) goto out;
+
+            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
+                                                    update_json);
+            if (rc) goto out;
+
+            usbdev->ctrl = usbctrl->devid;
+            usbdev->port = 1;
+        }
+    } else {
+        /* A controller was specified; look it up */
+        const char *libxl_path, *be_path, *tmp;
+
+        libxl_path = libxl__domain_device_libxl_path(gc, domid, usbdev->ctrl,
+                                                        LIBXL__DEVICE_KIND_VUSB);
+
+        be_path = vusb_be_from_xs_libxl(gc, libxl_path);
+        if (!be_path) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        if (usbdev->port) {
+            /* A specific port was requested; see if it's available */
+            rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                        GCSPRINTF("%s/port/%d",
+                                                  be_path, usbdev->port),
+                                        &tmp);
+            if (rc) goto out;
+
+            if (tmp && strcmp(tmp, "")) {
+                LOGD(ERROR, domid, "The controller port isn't available");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        } else {
+            /* No port was requested. Choose free port. */
+            int i, ports;
+
+            rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                        GCSPRINTF("%s/num-ports", be_path), &tmp);
+            if (rc) goto out;
+
+            ports = tmp ? atoi(tmp) : 0;
+
+            for (i = 0; i < ports; i++) {
+                rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                            GCSPRINTF("%s/port/%d", be_path, i + 1),
+                                            &tmp);
+                if (rc) goto out;
+
+                if (tmp && !strcmp(tmp, "")) {
+                    usbdev->port = i + 1;
+                    break;
+                }
+            }
+
+            if (!usbdev->port) {
+                LOGD(ERROR, domid, "No available port under specified controller");
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/* Add usb information to xenstore
+ *
+ * Adding a usb device won't create new 'qusb'/'vusb' device, but only write
+ * the device busid to the controller:port in xenstore.
+ */
+static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid,
+                                             libxl_device_usbdev *usbdev,
+                                             libxl_usbctrl_type type,
+                                             bool update_json)
+{
+    char *be_path, *busid;
+    int rc;
+    xs_transaction_t t = XBT_NULL;
+    libxl_domain_config d_config;
+    libxl_device_usbdev usbdev_saved;
+    libxl__flock *lock = NULL;
+
+    libxl_domain_config_init(&d_config);
+    libxl_device_usbdev_init(&usbdev_saved);
+    libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev);
+
+    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+                                    usbdev->u.hostdev.hostaddr);
+    if (!busid) {
+        LOGD(DEBUG, domid, "Fail to get busid of usb device");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (update_json) {
+        lock = libxl__lock_domain_userdata(gc, domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+            goto out;
+        }
+
+        rc = libxl__get_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        device_add_domain_config(gc, &d_config, &libxl__usbdev_devtype,
+                                         &usbdev_saved);
+
+        rc = libxl__dm_check_start(gc, &d_config, domid);
+        if (rc) goto out;
+    }
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        if (update_json) {
+            rc = libxl__set_domain_configuration(gc, domid, &d_config);
+            if (rc) goto out;
+        }
+
+        be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl,
+                                     usbdev->port);
+
+        LOGD(DEBUG, domid, "Adding usb device %s to xenstore: controller %d, port %d",
+            busid, usbdev->ctrl, usbdev->port);
+
+        rc = libxl__xs_write_checked(gc, t, be_path, busid);
+        if (rc) goto out;
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    rc = 0;
+
+out:
+    if (lock) libxl__unlock_file(lock);
+    libxl_device_usbdev_dispose(&usbdev_saved);
+    libxl_domain_config_dispose(&d_config);
+    return rc;
+}
+
+static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t domid,
+                                                libxl_device_usbdev *usbdev,
+                                                libxl_usbctrl_type type)
+{
+    char *be_path;
+
+    be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl, usbdev->port);
+
+    LOGD(DEBUG, domid, "Removing usb device from xenstore: controller %d, port %d",
+         usbdev->ctrl, usbdev->port);
+
+    return libxl__xs_write_checked(gc, XBT_NULL, be_path, "");
+}
+
+static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_usbdev *usbdev,
+                                        libxl_usbctrl_type type)
+{
+    return libxl__xs_read(gc, XBT_NULL,
+                          vusb_get_port_path(gc, domid, type, usbdev->ctrl,
+                                             usbdev->port));
+}
+
+/* get original driver path of usb interface, stored in @drvpath */
+static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char **drvpath)
+{
+    char *spath, *dp = NULL;
+
+    spath = GCSPRINTF(SYSFS_USB_DEV "/%s/driver", intf);
+
+    /* Find the canonical path to the driver. */
+    dp = libxl__zalloc(gc, PATH_MAX);
+    dp = realpath(spath, dp);
+    if (!dp && errno != ENOENT) {
+        LOGE(ERROR, "get realpath failed: '%s'", spath);
+        return ERROR_FAIL;
+    }
+
+    *drvpath = dp;
+
+    return 0;
+}
+
+static int unbind_usbintf(libxl__gc *gc, const char *intf)
+{
+    char *path;
+    int fd = -1;
+    int rc;
+
+    path = GCSPRINTF(SYSFS_USB_DEV "/%s/driver/unbind", intf);
+
+    fd = open(path, O_WRONLY);
+    if (fd < 0) {
+        LOGE(ERROR, "open file failed: '%s'", path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (libxl_write_exactly(CTX, fd, intf, strlen(intf), path, intf)) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    if (fd >= 0) close(fd);
+    return rc;
+}
+
+static int bind_usbintf(libxl__gc *gc, const char *intf, const char *drvpath)
+{
+    char *bind_path, *intf_path;
+    struct stat st;
+    int fd = -1;
+    int rc, r;
+
+    intf_path = GCSPRINTF("%s/%s", drvpath, intf);
+
+    /* check through lstat, if intf already exists under drvpath,
+     * it's already bound, return directly; if it doesn't exist,
+     * continue to do bind work; otherwise, return error.
+     */
+    r = lstat(intf_path, &st);
+    if (r == 0)
+        return 0;
+    if (r < 0 && errno != ENOENT)
+        return ERROR_FAIL;
+
+    bind_path = GCSPRINTF("%s/bind", drvpath);
+
+    fd = open(bind_path, O_WRONLY);
+    if (fd < 0) {
+        LOGE(ERROR, "open file failed: '%s'", bind_path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (libxl_write_exactly(CTX, fd, intf, strlen(intf), bind_path, intf)) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    if (fd >= 0) close(fd);
+    return rc;
+}
+
+/* Is usb interface bound to usbback? */
+static int usbintf_is_assigned(libxl__gc *gc, char *intf)
+{
+    char *spath;
+    int r;
+    struct stat st;
+
+    spath = GCSPRINTF(SYSFS_USBBACK_DRIVER "/%s", intf);
+    r = lstat(spath, &st);
+
+    if (r == 0)
+        return 1;
+    if (r < 0 && errno == ENOENT)
+        return 0;
+    LOGE(ERROR, "Accessing %s", spath);
+    return -1;
+}
+
+static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid,
+                                     char ***intfs, int *num)
+{
+    DIR *dir;
+    char *buf;
+    struct dirent *de;
+    int rc;
+
+    *intfs = NULL;
+    *num = 0;
+
+    buf = GCSPRINTF("%s:", busid);
+
+    dir = opendir(SYSFS_USB_DEV);
+    if (!dir) {
+        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
+        return ERROR_FAIL;
+    }
+
+    for (;;) {
+        errno = 0;
+        de = readdir(dir);
+
+        if (!de && errno) {
+            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        if (!de)
+            break;
+
+        if (!strcmp(de->d_name, ".") ||
+            !strcmp(de->d_name, ".."))
+            continue;
+
+        if (!strncmp(de->d_name, buf, strlen(buf))) {
+            GCREALLOC_ARRAY(*intfs, *num + 1);
+            (*intfs)[*num] = libxl__strdup(gc, de->d_name);
+            (*num)++;
+        }
+    }
+
+    rc = 0;
+
+out:
+    closedir(dir);
+    return rc;
+}
+
+/* Encode usb interface so that it could be written to xenstore as a key.
+ *
+ * Since xenstore key cannot include '.' or ':', we'll change '.' to '_',
+ * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1.
+ * This will be used to save original driver of USB device to xenstore.
+ */
+static char *usb_interface_xenstore_encode(libxl__gc *gc, const char *busid)
+{
+    char *str = libxl__strdup(gc, busid);
+    int i, len = strlen(str);
+
+    for (i = 0; i < len; i++) {
+        if (str[i] == '.') str[i] = '_';
+        if (str[i] == ':') str[i] = '@';
+    }
+    return str;
+}
+
+/* Unbind USB device from "usbback" driver.
+ *
+ * If there are many interfaces under USB device, check each interface,
+ * unbind from "usbback" driver.
+ */
+static int usbback_dev_unassign(libxl__gc *gc, const char *busid)
+{
+    char **intfs = NULL;
+    int i, num = 0;
+    int rc;
+
+    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
+    if (rc) goto out;
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+
+        /* check if the USB interface is already bound to "usbback" */
+        if (usbintf_is_assigned(gc, intf) > 0) {
+            /* unbind interface from usbback driver */
+            rc = unbind_usbintf(gc, intf);
+            if (rc) {
+                LOGE(ERROR, "Couldn't unbind %s from usbback", intf);
+                goto out;
+            }
+        }
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/* rebind USB device to original driver.
+ *
+ * If there are many interfaces under USB device, for reach interface,
+ * read driver_path from xenstore (if there is) and rebind to its
+ * original driver, then remove driver_path information from xenstore.
+ */
+static int usbdev_rebind(libxl__gc *gc, const char *busid)
+{
+    char **intfs = NULL;
+    char *usbdev_encode = NULL;
+    char *path = NULL;
+    int i, num = 0;
+    int rc;
+
+    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
+    if (rc) goto out;
+
+    usbdev_encode = usb_interface_xenstore_encode(gc, busid);
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+        char *usbintf_encode = NULL;
+        const char *drvpath;
+
+        /* rebind USB interface to its originial driver */
+        usbintf_encode = usb_interface_xenstore_encode(gc, intf);
+        path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
+                         usbdev_encode, usbintf_encode);
+        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath);
+        if (rc) goto out;
+
+        if (drvpath) {
+            rc = bind_usbintf(gc, intf, drvpath);
+            if (rc) {
+                LOGE(ERROR, "Couldn't rebind %s to %s", intf, drvpath);
+                goto out;
+            }
+        }
+    }
+
+out:
+    path = GCSPRINTF(USBBACK_INFO_PATH "/%s", usbdev_encode);
+    libxl__xs_rm_checked(gc, XBT_NULL, path);
+    return rc;
+}
+
+
+/* Bind USB device to "usbback" driver.
+ *
+ * If there are many interfaces under USB device, check each interface,
+ * unbind from original driver and bind to "usbback" driver.
+ */
+static int usbback_dev_assign(libxl__gc *gc, const char *busid)
+{
+    char **intfs = NULL;
+    int num = 0, i;
+    int rc;
+    char *usbdev_encode = NULL;
+
+    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
+    if (rc) return rc;
+
+    usbdev_encode = usb_interface_xenstore_encode(gc, busid);
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+        char *drvpath = NULL;
+
+        /* already assigned to usbback */
+        if (usbintf_is_assigned(gc, intf) > 0)
+            continue;
+
+        rc = usbintf_get_drvpath(gc, intf, &drvpath);
+        if (rc) goto out;
+
+        if (drvpath) {
+            /* write driver path to xenstore for later rebinding */
+            char *usbintf_encode = NULL;
+            char *path;
+
+            usbintf_encode = usb_interface_xenstore_encode(gc, intf);
+            path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
+                             usbdev_encode, usbintf_encode);
+            rc = libxl__xs_write_checked(gc, XBT_NULL, path, drvpath);
+            if (rc) goto out;
+
+            /* unbind interface from original driver */
+            rc = unbind_usbintf(gc, intf);
+            if (rc) goto out;
+        }
+
+        /* bind interface to usbback */
+        rc = bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER);
+        if (rc) {
+            LOG(ERROR, "Couldn't bind %s to %s", intf, SYSFS_USBBACK_DRIVER);
+            goto out;
+        }
+    }
+
+    return 0;
+
+out:
+    /* some interfaces might be bound to usbback, unbind it and
+     * rebind it to its original driver
+     */
+    usbback_dev_unassign(gc, busid);
+    usbdev_rebind(gc, busid);
+    return rc;
+}
+
+static void device_usbdev_add_qmp_cb(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
+static void device_usbdev_add_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbdev_add_done(libxl__egc *egc,
+    libxl__ao_device *aodev, int rc);
+
+/* AO operation to add a usb device.
+ *
+ * Generally, it does:
+ * 1) check if the usb device type is assignable
+ * 2) check if the usb device is already assigned to a domain
+ * 3) add 'busid' of the usb device to xenstore contoller/port/.
+ *    (PVUSB driver watches the xenstore changes and will detect that.)
+ * 4) unbind usb device from original driver and bind to usbback.
+ *    If usb device has many interfaces, then:
+ *    - unbind each interface from its original driver and bind to usbback.
+ *    - store the original driver to xenstore for later rebinding when
+ *      detaching the device.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, aodev->update_json, ...
+ */
+static void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid,
+                                     libxl_device_usbdev *usbdev,
+                                     libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    int rc;
+    libxl_device_usbdev *assigned;
+    int num_assigned;
+    libxl_device_usbctrl usbctrl;
+    char *busid;
+    bool has_callback = false;
+
+    libxl_device_usbctrl_init(&usbctrl);
+
+    /* Store *usbdev to be used by callbacks */
+    aodev->device_config = usbdev;
+    aodev->device_type = &libxl__usbdev_devtype;
+
+    /* Currently only support adding USB device from Dom0 backend.
+     * So, if USB controller is specified, check its backend domain,
+     * if it's not Dom0, report error.
+     */
+    if (usbdev->ctrl != -1) {
+        rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl,
+                                           &usbctrl);
+        if (rc) goto out;
+
+        if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
+            LOGD(ERROR, domid,
+                 "Don't support adding USB device from non-Dom0 backend");
+            rc = ERROR_INVAL;
+            goto out;
+        }
+        libxl_device_usbctrl_dispose(&usbctrl);
+    }
+
+    /* check usb device is assignable type */
+    if (!is_usbdev_assignable(gc, usbdev)) {
+        LOGD(ERROR, domid, "USB device is not assignable.");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* check usb device is already assigned */
+    rc = get_assigned_devices(gc, &assigned, &num_assigned);
+    if (rc) {
+        LOGD(ERROR, domid, "cannot determine if device is assigned,"
+                           " refusing to continue");
+        goto out;
+    }
+
+    if (is_usbdev_in_array(assigned, num_assigned, usbdev)) {
+        LOGD(ERROR, domid, "USB device already attached to a domain");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    /* fill default values, e.g, if usbdev->ctrl and usbdev->port
+     * not specified, choose available controller:port and fill in. */
+    rc = libxl__device_usbdev_setdefault(gc, domid, usbdev,
+                                         aodev->update_json);
+    if (rc) goto out;
+
+    rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
+    if (rc) goto out;
+
+    /* do actual adding usb device operation */
+    switch (usbctrl.type) {
+    case LIBXL_USBCTRL_TYPE_PV:
+        busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+                                        usbdev->u.hostdev.hostaddr);
+        if (!busid) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+                                               LIBXL_USBCTRL_TYPE_PV,
+                                               aodev->update_json);
+        if (rc) goto out;
+
+        rc = usbback_dev_assign(gc, busid);
+        if (rc) {
+            libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+                                                 LIBXL_USBCTRL_TYPE_PV);
+            goto out;
+        }
+        break;
+    case LIBXL_USBCTRL_TYPE_QUSB:
+        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+                                               LIBXL_USBCTRL_TYPE_QUSB,
+                                               aodev->update_json);
+        if (rc) goto out;
+
+        break;
+    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+                                               LIBXL_USBCTRL_TYPE_DEVICEMODEL,
+                                               aodev->update_json);
+        if (rc) goto out;
+
+        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+                                         device_usbdev_add_timeout,
+                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
+        if (rc) goto out;
+
+        aodev->qmp.ao = ao;
+        aodev->qmp.domid = domid;
+        aodev->qmp.callback = device_usbdev_add_qmp_cb;
+        aodev->qmp.payload_fd = -1;
+        rc = libxl__device_usbdev_add_hvm(egc, &aodev->qmp, usbdev);
+        if (rc) {
+            libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+                                             LIBXL_USBCTRL_TYPE_DEVICEMODEL);
+            goto out;
+        }
+        has_callback = true;
+        break;
+    default:
+        LOGD(ERROR, domid, "Unsupported usb controller type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    libxl_device_usbctrl_dispose(&usbctrl);
+    /* Only call _done if no callback have been setup */
+    if (!has_callback)
+        device_usbdev_add_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_add_timeout(libxl__egc *egc,
+                                      libxl__ev_time *ev,
+                                      const struct timeval *requested_abs,
+                                      int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+    if (rc == ERROR_TIMEDOUT)
+        LOGD(ERROR, aodev->qmp.domid,
+             "Adding usbdev to QEMU timed out");
+    device_usbdev_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbdev_add_qmp_cb(libxl__egc *egc,
+                                     libxl__ev_qmp *qmp,
+                                     const libxl__json_object *r,
+                                     int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+    libxl_device_usbdev *const usbdev = aodev->device_config;
+
+    if (rc)
+        libxl__device_usbdev_remove_xenstore(gc, qmp->domid,
+            usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL);
+    device_usbdev_add_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_add_done(libxl__egc *egc,
+                                   libxl__ao_device *aodev,
+                                   int rc)
+{
+    EGC_GC;
+
+    libxl__ev_time_deregister(gc, &aodev->timeout);
+    libxl__ev_qmp_dispose(gc, &aodev->qmp);
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+LIBXL_DEFINE_DEVICE_ADD(usbdev)
+static LIBXL_DEFINE_DEVICES_ADD(usbdev)
+
+static void device_usbdev_remove_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
+static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
+    libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
+static void device_usbdev_remove_done(libxl__egc *egc,
+    libxl__ao_device *aodev, int rc);
+
+/* Operation to remove usb device.
+ *
+ * Generally, it does:
+ * 1) check if the usb device is assigned to the domain
+ * 2) remove the usb device from xenstore controller/port.
+ * 3) unbind usb device from usbback and rebind to its original driver.
+ *    If usb device has many interfaces, do it to each interface.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, ...
+ */
+static void libxl__device_usbdev_remove(libxl__egc *egc, uint32_t domid,
+                                        libxl_device_usbdev *usbdev,
+                                        libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    int rc;
+    char *busid;
+    libxl_device_usbctrl usbctrl;
+    bool has_callback = false;
+
+    /* Store *usbdev to be used by callbacks */
+    aodev->device_config = usbdev;
+    aodev->device_type = &libxl__usbdev_devtype;
+
+    libxl_device_usbctrl_init(&usbctrl);
+
+    if (usbdev->ctrl < 0 || usbdev->port < 1) {
+        LOGD(ERROR, domid, "Invalid USB device");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
+    if (rc) goto out;
+
+    if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
+        LOGD(ERROR, domid,
+             "Don't support removing USB device from non-Dom0 backend");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    /* do actual removing usb device operation */
+    switch (usbctrl.type) {
+    case LIBXL_USBCTRL_TYPE_PV:
+        busid = usbdev_busid_from_ctrlport(gc, domid, usbdev, usbctrl.type);
+        if (!busid) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        /* Things are done in order of:
+         *   unbind USB device from usbback,
+         *   remove USB device from xenstore,
+         *   rebind USB device to original driver.
+         * It is to balance simplicity with robustness in case of failure:
+         * - We unbind all interfaces before rebinding any interfaces, so
+         *   that we never get into a situation where some interfaces are
+         *   assigned to usbback and some are assigned to the original drivers.
+         * - We also unbind the interfaces before removing the pvusb xenstore
+         *   nodes, so that if the unbind fails in the middle, the device still
+         *   shows up in xl usb-list, and the user can re-try removing it.
+         */
+        rc = usbback_dev_unassign(gc, busid);
+        if (rc) {
+            LOGD(ERROR, domid, "Error removing device from guest."
+                 " Try running usbdev-detach again.");
+            goto out;
+        }
+
+        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+                                                  LIBXL_USBCTRL_TYPE_PV);
+        if (rc) {
+            LOGD(ERROR, domid, "Error removing device from guest."
+                 " Try running usbdev-detach again.");
+            goto out;
+        }
+
+        rc = usbdev_rebind(gc, busid);
+        if (rc) {
+            LOGD(ERROR, domid, "USB device removed from guest, but couldn't"
+                 " re-bind to domain 0. Try removing and re-inserting"
+                 " the USB device or reloading the driver modules.");
+            goto out;
+        }
+
+        break;
+    case LIBXL_USBCTRL_TYPE_QUSB:
+        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+                                                  LIBXL_USBCTRL_TYPE_QUSB);
+        if (rc) goto out;
+
+        break;
+    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
+                                              LIBXL_USBCTRL_TYPE_DEVICEMODEL);
+        if (rc) goto out;
+
+        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
+                                         device_usbdev_remove_timeout,
+                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
+        if (rc) goto out;
+
+        aodev->qmp.ao = ao;
+        aodev->qmp.domid = domid;
+        aodev->qmp.callback = device_usbdev_remove_qmp_cb;
+        aodev->qmp.payload_fd = -1;
+        rc = libxl__device_usbdev_del_hvm(egc, &aodev->qmp, usbdev);
+        if (rc) {
+            libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
+                                              LIBXL_USBCTRL_TYPE_DEVICEMODEL,
+                                              false);
+            goto out;
+        }
+        has_callback = true;
+        break;
+    default:
+        LOGD(ERROR, domid, "Unsupported usb controller type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    libxl_device_usbctrl_dispose(&usbctrl);
+    /* Only call _done if no callback have been setup */
+    if (!has_callback)
+        device_usbdev_remove_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_remove_timeout(libxl__egc *egc,
+    libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
+
+    if (rc == ERROR_TIMEDOUT)
+        LOGD(ERROR, aodev->qmp.domid,
+             "Removing usbdev from QEMU timed out");
+    device_usbdev_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
+}
+
+static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
+                                        libxl__ev_qmp *qmp,
+                                        const libxl__json_object *r,
+                                        int rc)
+{
+    EGC_GC;
+    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
+    libxl_device_usbdev *const usbdev = aodev->device_config;
+
+    if (rc) {
+        libxl__device_usbdev_add_xenstore(gc, qmp->domid, usbdev,
+                                          LIBXL_USBCTRL_TYPE_DEVICEMODEL,
+                                          false);
+    }
+
+    device_usbdev_remove_done(egc, aodev, rc); /* must be last */
+}
+
+static void device_usbdev_remove_done(libxl__egc *egc,
+                                      libxl__ao_device *aodev,
+                                      int rc)
+{
+    EGC_GC;
+
+    libxl__ev_time_deregister(gc, &aodev->timeout);
+    libxl__ev_qmp_dispose(gc, &aodev->qmp);
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+}
+
+int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
+                               libxl_device_usbdev *usbdev,
+                               const libxl_asyncop_how *ao_how)
+
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__ao_device *aodev;
+
+    GCNEW(aodev);
+    libxl__prepare_ao_device(ao, aodev);
+    aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+    aodev->callback = device_addrm_aocomplete;
+    libxl__device_usbdev_remove(egc, domid, usbdev, aodev);
+
+    return AO_INPROGRESS;
+}
+
+int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx,
+                                    uint32_t domid,
+                                    int ctrl,
+                                    int port,
+                                    libxl_device_usbdev *usbdev)
+{
+    GC_INIT(ctx);
+    const char *libxl_path, *be_path, *busid;
+    int rc;
+
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, ctrl,
+                 LIBXL__DEVICE_KIND_VUSB);
+    be_path = vusb_be_from_xs_libxl(gc, libxl_path);
+    if (!be_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                           GCSPRINTF("%s/port/%d", be_path, port),
+                           &busid);
+    if (rc) goto out;
+
+    if (!busid || !strcmp(busid, "")) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    usbdev->ctrl = ctrl;
+    usbdev->port = port;
+    usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+    rc = usbdev_busaddr_from_busid(gc, busid,
+                                   &usbdev->u.hostdev.hostbus,
+                                   &usbdev->u.hostdev.hostaddr);
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+static int libxl_device_usbctrl_compare(const libxl_device_usbctrl *d1,
+                                        const libxl_device_usbctrl *d2)
+{
+    return COMPARE_USBCTRL(d1, d2);
+}
+
+static int libxl_device_usbctrl_dm_needed(void *e, unsigned domid)
+{
+    libxl_device_usbctrl *elem = e;
+
+    return elem->type == LIBXL_USBCTRL_TYPE_QUSB &&
+           elem->backend_domid == domid;
+}
+
+static int libxl_device_usbdev_compare(const libxl_device_usbdev *d1,
+                                       const libxl_device_usbdev *d2)
+{
+    return COMPARE_USB(d1, d2);
+}
+
+void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr)
+{
+   int i;
+
+   for (i = 0; i < nr; i++)
+       libxl_device_usbdev_dispose(&list[i]);
+   free(list);
+}
+
+#define libxl__device_usbctrl_update_devid NULL
+
+LIBXL_DEFINE_DEVID_TO_DEVICE(usbctrl)
+LIBXL_DEFINE_DEVICE_LIST(usbctrl)
+DEFINE_DEVICE_TYPE_STRUCT(usbctrl, VUSB,
+    .from_xenstore = (device_from_xenstore_fn_t)libxl__usbctrl_from_xenstore,
+    .dm_needed = libxl_device_usbctrl_dm_needed
+);
+
+#define libxl__device_from_usbdev NULL
+#define libxl__device_usbdev_update_devid NULL
+
+DEFINE_DEVICE_TYPE_STRUCT(usbdev, VUSB);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_utils.c b/tools/libs/light/libxl_utils.c
new file mode 100644 (file)
index 0000000..b039143
--- /dev/null
@@ -0,0 +1,1272 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include <ctype.h>
+
+#include "libxl_internal.h"
+#include "_paths.h"
+
+#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
+const
+#endif
+char *libxl_basename(const char *name)
+{
+    const char *filename;
+    if (name == NULL)
+        return strdup(".");
+    if (name[0] == '\0')
+        return strdup(".");
+
+    filename = strrchr(name, '/');
+    if (filename)
+        return strdup(filename+1);
+    return strdup(name);
+}
+
+unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus)
+{
+    /* 256 pages (1MB) per vcpu,
+       plus 1 page per MiB of RAM for the P2M map,
+       plus 1 page per MiB of RAM to shadow the resident processes.
+       This is higher than the minimum that Xen would allocate if no value
+       were given (but the Xen minimum is for safety, not performance).
+     */
+    return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024));
+}
+
+char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid)
+{
+    unsigned int len;
+    char path[strlen("/local/domain") + 12];
+    char *s;
+
+    snprintf(path, sizeof(path), "/local/domain/%d/name", domid);
+    s = xs_read(ctx->xsh, XBT_NULL, path, &len);
+    return s;
+}
+
+char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid)
+{
+    char *s = libxl_domid_to_name(CTX, domid);
+    libxl__ptr_add(gc, s);
+    return s;
+}
+
+int libxl_name_to_domid(libxl_ctx *ctx, const char *name,
+                        uint32_t *domid)
+{
+    int i, nb_domains;
+    char *domname;
+    libxl_dominfo *dominfo;
+    int ret = ERROR_INVAL;
+
+    dominfo = libxl_list_domain(ctx, &nb_domains);
+    if (!dominfo)
+        return ERROR_NOMEM;
+
+    for (i = 0; i < nb_domains; i++) {
+        domname = libxl_domid_to_name(ctx, dominfo[i].domid);
+        if (!domname)
+            continue;
+        if (strcmp(domname, name) == 0) {
+            *domid = dominfo[i].domid;
+            ret = 0;
+            free(domname);
+            break;
+        }
+        free(domname);
+    }
+    libxl_dominfo_list_free(dominfo, nb_domains);
+    return ret;
+}
+
+int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name,
+                                    uint32_t *domid)
+{
+    int i, rv;
+    for (i=0; name[i]; i++) {
+        if (!CTYPE(isdigit, name[i])) {
+            goto nondigit_found;
+        }
+    }
+    *domid = strtoul(name, NULL, 10);
+    return 0;
+
+ nondigit_found:
+    /* this could also check for uuids */
+    rv = libxl_name_to_domid(ctx, name, domid);
+    return rv;
+}
+
+static int qualifier_to_id(const char *p, uint32_t *id_r)
+{
+    int i, alldigit;
+
+    alldigit = 1;
+    for (i = 0; p[i]; i++) {
+        if (!isdigit((uint8_t)p[i])) {
+            alldigit = 0;
+            break;
+        }
+    }
+
+    if (i > 0 && alldigit) {
+        *id_r = strtoul(p, NULL, 10);
+        return 0;
+    } else {
+        /* check here if it's a uuid and do proper conversion */
+    }
+    return 1;
+}
+
+int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
+                                         uint32_t *poolid_r,
+                                         int *was_name_r)
+{
+    int was_name;
+
+    was_name = qualifier_to_id(p, poolid_r);
+    if (was_name_r) *was_name_r = was_name;
+    return was_name ? libxl_name_to_cpupoolid(ctx, p, poolid_r) : 0;
+}
+
+char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid)
+{
+    unsigned int len;
+    char path[strlen("/local/pool") + 12];
+    char *s;
+
+    snprintf(path, sizeof(path), "/local/pool/%d/name", poolid);
+    s = xs_read(ctx->xsh, XBT_NULL, path, &len);
+    if (!s && (poolid == 0))
+        return strdup("Pool-0");
+    return s;
+}
+
+/* This is a bit horrid but without xs_exists it seems like the only way. */
+int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid)
+{
+    int ret;
+    char *s = libxl_cpupoolid_to_name(ctx, poolid);
+
+    ret = (s != NULL);
+    free(s);
+    return ret;
+}
+
+char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid)
+{
+    char *s = libxl_cpupoolid_to_name(CTX, poolid);
+    libxl__ptr_add(gc, s);
+    return s;
+}
+
+int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name,
+                        uint32_t *poolid)
+{
+    int i, nb_pools;
+    char *poolname;
+    libxl_cpupoolinfo *poolinfo;
+    int ret = ERROR_INVAL;
+
+    poolinfo = libxl_list_cpupool(ctx, &nb_pools);
+    if (!poolinfo)
+        return ERROR_NOMEM;
+
+    for (i = 0; i < nb_pools; i++) {
+        if (ret && ((poolname = libxl_cpupoolid_to_name(ctx,
+            poolinfo[i].poolid)) != NULL)) {
+            if (strcmp(poolname, name) == 0) {
+                *poolid = poolinfo[i].poolid;
+                ret = 0;
+            }
+            free(poolname);
+        }
+    }
+    libxl_cpupoolinfo_list_free(poolinfo, nb_pools);
+    return ret;
+}
+
+int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid)
+{
+    GC_INIT(ctx);
+    char * stubdom_id_s;
+    int ret;
+
+    stubdom_id_s = libxl__xs_read(gc, XBT_NULL,
+                                  GCSPRINTF("%s/image/device-model-domid",
+                                  libxl__xs_get_dompath(gc, guest_domid)));
+    if (stubdom_id_s)
+        ret = atoi(stubdom_id_s);
+    else
+        ret = 0;
+    GC_FREE;
+    return ret;
+}
+
+int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid)
+{
+    GC_INIT(ctx);
+    char *target, *endptr;
+    uint32_t value;
+    int ret = 0;
+
+    target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/target",
+                            libxl__xs_get_dompath(gc, domid)));
+    if (!target)
+        goto out;
+    value = strtol(target, &endptr, 10);
+    if (*endptr != '\0')
+        goto out;
+    if (target_domid)
+        *target_domid = value;
+    ret = 1;
+out:
+    GC_FREE;
+    return ret;
+}
+
+static int logrename(libxl__gc *gc, const char *old, const char *new)
+{
+    int r;
+
+    r = rename(old, new);
+    if (r) {
+        if (errno == ENOENT) return 0; /* ok */
+
+        LOGE(ERROR, "failed to rotate logfile - "
+                    "could not rename %s to %s", old, new);
+        return ERROR_FAIL;
+    }
+    return 0;
+}
+
+int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name)
+{
+    GC_INIT(ctx);
+    struct stat stat_buf;
+    char *logfile, *logfile_new;
+    int i, rc;
+
+    logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
+    if (stat(logfile, &stat_buf) == 0) {
+        /* file exists, rotate */
+        logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.10", name);
+        unlink(logfile);
+        for (i = 9; i > 0; i--) {
+            logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i);
+            logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i + 1);
+            rc = logrename(gc, logfile, logfile_new);
+            if (rc)
+                goto out;
+        }
+        logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
+        logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.1", name);
+
+        rc = logrename(gc, logfile, logfile_new);
+        if (rc)
+            goto out;
+    } else {
+        if (errno != ENOENT)
+            LOGE(WARN, "problem checking existence of logfile %s, "
+                       "which might have needed to be rotated",
+                 name);
+    }
+    *full_name = strdup(logfile);
+    rc = 0;
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend)
+{
+    char *p;
+    int rc = 0;
+
+    if (!strcmp(s, "phy")) {
+        *backend = LIBXL_DISK_BACKEND_PHY;
+    } else if (!strcmp(s, "file")) {
+        *backend = LIBXL_DISK_BACKEND_TAP;
+    } else if (!strcmp(s, "qdisk")) {
+        *backend = LIBXL_DISK_BACKEND_QDISK;
+    } else if (!strcmp(s, "tap")) {
+        p = strchr(s, ':');
+        if (!p) {
+            rc = ERROR_INVAL;
+            goto out;
+        }
+        p++;
+        if (!strcmp(p, "vhd")) {
+            *backend = LIBXL_DISK_BACKEND_TAP;
+        } else if (!strcmp(p, "qcow")) {
+            *backend = LIBXL_DISK_BACKEND_QDISK;
+        } else if (!strcmp(p, "qcow2")) {
+            *backend = LIBXL_DISK_BACKEND_QDISK;
+        } else if (!strcmp(p, "qed")) {
+            *backend = LIBXL_DISK_BACKEND_QDISK;
+        }
+    }
+out:
+    return rc;
+}
+
+int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
+                             void **data_r, int *datalen_r) {
+    GC_INIT(ctx);
+    FILE *f = 0;
+    uint8_t *data = 0;
+    int datalen = 0;
+    int e;
+    struct stat stab;
+    ssize_t rs;
+
+    f = fopen(filename, "r");
+    if (!f) {
+        if (errno == ENOENT) return ENOENT;
+        LOGE(ERROR, "failed to open %s", filename);
+        goto xe;
+    }
+
+    if (fstat(fileno(f), &stab)) {
+        LOGE(ERROR, "failed to fstat %s", filename);
+        goto xe;
+    }
+
+    if (!S_ISREG(stab.st_mode)) {
+        LOGE(ERROR, "%s is not a plain file", filename);
+        errno = ENOTTY;
+        goto xe;
+    }
+
+    if (stab.st_size > INT_MAX) {
+        LOG(ERROR, "file %s is far too large", filename);
+        errno = EFBIG;
+        goto xe;
+    }
+
+    datalen = stab.st_size;
+
+    if (stab.st_size && data_r) {
+        data = malloc(datalen);
+        if (!data) goto xe;
+
+        rs = fread(data, 1, datalen, f);
+        if (rs != datalen) {
+            if (ferror(f))
+                LOGE(ERROR, "failed to read %s", filename);
+            else if (feof(f))
+                LOG(ERROR, "%s changed size while we were reading it",
+                   filename);
+            else
+                abort();
+            goto xe;
+        }
+    }
+
+    if (fclose(f)) {
+        f = 0;
+        LOGE(ERROR, "failed to close %s", filename);
+        goto xe;
+    }
+
+    if (data_r) *data_r = data;
+    if (datalen_r) *datalen_r = datalen;
+
+    GC_FREE;
+    return 0;
+
+ xe:
+    GC_FREE;
+    e = errno;
+    assert(e != ENOENT);
+    if (f) fclose(f);
+    free(data);
+    return e;
+}
+
+int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename,
+                                    void **data_r, int *datalen_r)
+{
+    FILE *f = 0;
+    uint8_t *data = 0;
+    int datalen = 0;
+    int e;
+    struct stat stab;
+    ssize_t rs;
+
+    f = fopen(filename, "r");
+    if (!f) {
+        if (errno == ENOENT) return ENOENT;
+        LOGE(ERROR, "failed to open %s", filename);
+        goto xe;
+    }
+
+    if (fstat(fileno(f), &stab)) {
+        LOGE(ERROR, "failed to fstat %s", filename);
+        goto xe;
+    }
+
+    if (!S_ISREG(stab.st_mode)) {
+        LOGE(ERROR, "%s is not a plain file", filename);
+        errno = ENOTTY;
+        goto xe;
+    }
+
+    if (stab.st_size > INT_MAX) {
+        LOG(ERROR, "file %s is far too large", filename);
+        errno = EFBIG;
+        goto xe;
+    }
+
+    datalen = stab.st_size;
+
+    if (stab.st_size && data_r) {
+        data = libxl__malloc(gc, datalen);
+
+        /* For sysfs file, datalen is always PAGE_SIZE. 'read'
+         * will return the number of bytes of the actual content,
+         * rs <= datalen is expected.
+         */
+        rs = fread(data, 1, datalen, f);
+        if (rs < datalen) {
+            if (ferror(f)) {
+                LOGE(ERROR, "failed to read %s", filename);
+                goto xe;
+            }
+
+            datalen = rs;
+            data = libxl__realloc(gc, data, datalen);
+        }
+    }
+
+    if (fclose(f)) {
+        f = 0;
+        LOGE(ERROR, "failed to close %s", filename);
+        goto xe;
+    }
+
+    if (data_r) *data_r = data;
+    if (datalen_r) *datalen_r = datalen;
+
+    return 0;
+
+ xe:
+    e = errno;
+    assert(e != ENOENT);
+    if (f) fclose(f);
+    return e;
+}
+
+
+#define READ_WRITE_EXACTLY(rw, zero_is_eof, constdata)                    \
+                                                                          \
+  int libxl_##rw##_exactly(libxl_ctx *ctx, int fd,                 \
+                           constdata void *data, ssize_t sz,              \
+                           const char *source, const char *what) {        \
+      ssize_t got;                                                        \
+      GC_INIT(ctx);                                                       \
+                                                                          \
+      while (sz > 0) {                                                    \
+          got = rw(fd, data, sz);                                         \
+          if (got == -1) {                                                \
+              if (errno == EINTR) continue;                               \
+              if (!ctx) { GC_FREE; return errno; }                        \
+              LOGE(ERROR, "failed to "#rw" %s%s%s",                       \
+                   what ? what : "", what ? " from " : "", source);       \
+              GC_FREE;                                                    \
+              return errno;                                               \
+          }                                                               \
+          if (got == 0) {                                                 \
+              if (!ctx) { GC_FREE; return  EPROTO; }                      \
+              LOG(ERROR, zero_is_eof                                      \
+                  ? "file/stream truncated reading %s%s%s"                \
+                  : "file/stream write returned 0! writing %s%s%s",       \
+                  what ? what : "", what ? " from " : "", source);        \
+              GC_FREE;                                                    \
+              return EPROTO;                                              \
+          }                                                               \
+          sz -= got;                                                      \
+          data = (char*)data + got;                                       \
+      }                                                                   \
+      GC_FREE;                                                            \
+      return 0;                                                           \
+  }
+
+READ_WRITE_EXACTLY(read, 1, /* */)
+READ_WRITE_EXACTLY(write, 0, const)
+
+int libxl__remove_file(libxl__gc *gc, const char *path)
+{
+    for (;;) {
+        int r = unlink(path);
+        if (!r) return 0;
+        if (errno == ENOENT) return 0;
+        if (errno == EINTR) continue;
+        LOGE(ERROR, "failed to remove file %s", path);
+        return ERROR_FAIL;
+     }
+}
+
+int libxl__remove_file_or_directory(libxl__gc *gc, const char *path)
+{
+    for (;;) {
+        int r = rmdir(path);
+        if (!r) return 0;
+        if (errno == ENOENT) return 0;
+        if (errno == ENOTEMPTY) return libxl__remove_directory(gc, path);
+        if (errno == ENOTDIR) return libxl__remove_file(gc, path);
+        if (errno == EINTR) continue;
+        LOGE(ERROR, "failed to remove %s", path);
+        return ERROR_FAIL;
+     }
+}
+
+int libxl__remove_directory(libxl__gc *gc, const char *dirpath)
+{
+    int rc = 0;
+    DIR *d = 0;
+
+    d = opendir(dirpath);
+    if (!d) {
+        if (errno == ENOENT)
+            goto out;
+
+        LOGE(ERROR, "failed to opendir %s for removal", dirpath);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    struct dirent *de;
+
+    for (;;) {
+        errno = 0;
+        de = readdir(d);
+        if (!de && errno) {
+            LOGE(ERROR, "failed to readdir %s for removal", dirpath);
+            rc = ERROR_FAIL;
+            break;
+        }
+        if (!de)
+            break;
+
+        if (!strcmp(de->d_name, ".") ||
+            !strcmp(de->d_name, ".."))
+            continue;
+
+        const char *subpath = GCSPRINTF("%s/%s", dirpath, de->d_name);
+        if (libxl__remove_file_or_directory(gc, subpath))
+            rc = ERROR_FAIL;
+    }
+
+    for (;;) {
+        int r = rmdir(dirpath);
+        if (!r) break;
+        if (errno == ENOENT) goto out;
+        if (errno == EINTR) continue;
+        LOGE(ERROR, "failed to remove emptied directory %s", dirpath);
+        rc = ERROR_FAIL;
+    }
+
+ out:
+    if (d) closedir(d);
+
+    return rc;
+}
+
+int libxl_pipe(libxl_ctx *ctx, int pipes[2])
+{
+    GC_INIT(ctx);
+    int ret = 0;
+    if (pipe(pipes) < 0) {
+        LOG(ERROR, "Failed to create a pipe");
+        ret = -1;
+    }
+    GC_FREE;
+    return ret;
+}
+
+int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits)
+{
+    GC_INIT(ctx);
+    int sz;
+
+    sz = (n_bits + 7) / 8;
+    bitmap->map = libxl__calloc(NOGC, sizeof(*bitmap->map), sz);
+    bitmap->size = sz;
+
+    GC_FREE;
+    return 0;
+}
+
+void libxl_bitmap_init(libxl_bitmap *map)
+{
+    memset(map, '\0', sizeof(*map));
+}
+
+void libxl_bitmap_dispose(libxl_bitmap *map)
+{
+    if (!map)
+        return;
+
+    free(map->map);
+    map->map = NULL;
+    map->size = 0;
+}
+
+void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
+                       const libxl_bitmap *sptr)
+{
+    int sz;
+
+    assert(dptr->size == sptr->size);
+    sz = dptr->size = sptr->size;
+    memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
+}
+
+/* This function copies X bytes from source to destination bitmap,
+ * where X is the smaller of the two sizes.
+ *
+ * If destination's size is larger than source, the extra bytes are
+ * untouched.
+ */
+void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
+                                    const libxl_bitmap *sptr)
+{
+    int sz;
+
+    sz = dptr->size < sptr->size ? dptr->size : sptr->size;
+    memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
+}
+
+void libxl_bitmap_copy_alloc(libxl_ctx *ctx,
+                             libxl_bitmap *dptr,
+                             const libxl_bitmap *sptr)
+{
+    GC_INIT(ctx);
+
+    dptr->map = libxl__calloc(NOGC, sptr->size, sizeof(*sptr->map));
+    dptr->size = sptr->size;
+    memcpy(dptr->map, sptr->map, sptr->size * sizeof(*sptr->map));
+
+    GC_FREE;
+}
+
+int libxl_bitmap_is_full(const libxl_bitmap *bitmap)
+{
+    int i;
+
+    for (i = 0; i < bitmap->size; i++)
+        if (bitmap->map[i] != (uint8_t)-1)
+            return 0;
+   return 1;
+}
+
+int libxl_bitmap_is_empty(const libxl_bitmap *bitmap)
+{
+    int i;
+
+    for (i = 0; i < bitmap->size; i++)
+        if (bitmap->map[i])
+            return 0;
+    return 1;
+}
+
+int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit)
+{
+    if (bit >= bitmap->size * 8)
+        return 0;
+    return (bitmap->map[bit / 8] & (1 << (bit & 7))) ? 1 : 0;
+}
+
+void libxl_bitmap_set(libxl_bitmap *bitmap, int bit)
+{
+    if (bit >= bitmap->size * 8)
+        return;
+    bitmap->map[bit / 8] |= 1 << (bit & 7);
+}
+
+void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit)
+{
+    if (bit >= bitmap->size * 8)
+        return;
+    bitmap->map[bit / 8] &= ~(1 << (bit & 7));
+}
+
+int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
+                    const libxl_bitmap *map1, const libxl_bitmap *map2)
+{
+    GC_INIT(ctx);
+    int rc;
+    uint32_t i;
+    const libxl_bitmap *large_map;
+    const libxl_bitmap *small_map;
+
+    if (map1->size > map2->size) {
+        large_map = map1;
+        small_map = map2;
+    } else {
+        large_map = map2;
+        small_map = map1;
+    }
+
+    rc = libxl_bitmap_alloc(ctx, or_map, large_map->size * 8);
+    if (rc)
+        goto out;
+
+    /*
+     *  If bitmaps aren't the same size, their union (logical or) will
+     *  be size of larger bit map.  Any bit past the end of the
+     *  smaller bit map, will match the larger one.
+     */
+    for (i = 0; i < small_map->size; i++)
+        or_map->map[i] = (small_map->map[i] | large_map->map[i]);
+
+    for (i = small_map->size; i < large_map->size; i++)
+        or_map->map[i] = large_map->map[i];
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
+                     const libxl_bitmap *map1, const libxl_bitmap *map2)
+{
+    GC_INIT(ctx);
+    int rc;
+    uint32_t i;
+    const libxl_bitmap *large_map;
+    const libxl_bitmap *small_map;
+
+    if (map1->size > map2->size) {
+        large_map = map1;
+        small_map = map2;
+    } else {
+        large_map = map2;
+        small_map = map1;
+    }
+
+    rc = libxl_bitmap_alloc(ctx, and_map, small_map->size * 8);
+    if (rc)
+        goto out;
+
+    /*
+     *  If bitmaps aren't same size, their 'and' will be size of
+     *  smaller bit map
+     */
+    for (i = 0; i < and_map->size; i++)
+        and_map->map[i] = (large_map->map[i] & small_map->map[i]);
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_bitmap_count_set(const libxl_bitmap *bitmap)
+{
+    int i, nr_set_bits = 0;
+    libxl_for_each_set_bit(i, *bitmap)
+        nr_set_bits++;
+
+    return nr_set_bits;
+}
+
+/* NB. caller is responsible for freeing the memory */
+char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap)
+{
+    GC_INIT(ctx);
+    int i = bitmap->size;
+    char *p = libxl__zalloc(NOGC, bitmap->size * 2 + 3);
+    char *q = p;
+    strncpy(p, "0x", 3);
+    p += 2;
+    while(--i >= 0) {
+        sprintf(p, "%02x", bitmap->map[i]);
+        p += 2;
+    }
+    *p = '\0';
+    GC_FREE;
+    return q;
+}
+
+int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus)
+{
+    GC_INIT(ctx);
+    int rc = 0;
+
+    if (max_cpus < 0) {
+        rc = ERROR_INVAL;
+        LOG(ERROR, "invalid number of cpus provided");
+        goto out;
+    }
+    if (max_cpus == 0)
+        max_cpus = libxl_get_max_cpus(ctx);
+    if (max_cpus < 0) {
+        LOG(ERROR, "failed to retrieve the maximum number of cpus");
+        rc = max_cpus;
+        goto out;
+    }
+    /* This can't fail: no need to check and log */
+    libxl_bitmap_alloc(ctx, cpumap, max_cpus);
+
+ out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
+                            int max_nodes)
+{
+    GC_INIT(ctx);
+    int rc = 0;
+
+    if (max_nodes < 0) {
+        rc = ERROR_INVAL;
+        LOG(ERROR, "invalid number of nodes provided");
+        goto out;
+    }
+
+    if (max_nodes == 0)
+        max_nodes = libxl_get_max_nodes(ctx);
+    if (max_nodes < 0) {
+        LOG(ERROR, "failed to retrieve the maximum number of nodes");
+        rc = max_nodes;
+        goto out;
+    }
+    /* This can't fail: no need to check and log */
+    libxl_bitmap_alloc(ctx, nodemap, max_nodes);
+
+ out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl__count_physical_sockets(libxl__gc *gc, int *sockets)
+{
+    int rc;
+    libxl_physinfo info;
+
+    libxl_physinfo_init(&info);
+
+    rc = libxl_get_physinfo(CTX, &info);
+    if (rc)
+        return rc;
+
+    *sockets = info.nr_cpus / info.threads_per_core
+                            / info.cores_per_socket;
+
+    libxl_physinfo_dispose(&info);
+    return 0;
+}
+
+int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
+                              int max_sockets)
+{
+    GC_INIT(ctx);
+    int rc = 0;
+
+    if (max_sockets < 0) {
+        rc = ERROR_INVAL;
+        LOG(ERROR, "invalid number of sockets provided");
+        goto out;
+    }
+
+    if (max_sockets == 0) {
+        rc = libxl__count_physical_sockets(gc, &max_sockets);
+        if (rc) {
+            LOGE(ERROR, "failed to get system socket count");
+            goto out;
+        }
+    }
+    /* This can't fail: no need to check and log */
+    libxl_bitmap_alloc(ctx, socketmap, max_sockets);
+
+ out:
+    GC_FREE;
+    return rc;
+
+}
+
+int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap)
+{
+    libxl_cputopology *tinfo = NULL;
+    int nr_cpus = 0, i, rc = 0;
+
+    tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
+    if (tinfo == NULL) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl_bitmap_set_none(socketmap);
+    for (i = 0; i < nr_cpus; i++)
+        if (tinfo[i].socket != XEN_INVALID_SOCKET_ID
+            && !libxl_bitmap_test(socketmap, tinfo[i].socket))
+            libxl_bitmap_set(socketmap, tinfo[i].socket);
+
+ out:
+    libxl_cputopology_list_free(tinfo, nr_cpus);
+    return rc;
+}
+
+int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
+                            const libxl_bitmap *nodemap,
+                            libxl_bitmap *cpumap)
+{
+    libxl_cputopology *tinfo = NULL;
+    int nr_cpus = 0, i, rc = 0;
+
+    tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
+    if (tinfo == NULL) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl_bitmap_set_none(cpumap);
+    for (i = 0; i < nr_cpus; i++) {
+        if (libxl_bitmap_test(nodemap, tinfo[i].node))
+            libxl_bitmap_set(cpumap, i);
+    }
+ out:
+    libxl_cputopology_list_free(tinfo, nr_cpus);
+    return rc;
+}
+
+int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
+                         libxl_bitmap *cpumap)
+{
+    libxl_bitmap nodemap;
+    int rc = 0;
+
+    libxl_bitmap_init(&nodemap);
+
+    rc = libxl_node_bitmap_alloc(ctx, &nodemap, 0);
+    if (rc)
+        goto out;
+
+    libxl_bitmap_set_none(&nodemap);
+    libxl_bitmap_set(&nodemap, node);
+
+    rc = libxl_nodemap_to_cpumap(ctx, &nodemap, cpumap);
+
+ out:
+    libxl_bitmap_dispose(&nodemap);
+    return rc;
+}
+
+int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
+                            const libxl_bitmap *cpumap,
+                            libxl_bitmap *nodemap)
+{
+    libxl_cputopology *tinfo = NULL;
+    int nr_cpus = 0, i, rc = 0;
+
+    tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
+    if (tinfo == NULL) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    libxl_bitmap_set_none(nodemap);
+    libxl_for_each_set_bit(i, *cpumap) {
+        if (i >= nr_cpus)
+            break;
+        libxl_bitmap_set(nodemap, tinfo[i].node);
+    }
+ out:
+    libxl_cputopology_list_free(tinfo, nr_cpus);
+    return rc;
+}
+
+int libxl_get_max_cpus(libxl_ctx *ctx)
+{
+    int max_cpus = xc_get_max_cpus(ctx->xch);
+
+    return max_cpus < 0 ? ERROR_FAIL : max_cpus;
+}
+
+int libxl_get_online_cpus(libxl_ctx *ctx)
+{
+    int online_cpus = xc_get_online_cpus(ctx->xch);
+
+    return online_cpus < 0 ? ERROR_FAIL : online_cpus;
+}
+
+int libxl_get_max_nodes(libxl_ctx *ctx)
+{
+    int max_nodes = xc_get_max_nodes(ctx->xch);
+
+    return max_nodes < 0 ? ERROR_FAIL : max_nodes;
+}
+
+int libxl__enum_from_string(const libxl_enum_string_table *t,
+                            const char *s, int *e)
+{
+    if (!t) return ERROR_INVAL;
+
+    for( ; t->s; t++) {
+        if (!strcasecmp(t->s, s)) {
+                *e = t->v;
+                return 0;
+        }
+    }
+    return ERROR_FAIL;
+}
+
+void libxl_cputopology_list_free(libxl_cputopology *list, int nr)
+{
+    int i;
+    for (i = 0; i < nr; i++)
+        libxl_cputopology_dispose(&list[i]);
+    free(list);
+}
+
+void libxl_pcitopology_list_free(libxl_pcitopology *list, int nr)
+{
+    int i;
+    for (i = 0; i < nr; i++)
+        libxl_pcitopology_dispose(&list[i]);
+    free(list);
+}
+
+void libxl_numainfo_list_free(libxl_numainfo *list, int nr)
+{
+    int i;
+    for (i = 0; i < nr; i++)
+        libxl_numainfo_dispose(&list[i]);
+    free(list);
+}
+
+void libxl_vcpuinfo_list_free(libxl_vcpuinfo *list, int nr)
+{
+    int i;
+    for (i = 0; i < nr; i++)
+        libxl_vcpuinfo_dispose(&list[i]);
+    free(list);
+}
+
+int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
+                       const char data,
+                       int nfds, const int fds[], const char *what) {
+    struct msghdr msg = { 0 };
+    struct cmsghdr *cmsg;
+    size_t spaceneeded = nfds * sizeof(fds[0]);
+    char control[CMSG_SPACE(spaceneeded)];
+    const size_t datalen = 1;
+    struct iovec iov;
+    int r;
+
+    iov.iov_base = (void*)&data;
+    iov.iov_len  = datalen;
+
+    /* compose the message */
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = control;
+    msg.msg_controllen = sizeof(control);
+
+    /* attach open fd */
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(spaceneeded);
+    memcpy(CMSG_DATA(cmsg), fds, spaceneeded);
+
+    msg.msg_controllen = cmsg->cmsg_len;
+
+    while (1) {
+        r = sendmsg(carrier, &msg, 0);
+        if (r < 0) {
+            if (errno == EINTR)
+                continue;
+            if (errno == EWOULDBLOCK) {
+                return ERROR_NOT_READY;
+            }
+            LOGE(ERROR, "failed to send fd-carrying message (%s)", what);
+            return ERROR_FAIL;
+        }
+        if (r != datalen) {
+            LOG(ERROR, "sendmsg have written %d instead of %zu",
+                r, datalen);
+            return ERROR_FAIL;
+        }
+        break;
+    };
+
+    return 0;
+}
+
+int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
+                       void *databuf, size_t datalen,
+                       int nfds, int fds[], const char *what)
+{
+    struct msghdr msg = { 0 };
+    struct cmsghdr *cmsg;
+    size_t spaceneeded = nfds * sizeof(fds[0]);
+    char control[CMSG_SPACE(spaceneeded)];
+    struct iovec iov;
+    int r;
+
+    iov.iov_base = databuf;
+    iov.iov_len  = datalen;
+
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = control;
+    msg.msg_controllen = sizeof(control);
+
+    for (;;) {
+        r = recvmsg(carrier, &msg, 0);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            if (errno == EWOULDBLOCK) return -1;
+            LOGE(ERROR,"recvmsg failed (%s)", what);
+            return ERROR_FAIL;
+        }
+        if (r == 0) {
+            LOG(ERROR,"recvmsg got EOF (%s)", what);
+            return ERROR_FAIL;
+        }
+        cmsg = CMSG_FIRSTHDR(&msg);
+        if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+            LOG(ERROR,"recvmsg got no control msg"
+                " when expecting fds (%s)", what);
+            return ERROR_FAIL;
+        }
+        if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+            LOG(ERROR, "recvmsg got unexpected"
+                " cmsg_level %d (!=%d) or _type %d (!=%d) (%s)",
+                cmsg->cmsg_level, SOL_SOCKET,
+                cmsg->cmsg_type, SCM_RIGHTS,
+                what);
+            return ERROR_FAIL;
+        }
+        if (cmsg->cmsg_len != CMSG_LEN(spaceneeded) ||
+            msg.msg_controllen != cmsg->cmsg_len) {
+            LOG(ERROR, "recvmsg got unexpected"
+                " number of fds or extra control data"
+                " (%ld bytes' worth, expected %ld) (%s)",
+                (long)CMSG_LEN(spaceneeded), (long)cmsg->cmsg_len,
+                what);
+            int i, fd;
+            unsigned char *p;
+            for (i=0, p=CMSG_DATA(cmsg);
+                 CMSG_SPACE(i * sizeof(fds[0]));
+                 i++, i+=sizeof(fd)) {
+                memcpy(&fd, p, sizeof(fd));
+                close(fd);
+            }
+            return ERROR_FAIL;
+        }
+        memcpy(fds, CMSG_DATA(cmsg), spaceneeded);
+        return 0;
+    }
+}         
+
+void libxl_dominfo_list_free(libxl_dominfo *list, int nr)
+{
+    int i;
+    for (i = 0; i < nr; i++)
+        libxl_dominfo_dispose(&list[i]);
+    free(list);
+}
+
+void libxl_vminfo_list_free(libxl_vminfo *list, int nr)
+{
+    int i;
+    for (i = 0; i < nr; i++)
+        libxl_vminfo_dispose(&list[i]);
+    free(list);
+}
+
+void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nr)
+{
+    int i;
+    for (i = 0; i < nr; i++)
+        libxl_cpupoolinfo_dispose(&list[i]);
+    free(list);
+}
+
+int libxl_domid_valid_guest(uint32_t domid)
+{
+    /* returns 1 if the value _could_ be a valid guest domid, 0 otherwise
+     * does not check whether the domain actually exists */
+    return domid > 0 && domid < DOMID_FIRST_RESERVED;
+}
+
+void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src)
+{
+    GC_INIT(ctx);
+
+    if (*src)
+        *dst = libxl__strdup(NOGC, *src);
+    else
+        *dst = NULL;
+
+    GC_FREE;
+}
+
+/*
+ * Fill @buf with @len random bytes.
+ */
+int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len)
+{
+    static const char *dev = "/dev/urandom";
+    int fd;
+    int ret;
+
+    fd = open(dev, O_RDONLY);
+    if (fd < 0) {
+        LOGE(ERROR, "failed to open \"%s\"", dev);
+        return ERROR_FAIL;
+    }
+    ret = libxl_fd_set_cloexec(CTX, fd, 1);
+    if (ret) {
+        close(fd);
+        return ERROR_FAIL;
+    }
+
+    ret = libxl_read_exactly(CTX, fd, buf, len, dev, NULL);
+
+    close(fd);
+
+    return ret;
+}
+
+int libxl__prepare_sockaddr_un(libxl__gc *gc,
+                               struct sockaddr_un *un, const char *path,
+                               const char *what)
+{
+    if (sizeof(un->sun_path) - 1 <= strlen(path)) {
+        LOG(ERROR, "UNIX socket path '%s' is too long for %s", path, what);
+        LOG(DEBUG, "Path must be less than %zu bytes", sizeof(un->sun_path) - 1);
+        return ERROR_INVAL;
+    }
+    memset(un, 0, sizeof(struct sockaddr_un));
+    un->sun_family = AF_UNIX;
+    strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_uuid.c b/tools/libs/light/libxl_uuid.c
new file mode 100644 (file)
index 0000000..dadb79b
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008,2010 Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+#if defined(__linux__)
+
+int libxl_uuid_is_nil(const libxl_uuid *uuid)
+{
+     return uuid_is_null(uuid->uuid);
+}
+
+void libxl_uuid_generate(libxl_uuid *uuid)
+{
+     uuid_generate(uuid->uuid);
+}
+
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
+{
+     return uuid_parse(in, uuid->uuid);
+}
+
+void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
+                     const libxl_uuid *src)
+{
+     uuid_copy(dst->uuid, src->uuid);
+}
+
+void libxl_uuid_clear(libxl_uuid *uuid)
+{
+     uuid_clear(uuid->uuid);
+}
+
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
+{
+     return uuid_compare(uuid1->uuid, uuid2->uuid);
+}
+
+const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
+{
+    return uuid->uuid;
+}
+
+uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
+{
+    return uuid->uuid;
+}
+
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+
+int libxl_uuid_is_nil(const libxl_uuid *uuid)
+{
+    uint32_t status;
+    uuid_t nat_uuid;
+
+    uuid_dec_be(uuid->uuid, &nat_uuid);
+
+    return uuid_is_nil(&nat_uuid, &status);
+}
+
+void libxl_uuid_generate(libxl_uuid *uuid)
+{
+    uint32_t status;
+    uuid_t nat_uuid;
+
+    uuid_create(&nat_uuid, &status);
+    assert(status == uuid_s_ok);
+
+    uuid_enc_be(uuid->uuid, &nat_uuid);
+}
+
+#ifdef __FreeBSD__
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
+{
+    uint32_t status;
+    uuid_t nat_uuid;
+
+    uuid_from_string(in, &nat_uuid, &status);
+    if (status != uuid_s_ok)
+        return ERROR_FAIL;
+    uuid_enc_be(uuid->uuid, &nat_uuid);
+
+    return 0;
+}
+#else
+#define LIBXL__UUID_PTRS(uuid) &uuid[0], &uuid[1], &uuid[2], &uuid[3], \
+                               &uuid[4], &uuid[5], &uuid[6], &uuid[7], \
+                               &uuid[8], &uuid[9], &uuid[10],&uuid[11], \
+                               &uuid[12],&uuid[13],&uuid[14],&uuid[15]
+int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
+{
+    if ( sscanf(in, LIBXL_UUID_FMT, LIBXL__UUID_PTRS(uuid->uuid)) != sizeof(uuid->uuid) )
+        return -1;
+    return 0;
+}
+#undef LIBXL__UUID_PTRS
+#endif
+
+void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
+                     const libxl_uuid *src)
+{
+    memcpy(&dst->uuid, &src->uuid, sizeof(dst->uuid));
+}
+
+void libxl_uuid_clear(libxl_uuid *uuid)
+{
+    memset(&uuid->uuid, 0, sizeof(uuid->uuid));
+}
+
+#ifdef __FreeBSD__
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
+{
+    uuid_t nat_uuid1, nat_uuid2;
+
+    uuid_dec_be(uuid1->uuid, &nat_uuid1);
+    uuid_dec_be(uuid2->uuid, &nat_uuid2);
+
+    return uuid_compare(&nat_uuid1, &nat_uuid2, NULL);
+}
+#else
+int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
+{
+     return memcmp(uuid1->uuid, uuid2->uuid, sizeof(uuid1->uuid));
+}
+#endif
+
+const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
+{
+
+    return uuid->uuid;
+}
+
+uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
+{
+
+    return uuid->uuid;
+}
+#else
+
+#error "Please update libxl_uuid.c for your OS"
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_vdispl.c b/tools/libs/light/libxl_vdispl.c
new file mode 100644 (file)
index 0000000..8ddc894
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 EPAM Systems Inc.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_internal.h"
+
+#include <xen/io/displif.h>
+
+static int libxl__device_vdispl_setdefault(libxl__gc *gc, uint32_t domid,
+                                           libxl_device_vdispl *vdispl,
+                                           bool hotplug)
+{
+    return libxl__resolve_domid(gc, vdispl->backend_domname,
+                                &vdispl->backend_domid);
+}
+
+static int libxl__vdispl_from_xenstore(libxl__gc *gc, const char *libxl_path,
+                                       libxl_devid devid,
+                                       libxl_device_vdispl *vdispl)
+{
+    const char *be_path;
+    int rc;
+
+    vdispl->devid = devid;
+    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+                                  GCSPRINTF("%s/backend", libxl_path),
+                                  &be_path);
+    if (rc) return rc;
+
+    return libxl__backendpath_parse_domid(gc, be_path, &vdispl->backend_domid);
+}
+
+static void libxl__update_config_vdispl(libxl__gc *gc,
+                                        libxl_device_vdispl *dst,
+                                        libxl_device_vdispl *src)
+{
+    dst->devid = src->devid;
+    dst->be_alloc = src->be_alloc;
+}
+
+static int libxl_device_vdispl_compare(const libxl_device_vdispl *d1,
+                                       const libxl_device_vdispl *d2)
+{
+    return COMPARE_DEVID(d1, d2);
+}
+
+static void libxl__device_vdispl_add(libxl__egc *egc, uint32_t domid,
+                                     libxl_device_vdispl *vdispl,
+                                     libxl__ao_device *aodev)
+{
+    libxl__device_add_async(egc, domid, &libxl__vdispl_devtype, vdispl, aodev);
+}
+
+static int libxl__set_xenstore_vdispl(libxl__gc *gc, uint32_t domid,
+                                      libxl_device_vdispl *vdispl,
+                                      flexarray_t *back, flexarray_t *front,
+                                      flexarray_t *ro_front)
+{
+    int i;
+
+    flexarray_append_pair(ro_front, XENDISPL_FIELD_BE_ALLOC,
+                          GCSPRINTF("%d", vdispl->be_alloc));
+
+    for (i = 0; i < vdispl->num_connectors; i++) {
+        flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_RESOLUTION, i),
+                              GCSPRINTF("%d"XENDISPL_RESOLUTION_SEPARATOR"%d", vdispl->connectors[i].width,
+                                                 vdispl->connectors[i].height));
+        flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_UNIQUE_ID, i),
+                              vdispl->connectors[i].unique_id);
+    }
+
+    return 0;
+}
+
+static int libxl__device_vdispl_getconnectors(libxl_ctx *ctx,
+                                              const char *path,
+                                              libxl_vdisplinfo *info)
+{
+    GC_INIT(ctx);
+    char *connector = NULL;
+    char *connector_path;
+    int i, rc;
+
+    info->num_connectors = 0;
+
+    connector_path = GCSPRINTF("%s/%d", path, info->num_connectors);
+
+    while ((connector = xs_read(ctx->xsh, XBT_NULL, connector_path, NULL)) !=
+           NULL) {
+        free(connector);
+        connector_path = GCSPRINTF("%s/%d", path, ++info->num_connectors);
+    }
+
+    info->connectors = libxl__calloc(NOGC, info->num_connectors,
+                                     sizeof(*info->connectors));
+
+    for (i = 0; i < info->num_connectors; i++) {
+        char *value;
+        char *value_path;
+
+        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_UNIQUE_ID, path, i);
+        info->connectors[i].unique_id = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+        if (info->connectors[i].unique_id == NULL) { rc = ERROR_FAIL; goto out; }
+
+        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_RESOLUTION, path, i);
+        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+        if (value == NULL) { rc = ERROR_FAIL; goto out; }
+
+        rc = sscanf(value, "%u"XENDISPL_RESOLUTION_SEPARATOR"%u", &info->connectors[i].width,
+                    &info->connectors[i].height);
+        free(value);
+
+        if (rc != 2) {
+            rc = ERROR_FAIL; goto out;
+        }
+
+        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_RING_REF, path, i);
+        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+        info->connectors[i].req_rref = value ? strtoul(value, NULL, 10) : -1;
+        free(value);
+
+        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_CHANNEL, path, i);
+        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+        info->connectors[i].req_evtch = value ? strtoul(value, NULL, 10) : -1;
+        free(value);
+
+        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_RING_REF, path, i);
+        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+        info->connectors[i].evt_rref = value ? strtoul(value, NULL, 10) : -1;
+        free(value);
+
+        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_CHANNEL, path, i);
+        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
+        info->connectors[i].evt_evtch = value ? strtoul(value, NULL, 10) : -1;
+        free(value);
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                const libxl_device_vdispl *vdispl,
+                                libxl_vdisplinfo *info)
+{
+    GC_INIT(ctx);
+    char *libxl_path, *devpath;
+    char *val;
+    int rc;
+
+    libxl_vdisplinfo_init(info);
+    info->devid = vdispl->devid;
+
+    devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
+                                                 LIBXL__DEVICE_KIND_VDISPL);
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
+                                                 LIBXL__DEVICE_KIND_VDISPL);
+
+    info->backend = xs_read(ctx->xsh, XBT_NULL,
+                            GCSPRINTF("%s/backend", libxl_path),
+                            NULL);
+    if (!info->backend) { rc = ERROR_FAIL; goto out; }
+
+    rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
+    if (rc) goto out;
+
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
+    info->state = val ? strtoul(val, NULL, 10) : -1;
+
+    info->frontend = xs_read(ctx->xsh, XBT_NULL,
+                             GCSPRINTF("%s/frontend", libxl_path),
+                             NULL);
+    info->frontend_id = domid;
+
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/"XENDISPL_FIELD_BE_ALLOC, devpath));
+    info->be_alloc = val ? strtoul(val, NULL, 10) : 0;
+
+    rc = libxl__device_vdispl_getconnectors(ctx, devpath, info);
+    if (rc) goto out;
+
+    rc = 0;
+
+out:
+     GC_FREE;
+     return rc;
+}
+
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(vdispl)
+static LIBXL_DEFINE_UPDATE_DEVID(vdispl)
+static LIBXL_DEFINE_DEVICES_ADD(vdispl)
+
+LIBXL_DEFINE_DEVID_TO_DEVICE(vdispl)
+LIBXL_DEFINE_DEVICE_ADD(vdispl)
+LIBXL_DEFINE_DEVICE_REMOVE(vdispl)
+LIBXL_DEFINE_DEVICE_LIST(vdispl)
+
+DEFINE_DEVICE_TYPE_STRUCT(vdispl, VDISPL,
+    .update_config = (device_update_config_fn_t)libxl__update_config_vdispl,
+    .from_xenstore = (device_from_xenstore_fn_t)libxl__vdispl_from_xenstore,
+    .set_xenstore_config = (device_set_xenstore_config_fn_t)
+                           libxl__set_xenstore_vdispl
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_vkb.c b/tools/libs/light/libxl_vkb.c
new file mode 100644 (file)
index 0000000..4c44a81
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2016 EPAM Systems Inc.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_internal.h"
+
+#include <xen/io/kbdif.h>
+
+static int libxl__device_vkb_setdefault(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_vkb *vkb, bool hotplug)
+{
+    if (vkb->backend_type == LIBXL_VKB_BACKEND_UNKNOWN) {
+        vkb->backend_type = LIBXL_VKB_BACKEND_QEMU;
+    }
+
+    return libxl__resolve_domid(gc, vkb->backend_domname, &vkb->backend_domid);
+}
+
+static int libxl__device_vkb_dm_needed(void *e, uint32_t domid)
+{
+    libxl_device_vkb *elem = e;
+
+    return elem->backend_type == LIBXL_VKB_BACKEND_QEMU;
+}
+
+static int libxl__set_xenstore_vkb(libxl__gc *gc, uint32_t domid,
+                                   libxl_device_vkb *vkb,
+                                   flexarray_t *back, flexarray_t *front,
+                                   flexarray_t *ro_front)
+{
+    if (vkb->unique_id) {
+        flexarray_append_pair(back, XENKBD_FIELD_UNIQUE_ID, vkb->unique_id);
+    }
+
+    if (vkb->feature_disable_keyboard) {
+        flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_KEYBRD,
+                              GCSPRINTF("%u", vkb->feature_disable_keyboard));
+    }
+
+    if (vkb->feature_disable_pointer) {
+        flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_POINTER,
+                              GCSPRINTF("%u", vkb->feature_disable_pointer));
+    }
+
+    if (vkb->feature_abs_pointer) {
+        flexarray_append_pair(back, XENKBD_FIELD_FEAT_ABS_POINTER,
+                              GCSPRINTF("%u", vkb->feature_abs_pointer));
+    }
+
+    if (vkb->feature_raw_pointer) {
+        flexarray_append_pair(back, XENKBD_FIELD_FEAT_RAW_POINTER,
+                              GCSPRINTF("%u", vkb->feature_raw_pointer));
+    }
+
+    if (vkb->feature_multi_touch) {
+        flexarray_append_pair(back, XENKBD_FIELD_FEAT_MTOUCH,
+                              GCSPRINTF("%u", vkb->feature_multi_touch));
+        flexarray_append_pair(back, XENKBD_FIELD_MT_WIDTH,
+                              GCSPRINTF("%u", vkb->multi_touch_width));
+        flexarray_append_pair(back, XENKBD_FIELD_MT_HEIGHT,
+                              GCSPRINTF("%u", vkb->multi_touch_height));
+        flexarray_append_pair(back, XENKBD_FIELD_MT_NUM_CONTACTS,
+                              GCSPRINTF("%u", vkb->multi_touch_num_contacts));
+    }
+
+    if (vkb->width) {
+        flexarray_append_pair(back, XENKBD_FIELD_WIDTH,
+                              GCSPRINTF("%u", vkb->width));
+    }
+
+    if (vkb->height) {
+        flexarray_append_pair(back, XENKBD_FIELD_HEIGHT,
+                              GCSPRINTF("%u", vkb->height));
+    }
+
+    return 0;
+}
+
+static int libxl__vkb_from_xenstore(libxl__gc *gc, const char *libxl_path,
+                                    libxl_devid devid,
+                                    libxl_device_vkb *vkb)
+{
+    const char *be_path, *fe_path, *tmp;
+    libxl__device dev;
+    int rc;
+
+    vkb->devid = devid;
+
+    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+                                  GCSPRINTF("%s/backend", libxl_path),
+                                  &be_path);
+    if (rc) goto out;
+
+    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+                                  GCSPRINTF("%s/frontend", libxl_path),
+                                  &fe_path);
+    if (rc) goto out;
+
+    rc = libxl__backendpath_parse_domid(gc, be_path, &vkb->backend_domid);
+    if (rc) goto out;
+
+    rc = libxl__parse_backend_path(gc, be_path, &dev);
+    if (rc) goto out;
+
+    vkb->backend_type = dev.backend_kind == LIBXL__DEVICE_KIND_VINPUT ?
+                                            LIBXL_VKB_BACKEND_LINUX : LIBXL_VKB_BACKEND_QEMU;
+
+    vkb->unique_id = xs_read(CTX->xsh, XBT_NULL, GCSPRINTF("%s/"XENKBD_FIELD_UNIQUE_ID, be_path), NULL);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_KEYBRD,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->feature_disable_keyboard = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_POINTER,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->feature_disable_pointer = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_ABS_POINTER,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->feature_abs_pointer = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_RAW_POINTER,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->feature_raw_pointer = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_MTOUCH,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->feature_multi_touch = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_MT_WIDTH,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->multi_touch_width = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_MT_HEIGHT,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->multi_touch_height = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_MT_NUM_CONTACTS,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->multi_touch_num_contacts = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_WIDTH,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->width = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENKBD_FIELD_HEIGHT,
+                                be_path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        vkb->height = strtoul(tmp, NULL, 0);
+    }
+
+    rc = 0;
+
+out:
+
+    return rc;
+}
+
+static int libxl__device_from_vkb(libxl__gc *gc, uint32_t domid,
+                                  libxl_device_vkb *type, libxl__device *device)
+{
+    device->backend_devid   = type->devid;
+    device->backend_domid   = type->backend_domid;
+    device->backend_kind    = type->backend_type == LIBXL_VKB_BACKEND_LINUX ?
+                              LIBXL__DEVICE_KIND_VINPUT : LIBXL__DEVICE_KIND_VKBD;
+    device->devid           = type->devid;
+    device->domid           = domid;
+    device->kind            = LIBXL__DEVICE_KIND_VKBD;
+
+    return 0;
+}
+
+int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    rc = libxl__device_add(gc, domid, &libxl__vkb_devtype, vkb);
+    if (rc) {
+        LOGD(ERROR, domid, "Unable to add vkb device");
+        goto out;
+    }
+
+out:
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
+                              int devid, libxl_device_vkb *vkb)
+{
+    GC_INIT(ctx);
+
+    libxl_device_vkb *vkbs = NULL;
+    int n, i;
+    int rc;
+
+    libxl_device_vkb_init(vkb);
+
+    vkbs = libxl__device_list(gc, &libxl__vkb_devtype, domid, &n);
+
+    if (!vkbs) { rc = ERROR_NOTFOUND; goto out; }
+
+    for (i = 0; i < n; ++i) {
+        if (devid == vkbs[i].devid) {
+            libxl_device_vkb_copy(ctx, vkb, &vkbs[i]);
+            rc = 0;
+            goto out;
+        }
+    }
+
+    rc = ERROR_NOTFOUND;
+
+out:
+
+    if (vkbs)
+        libxl__device_list_free(&libxl__vkb_devtype, vkbs, n);
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
+                             const libxl_device_vkb *vkb,
+                             libxl_vkbinfo *info)
+{
+    GC_INIT(ctx);
+    char *libxl_path, *dompath, *devpath;
+    char *val;
+    int rc;
+
+    libxl_vkbinfo_init(info);
+    dompath = libxl__xs_get_dompath(gc, domid);
+    info->devid = vkb->devid;
+
+    devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
+                                                 LIBXL__DEVICE_KIND_VKBD);
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
+                                                 LIBXL__DEVICE_KIND_VKBD);
+
+    info->backend = xs_read(ctx->xsh, XBT_NULL,
+                            GCSPRINTF("%s/backend", libxl_path),
+                            NULL);
+    if (!info->backend) { rc = ERROR_FAIL; goto out; }
+
+    rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
+    if (rc) goto out;
+
+    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
+    info->state = val ? strtoul(val, NULL, 10) : -1;
+
+    info->frontend = xs_read(ctx->xsh, XBT_NULL,
+                             GCSPRINTF("%s/frontend", libxl_path),
+                             NULL);
+    info->frontend_id = domid;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/"XENKBD_FIELD_EVT_CHANNEL, devpath));
+    info->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/"XENKBD_FIELD_RING_GREF, devpath));
+    info->rref = val ? strtoul(val, NULL, 10) : -1;
+
+    rc = 0;
+
+out:
+     GC_FREE;
+     return rc;
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(vkb)
+
+#define libxl__add_vkbs NULL
+#define libxl_device_vkb_compare NULL
+
+LIBXL_DEFINE_DEVICE_LIST(vkb)
+LIBXL_DEFINE_DEVICE_REMOVE(vkb)
+
+DEFINE_DEVICE_TYPE_STRUCT(vkb, VKBD,
+    .skip_attach = 1,
+    .dm_needed = libxl__device_vkb_dm_needed,
+    .set_xenstore_config = (device_set_xenstore_config_fn_t)
+                           libxl__set_xenstore_vkb,
+    .from_xenstore = (device_from_xenstore_fn_t)libxl__vkb_from_xenstore
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_vnuma.c b/tools/libs/light/libxl_vnuma.c
new file mode 100644 (file)
index 0000000..c2e144c
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2014      Citrix Ltd.
+ * Author Wei Liu <wei.liu2@citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+#include "libxl_osdeps.h" /* must come before any other headers */
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+#include <stdlib.h>
+
+#include <xenctrl_dom.h>
+
+bool libxl__vnuma_configured(const libxl_domain_build_info *b_info)
+{
+    return b_info->num_vnuma_nodes != 0;
+}
+
+/* Sort vmemranges in ascending order with "start" */
+static int compare_vmemrange(const void *a, const void *b)
+{
+    const xen_vmemrange_t *x = a, *y = b;
+    if (x->start < y->start)
+        return -1;
+    if (x->start > y->start)
+        return 1;
+    return 0;
+}
+
+/* Check if a vcpu has an hard (or soft) affinity set in such
+ * a way that it does not match the pnode to which the vcpu itself
+ * is assigned to.
+ */
+static int check_vnuma_affinity(libxl__gc *gc,
+                                 unsigned int vcpu,
+                                 unsigned int pnode,
+                                 unsigned int num_affinity,
+                                 const libxl_bitmap *affinity,
+                                 const char *kind)
+{
+    libxl_bitmap nodemap;
+    int rc = 0;
+
+    libxl_bitmap_init(&nodemap);
+
+    rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
+    if (rc) {
+        LOG(ERROR, "Can't allocate nodemap");
+        goto out;
+    }
+
+    rc = libxl_cpumap_to_nodemap(CTX, affinity, &nodemap);
+    if (rc) {
+        LOG(ERROR, "Can't convert Vcpu %d affinity to nodemap", vcpu);
+        goto out;
+    }
+
+    if (libxl_bitmap_count_set(&nodemap) != 1 ||
+        !libxl_bitmap_test(&nodemap, pnode))
+        LOG(WARN, "Vcpu %d %s affinity and vnuma info mismatch", vcpu, kind);
+
+out:
+    libxl_bitmap_dispose(&nodemap);
+    return rc;
+}
+
+/* Check if vNUMA configuration is valid:
+ *  1. all pnodes inside vnode_to_pnode array are valid
+ *  2. each vcpu belongs to one and only one vnode
+ *  3. each vmemrange is valid and doesn't overlap with any other
+ *  4. local distance cannot be larger than remote distance
+ *
+ * Check also, if any hard or soft affinity is specified, whether
+ * they match with the vNUMA related bits (namely vcpus to vnodes
+ * mappings and vnodes to pnodes association). If that does not
+ * hold, however, just print a warning, as that has "only"
+ * performance implications.
+ */
+int libxl__vnuma_config_check(libxl__gc *gc,
+                              const libxl_domain_build_info *b_info,
+                              const libxl__domain_build_state *state)
+{
+    int nr_nodes = 0, rc = ERROR_VNUMA_CONFIG_INVALID;
+    unsigned int i, j;
+    libxl_numainfo *ninfo = NULL;
+    uint64_t total_memkb = 0;
+    libxl_bitmap cpumap;
+    libxl_vnode_info *v;
+
+    libxl_bitmap_init(&cpumap);
+
+    /* Check pnode specified is valid */
+    ninfo = libxl_get_numainfo(CTX, &nr_nodes);
+    if (!ninfo) {
+        LOG(ERROR, "libxl_get_numainfo failed");
+        goto out;
+    }
+
+    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+        uint32_t pnode;
+
+        v = &b_info->vnuma_nodes[i];
+        pnode = v->pnode;
+
+        /* The pnode specified is not valid? */
+        if (pnode >= nr_nodes) {
+            LOG(ERROR, "Invalid pnode %"PRIu32" specified", pnode);
+            goto out;
+        }
+
+        total_memkb += v->memkb;
+    }
+
+    if (total_memkb != b_info->max_memkb) {
+        LOG(ERROR, "Amount of memory mismatch (0x%"PRIx64" != 0x%"PRIx64")",
+            total_memkb, b_info->max_memkb);
+        goto out;
+    }
+
+    /* Check vcpu mapping */
+    libxl_cpu_bitmap_alloc(CTX, &cpumap, b_info->max_vcpus);
+    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+        v = &b_info->vnuma_nodes[i];
+        libxl_for_each_set_bit(j, v->vcpus) {
+            if (!libxl_bitmap_test(&cpumap, j))
+                libxl_bitmap_set(&cpumap, j);
+            else {
+                LOG(ERROR, "Vcpu %d assigned more than once", j);
+                goto out;
+            }
+        }
+    }
+
+    for (i = 0; i < b_info->max_vcpus; i++) {
+        if (!libxl_bitmap_test(&cpumap, i)) {
+            LOG(ERROR, "Vcpu %d is not assigned to any vnode", i);
+            goto out;
+        }
+    }
+
+    /* Check whether vcpu affinity (if any) matches vnuma configuration */
+    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+        v = &b_info->vnuma_nodes[i];
+        libxl_for_each_set_bit(j, v->vcpus) {
+            if (b_info->num_vcpu_hard_affinity > j)
+                check_vnuma_affinity(gc, j, v->pnode,
+                                     b_info->num_vcpu_hard_affinity,
+                                     &b_info->vcpu_hard_affinity[j],
+                                     "hard");
+            if (b_info->num_vcpu_soft_affinity > j)
+                check_vnuma_affinity(gc, j, v->pnode,
+                                     b_info->num_vcpu_soft_affinity,
+                                     &b_info->vcpu_soft_affinity[j],
+                                     "soft");
+        }
+    }
+
+    /* Check vmemranges */
+    qsort(state->vmemranges, state->num_vmemranges, sizeof(xen_vmemrange_t),
+          compare_vmemrange);
+
+    for (i = 0; i < state->num_vmemranges; i++) {
+        if (state->vmemranges[i].end < state->vmemranges[i].start) {
+                LOG(ERROR, "Vmemrange end < start");
+                goto out;
+        }
+    }
+
+    for (i = 0; i < state->num_vmemranges - 1; i++) {
+        if (state->vmemranges[i].end > state->vmemranges[i+1].start) {
+            LOG(ERROR,
+                "Vmemranges overlapped, 0x%"PRIx64"-0x%"PRIx64", 0x%"PRIx64"-0x%"PRIx64,
+                state->vmemranges[i].start, state->vmemranges[i].end,
+                state->vmemranges[i+1].start, state->vmemranges[i+1].end);
+            goto out;
+        }
+    }
+
+    /* Check vdistances */
+    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+        uint32_t local_distance, remote_distance;
+
+        v = &b_info->vnuma_nodes[i];
+        local_distance = v->distances[i];
+
+        for (j = 0; j < v->num_distances; j++) {
+            if (i == j) continue;
+            remote_distance = v->distances[j];
+            if (local_distance > remote_distance) {
+                LOG(ERROR,
+                    "Distance from %u to %u smaller than %u's local distance",
+                    i, j, i);
+                goto out;
+            }
+        }
+    }
+
+    rc = 0;
+out:
+    libxl_numainfo_list_free(ninfo, nr_nodes);
+    libxl_bitmap_dispose(&cpumap);
+    return rc;
+}
+
+int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
+                                            uint32_t domid,
+                                            libxl_domain_build_info *b_info,
+                                            libxl__domain_build_state *state)
+{
+    int i;
+    uint64_t next;
+    xen_vmemrange_t *v = NULL;
+
+    /* Generate one vmemrange for each virtual node. */
+    GCREALLOC_ARRAY(v, b_info->num_vnuma_nodes);
+    next = 0;
+    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
+        libxl_vnode_info *p = &b_info->vnuma_nodes[i];
+
+        v[i].start = next;
+        v[i].end = next + (p->memkb << 10);
+        v[i].flags = 0;
+        v[i].nid = i;
+
+        next = v[i].end;
+    }
+
+    state->vmemranges = v;
+    state->num_vmemranges = i;
+
+    return 0;
+}
+
+/* Build vmemranges for PV guest */
+int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
+                                    uint32_t domid,
+                                    libxl_domain_build_info *b_info,
+                                    libxl__domain_build_state *state)
+{
+    assert(state->vmemranges == NULL);
+    return libxl__arch_vnuma_build_vmemrange(gc, domid, b_info, state);
+}
+
+/* Build vmemranges for HVM guest */
+int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
+                                     uint32_t domid,
+                                     libxl_domain_build_info *b_info,
+                                     libxl__domain_build_state *state,
+                                     struct xc_dom_image *dom)
+{
+    uint64_t hole_start, hole_end, next;
+    int nid, nr_vmemrange;
+    xen_vmemrange_t *vmemranges;
+    int rc;
+
+    /* Derive vmemranges from vnode size and memory hole.
+     *
+     * Guest physical address space layout:
+     * [0, hole_start) [hole_start, hole_end) [hole_end, highmem_end)
+     */
+    hole_start = dom->lowmem_end < dom->mmio_start ?
+        dom->lowmem_end : dom->mmio_start;
+    hole_end = (dom->mmio_start + dom->mmio_size) > (1ULL << 32) ?
+        (dom->mmio_start + dom->mmio_size) : (1ULL << 32);
+
+    assert(state->vmemranges == NULL);
+
+    next = 0;
+    nr_vmemrange = 0;
+    vmemranges = NULL;
+    for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
+        libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
+        uint64_t remaining_bytes = p->memkb << 10;
+
+        /* Consider video ram belongs to vnode 0 */
+        if (nid == 0) {
+            if (p->memkb < b_info->video_memkb) {
+                LOGD(ERROR, domid, "vnode 0 too small to contain video ram");
+                rc = ERROR_INVAL;
+                goto out;
+            }
+            remaining_bytes -= (b_info->video_memkb << 10);
+        }
+
+        while (remaining_bytes > 0) {
+            uint64_t count = remaining_bytes;
+
+            if (next >= hole_start && next < hole_end)
+                next = hole_end;
+            if ((next < hole_start) && (next + remaining_bytes >= hole_start))
+                count = hole_start - next;
+
+            GCREALLOC_ARRAY(vmemranges, nr_vmemrange+1);
+            vmemranges[nr_vmemrange].start = next;
+            vmemranges[nr_vmemrange].end = next + count;
+            vmemranges[nr_vmemrange].flags = 0;
+            vmemranges[nr_vmemrange].nid = nid;
+
+            nr_vmemrange++;
+            remaining_bytes -= count;
+            next += count;
+        }
+    }
+
+    state->vmemranges = vmemranges;
+    state->num_vmemranges = nr_vmemrange;
+
+    rc = 0;
+out:
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_vsnd.c b/tools/libs/light/libxl_vsnd.c
new file mode 100644 (file)
index 0000000..0bc5f6d
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2016 EPAM Systems Inc.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_internal.h"
+
+#include <xen/io/sndif.h>
+
+static int libxl__device_vsnd_setdefault(libxl__gc *gc, uint32_t domid,
+                                         libxl_device_vsnd *vsnd,
+                                         bool hotplug)
+{
+    return libxl__resolve_domid(gc, vsnd->backend_domname,
+                                &vsnd->backend_domid);
+}
+
+static int libxl__device_from_vsnd(libxl__gc *gc, uint32_t domid,
+                                   libxl_device_vsnd *vsnd,
+                                   libxl__device *device)
+{
+   device->backend_devid   = vsnd->devid;
+   device->backend_domid   = vsnd->backend_domid;
+   device->backend_kind    = LIBXL__DEVICE_KIND_VSND;
+   device->devid           = vsnd->devid;
+   device->domid           = domid;
+   device->kind            = LIBXL__DEVICE_KIND_VSND;
+
+   return 0;
+}
+
+static int libxl__sample_rates_from_string(libxl__gc *gc, const char *str,
+                                           libxl_vsnd_params *params)
+{
+    char *tmp = libxl__strdup(gc, str);
+
+    params->num_sample_rates = 0;
+    params->sample_rates = NULL;
+
+    char *p = strtok(tmp, " ,");
+
+    while (p != NULL) {
+        params->sample_rates = libxl__realloc(NOGC, params->sample_rates,
+                                              sizeof(*params->sample_rates) *
+                                              (params->num_sample_rates + 1));
+        params->sample_rates[params->num_sample_rates++] = strtoul(p, NULL, 0);
+        p = strtok(NULL, " ,");
+    }
+
+    return 0;
+}
+
+static int libxl__sample_formats_from_string(libxl__gc *gc, const char *str,
+                                             libxl_vsnd_params *params)
+{
+    int rc;
+    char *tmp = libxl__strdup(gc, str);
+
+    params->num_sample_formats = 0;
+    params->sample_formats = NULL;
+
+    char *p = strtok(tmp, " ,");
+
+    while (p != NULL) {
+        params->sample_formats = libxl__realloc(NOGC, params->sample_formats,
+                                                sizeof(*params->sample_formats) *
+                                                (params->num_sample_formats + 1));
+
+        libxl_vsnd_pcm_format format;
+
+        rc = libxl_vsnd_pcm_format_from_string(p, &format);
+        if (rc) goto out;
+
+        params->sample_formats[params->num_sample_formats++] = format;
+        p = strtok(NULL, " ,");
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__params_from_xenstore(libxl__gc *gc, const char *path,
+                                       libxl_vsnd_params *params)
+{
+    const char *tmp;
+    int rc;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_RATES,
+                                          path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        rc = libxl__sample_rates_from_string(gc, tmp, params);
+        if (rc) goto out;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_FORMATS,
+                                          path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        rc = libxl__sample_formats_from_string(gc, tmp, params);
+        if (rc) goto out;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                 GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MIN,
+                                           path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        params->channels_min = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                 GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MAX,
+                                           path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        params->channels_max = strtoul(tmp, NULL, 0);
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                 GCSPRINTF("%s/"XENSND_FIELD_BUFFER_SIZE,
+                                           path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        params->buffer_size = strtoul(tmp, NULL, 0);
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__stream_from_xenstore(libxl__gc *gc, const char *path,
+                                       libxl_vsnd_stream *stream)
+{
+    const char *tmp;
+    int rc;
+
+    stream->unique_id = xs_read(CTX->xsh, XBT_NULL,
+                         GCSPRINTF("%s/"XENSND_FIELD_STREAM_UNIQUE_ID,
+                         path), NULL);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/"XENSND_FIELD_TYPE,
+                                          path), &tmp);
+    if (rc) goto out;
+
+    if (tmp) {
+        libxl_vsnd_stream_type type;
+
+        rc = libxl_vsnd_stream_type_from_string(tmp, &type);
+        if (rc) goto out;
+
+        stream->type = type;
+    }
+
+    rc = libxl__params_from_xenstore(gc, path, &stream->params);
+    if (rc) goto out;
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__pcm_from_xenstore(libxl__gc *gc, const char *path,
+                                    libxl_vsnd_pcm *pcm)
+{
+    const char *tmp;
+    int rc;
+
+    pcm->name = xs_read(CTX->xsh, XBT_NULL,
+                        GCSPRINTF("%s/"XENSND_FIELD_DEVICE_NAME, path), NULL);
+
+    rc = libxl__params_from_xenstore(gc, path, &pcm->params);
+    if (rc) goto out;
+
+    pcm->streams = NULL;
+    pcm->num_vsnd_streams = 0;
+
+    do {
+        char *stream_path = GCSPRINTF("%s/%d", path, pcm->num_vsnd_streams);
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL, stream_path, &tmp);
+        if (rc) goto out;
+
+        if (tmp) {
+            pcm->streams = libxl__realloc(NOGC, pcm->streams,
+                                          sizeof(*pcm->streams) *
+                                          (++pcm->num_vsnd_streams));
+
+            libxl_vsnd_stream_init(&pcm->streams[pcm->num_vsnd_streams - 1]);
+
+            rc = libxl__stream_from_xenstore(gc, stream_path,
+                                             &pcm->streams[pcm->num_vsnd_streams
+                                             - 1]);
+            if (rc) goto out;
+        }
+    } while (tmp);
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__vsnd_from_xenstore(libxl__gc *gc, const char *libxl_path,
+                                     libxl_devid devid,
+                                     libxl_device_vsnd *vsnd)
+{
+    const char *tmp;
+    const char *fe_path;
+    int rc;
+
+    vsnd->devid = devid;
+    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+                                  GCSPRINTF("%s/backend", libxl_path),
+                                  &tmp);
+    if (rc) goto out;
+
+    rc = libxl__backendpath_parse_domid(gc, tmp, &vsnd->backend_domid);
+    if (rc) goto out;
+
+    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+                                  GCSPRINTF("%s/frontend", libxl_path),
+                                  &fe_path);
+    if (rc) goto out;
+
+    vsnd->short_name = xs_read(CTX->xsh, XBT_NULL,
+                               GCSPRINTF("%s/"XENSND_FIELD_VCARD_SHORT_NAME,
+                               fe_path), NULL);
+
+    vsnd->long_name = xs_read(CTX->xsh, XBT_NULL,
+                              GCSPRINTF("%s/"XENSND_FIELD_VCARD_LONG_NAME,
+                              fe_path), NULL);
+
+    rc = libxl__params_from_xenstore(gc, fe_path, &vsnd->params);
+    if (rc) goto out;
+
+    vsnd->pcms = NULL;
+    vsnd->num_vsnd_pcms = 0;
+
+    do {
+        char *pcm_path = GCSPRINTF("%s/%d", fe_path, vsnd->num_vsnd_pcms);
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL, pcm_path, &tmp);
+        if (rc) goto out;
+
+        if (tmp) {
+            vsnd->pcms = libxl__realloc(NOGC, vsnd->pcms, sizeof(*vsnd->pcms) *
+                                        (++vsnd->num_vsnd_pcms));
+
+            libxl_vsnd_pcm_init(&vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
+
+            rc = libxl__pcm_from_xenstore(gc, pcm_path,
+                                          &vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
+            if (rc) goto out;
+        }
+    } while (tmp);
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static void libxl__update_config_vsnd(libxl__gc *gc,
+                                      libxl_device_vsnd *dst,
+                                      libxl_device_vsnd *src)
+{
+    dst->devid = src->devid;
+}
+
+static int libxl_device_vsnd_compare(const libxl_device_vsnd *d1,
+                                     const libxl_device_vsnd *d2)
+{
+    return COMPARE_DEVID(d1, d2);
+}
+
+static void libxl__device_vsnd_add(libxl__egc *egc, uint32_t domid,
+                                   libxl_device_vsnd *vsnd,
+                                   libxl__ao_device *aodev)
+{
+    libxl__device_add_async(egc, domid, &libxl__vsnd_devtype, vsnd, aodev);
+}
+
+static unsigned int libxl__rates_to_str_vsnd(char *str, uint32_t *sample_rates,
+                                             int num_sample_rates)
+{
+    unsigned int len;
+    int i;
+
+    len = 0;
+
+    if (num_sample_rates == 0) goto out;
+
+    for (i = 0; i < num_sample_rates - 1; i++) {
+        if (str) {
+            len += sprintf(&str[len], "%u,", sample_rates[i]);
+        } else {
+            len += snprintf(NULL, 0, "%u,", sample_rates[i]);
+        }
+    }
+
+    if (str) {
+        len += sprintf(&str[len], "%u", sample_rates[i]);
+    } else {
+        len += snprintf(NULL, 0, "%u", sample_rates[i]);
+    }
+
+out:
+    return len;
+}
+
+static unsigned int libxl__formats_to_str_vsnd(char *str,
+                                               libxl_vsnd_pcm_format *sample_formats,
+                                               int num_sample_formats)
+{
+    unsigned int len;
+    int i;
+
+    len = 0;
+
+    if (num_sample_formats == 0) goto out;
+
+    for (i = 0; i < num_sample_formats - 1; i++) {
+        if (str) {
+            len += sprintf(&str[len], "%s,",
+                           libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+        } else {
+            len += snprintf(NULL, 0, "%s,",
+                            libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+        }
+    }
+
+    if (str) {
+        len += sprintf(&str[len], "%s",
+                       libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+    } else {
+        len += snprintf(NULL, 0, "%s",
+                        libxl_vsnd_pcm_format_to_string(sample_formats[i]));
+    }
+
+out:
+    return len;
+}
+
+static int libxl__set_params_vsnd(libxl__gc *gc, char *path,
+                                  libxl_vsnd_params *params, flexarray_t *front)
+{
+    char *buffer;
+    int len;
+    int rc;
+
+    if (params->sample_rates) {
+        /* calculate required string size */
+        len = libxl__rates_to_str_vsnd(NULL, params->sample_rates,
+                                       params->num_sample_rates);
+
+        if (len) {
+            buffer = libxl__malloc(gc, len + 1);
+
+            libxl__rates_to_str_vsnd(buffer, params->sample_rates,
+                                     params->num_sample_rates);
+            rc = flexarray_append_pair(front,
+                                       GCSPRINTF("%s"XENSND_FIELD_SAMPLE_RATES,
+                                                 path), buffer);
+            if (rc) goto out;
+        }
+    }
+
+    if (params->sample_formats) {
+        /* calculate required string size */
+        len = libxl__formats_to_str_vsnd(NULL, params->sample_formats,
+                                         params->num_sample_formats);
+
+        if (len) {
+            buffer = libxl__malloc(gc, len + 1);
+
+            libxl__formats_to_str_vsnd(buffer, params->sample_formats,
+                                     params->num_sample_formats);
+            rc = flexarray_append_pair(front,
+                                       GCSPRINTF("%s"XENSND_FIELD_SAMPLE_FORMATS,
+                                                 path), buffer);
+            if (rc) goto out;
+        }
+    }
+
+    if (params->channels_min) {
+        rc = flexarray_append_pair(front,
+                                   GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MIN, path),
+                                   GCSPRINTF("%u", params->channels_min));
+        if (rc) goto out;
+    }
+
+    if (params->channels_max) {
+        rc = flexarray_append_pair(front,
+                                   GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MAX, path),
+                                   GCSPRINTF("%u", params->channels_max));
+        if (rc) goto out;
+    }
+
+    if (params->buffer_size) {
+        rc = flexarray_append_pair(front,
+                                   GCSPRINTF("%s"XENSND_FIELD_BUFFER_SIZE, path),
+                                   GCSPRINTF("%u", params->buffer_size));
+        if (rc) goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__set_streams_vsnd(libxl__gc *gc, char *path,
+                                   libxl_vsnd_stream *streams,
+                                   int num_streams, flexarray_t *front)
+{
+    int i;
+    int rc;
+
+    for (i = 0; i < num_streams; i++) {
+        rc = flexarray_append_pair(front,
+                 GCSPRINTF("%s%d/"XENSND_FIELD_STREAM_UNIQUE_ID, path, i),
+                 streams[i].unique_id);
+        if (rc) goto out;
+
+        const char *type = libxl_vsnd_stream_type_to_string(streams[i].type);
+
+        if (type) {
+            rc = flexarray_append_pair(front,
+                     GCSPRINTF("%s%d/"XENSND_FIELD_TYPE, path, i),
+                     (char *)type);
+            if (rc) goto out;
+        }
+
+        rc = libxl__set_params_vsnd(gc, GCSPRINTF("%s%d/", path, i),
+                                    &streams[i].params, front);
+        if (rc) goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__set_pcms_vsnd(libxl__gc *gc, libxl_vsnd_pcm *pcms,
+                                int num_pcms, flexarray_t *front)
+{
+    int i;
+    int rc;
+
+    for (i = 0; i < num_pcms; i++) {
+        if (pcms[i].name) {
+            rc = flexarray_append_pair(front,
+                                       GCSPRINTF("%d/"XENSND_FIELD_DEVICE_NAME, i),
+                                       pcms[i].name);
+            if (rc) goto out;
+        }
+
+        char *path = GCSPRINTF("%d/", i);
+
+        rc = libxl__set_params_vsnd(gc, path, &pcms[i].params, front);
+        if (rc) goto out;
+
+        rc = libxl__set_streams_vsnd(gc, path, pcms[i].streams,
+                                     pcms[i].num_vsnd_streams, front);
+        if (rc) goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__set_xenstore_vsnd(libxl__gc *gc, uint32_t domid,
+                                    libxl_device_vsnd *vsnd,
+                                    flexarray_t *back, flexarray_t *front,
+                                    flexarray_t *ro_front)
+{
+    int rc;
+
+    if (vsnd->long_name) {
+        rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_LONG_NAME,
+                                   vsnd->long_name);
+        if (rc) goto out;
+    }
+
+    if (vsnd->short_name) {
+        rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_SHORT_NAME,
+                                   vsnd->short_name);
+        if (rc) goto out;
+    }
+
+    rc = libxl__set_params_vsnd(gc, "", &vsnd->params, front);
+    if (rc) goto out;
+
+    rc = libxl__set_pcms_vsnd(gc, vsnd->pcms, vsnd->num_vsnd_pcms, front);
+    if (rc) goto out;
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__device_stream_getinfo(libxl__gc *gc, const char *path,
+                                        libxl_vsnd_pcm* pcm,
+                                        libxl_pcminfo *info)
+{
+    const char *tmp;
+    int i;
+    int rc;
+
+    info->num_vsnd_streams = pcm->num_vsnd_streams;
+    info->streams = libxl__malloc(NOGC, sizeof(*info->streams) * info->num_vsnd_streams);
+
+    for (i = 0; i < info->num_vsnd_streams; i++)
+    {
+        libxl_streaminfo_init(&info->streams[i]);
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/%d/"XENSND_FIELD_RING_REF,
+                                    path, i), &tmp);
+        if (rc) goto out;
+
+        info->streams[i].req_rref = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/%d/"XENSND_FIELD_EVT_CHNL,
+                                    path, i), &tmp);
+        if (rc) goto out;
+
+        info->streams[i].req_evtch = tmp ? strtoul(tmp, NULL, 10) : -1;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+static int libxl__device_pcm_getinfo(libxl__gc *gc, const char *path,
+                                     const libxl_device_vsnd *vsnd,
+                                     libxl_vsndinfo *info)
+{
+    int i;
+    int rc;
+
+    info->num_vsnd_pcms = vsnd->num_vsnd_pcms;
+    info->pcms = libxl__malloc(NOGC, sizeof(*info->pcms) * info->num_vsnd_pcms);
+
+    for (i = 0; i < info->num_vsnd_pcms; i++)
+    {
+        libxl_pcminfo_init(&info->pcms[i]);
+
+        rc = libxl__device_stream_getinfo(gc, GCSPRINTF("%s/%d", path, i),
+                                          &vsnd->pcms[i], &info->pcms[i]);
+        if (rc) goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
+                              const libxl_device_vsnd *vsnd,
+                              libxl_vsndinfo *info)
+{
+    GC_INIT(ctx);
+    char *libxl_path, *dompath, *devpath;
+    const char *val;
+    int rc;
+
+    libxl_vsndinfo_init(info);
+    dompath = libxl__xs_get_dompath(gc, domid);
+    info->devid = vsnd->devid;
+
+    devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
+                                                 LIBXL__DEVICE_KIND_VSND);
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
+                                                 LIBXL__DEVICE_KIND_VSND);
+
+    info->backend = xs_read(ctx->xsh, XBT_NULL,
+                            GCSPRINTF("%s/backend", libxl_path), NULL);
+
+    rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
+    if (rc) goto out;
+
+    val = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/state", devpath), NULL);
+
+    info->state = val ? strtoul(val, NULL, 10) : -1;
+
+    info->frontend = xs_read(ctx->xsh, XBT_NULL,
+                             GCSPRINTF("%s/frontend", libxl_path), NULL);
+
+    info->frontend_id = domid;
+
+    rc = libxl__device_pcm_getinfo(gc, devpath, vsnd, info);
+    if (rc) goto out;
+
+    rc = 0;
+
+out:
+     GC_FREE;
+     return rc;
+}
+
+int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
+                               int devid, libxl_device_vsnd *vsnd)
+{
+    GC_INIT(ctx);
+
+    libxl_device_vsnd *vsnds = NULL;
+    int n, i;
+    int rc;
+
+    libxl_device_vsnd_init(vsnd);
+
+    vsnds = libxl__device_list(gc, &libxl__vsnd_devtype, domid, &n);
+
+    if (!vsnds) { rc = ERROR_NOTFOUND; goto out; }
+
+    for (i = 0; i < n; ++i) {
+        if (devid == vsnds[i].devid) {
+            libxl_device_vsnd_copy(ctx, vsnd, &vsnds[i]);
+            rc = 0;
+            goto out;
+        }
+    }
+
+    rc = ERROR_NOTFOUND;
+
+out:
+    if (vsnds)
+        libxl__device_list_free(&libxl__vsnd_devtype, vsnds, n);
+
+    GC_FREE;
+    return rc;
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(vsnd)
+static LIBXL_DEFINE_DEVICES_ADD(vsnd)
+
+LIBXL_DEFINE_DEVICE_ADD(vsnd)
+LIBXL_DEFINE_DEVICE_REMOVE(vsnd)
+LIBXL_DEFINE_DEVICE_LIST(vsnd)
+
+DEFINE_DEVICE_TYPE_STRUCT(vsnd, VSND,
+    .update_config = (device_update_config_fn_t) libxl__update_config_vsnd,
+    .from_xenstore = (device_from_xenstore_fn_t) libxl__vsnd_from_xenstore,
+    .set_xenstore_config = (device_set_xenstore_config_fn_t)
+                           libxl__set_xenstore_vsnd
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_vtpm.c b/tools/libs/light/libxl_vtpm.c
new file mode 100644 (file)
index 0000000..dd00b26
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016      SUSE Linux GmbH
+ * Author Juergen Gross <jgross@suse.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include "libxl_internal.h"
+
+static int libxl__device_vtpm_setdefault(libxl__gc *gc, uint32_t domid,
+                                         libxl_device_vtpm *vtpm, bool hotplug)
+{
+    int rc;
+    if (libxl_uuid_is_nil(&vtpm->uuid)) {
+        libxl_uuid_generate(&vtpm->uuid);
+    }
+    rc = libxl__resolve_domid(gc, vtpm->backend_domname, &vtpm->backend_domid);
+    return rc;
+}
+
+static void libxl__update_config_vtpm(libxl__gc *gc, libxl_device_vtpm *dst,
+                                      libxl_device_vtpm *src)
+{
+    dst->devid = src->devid;
+    libxl_uuid_copy(CTX, &dst->uuid, &src->uuid);
+}
+
+static int libxl__set_xenstore_vtpm(libxl__gc *gc, uint32_t domid,
+                                    libxl_device_vtpm *vtpm,
+                                    flexarray_t *back, flexarray_t *front,
+                                    flexarray_t *ro_front)
+{
+    flexarray_append_pair(back, "handle", GCSPRINTF("%d", vtpm->devid));
+    flexarray_append_pair(back, "uuid",
+                          GCSPRINTF(LIBXL_UUID_FMT,
+                                    LIBXL_UUID_BYTES(vtpm->uuid)));
+    flexarray_append_pair(back, "resume", "False");
+
+    flexarray_append_pair(front, "handle", GCSPRINTF("%d", vtpm->devid));
+
+    return 0;
+}
+
+static void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid,
+                                   libxl_device_vtpm *vtpm,
+                                   libxl__ao_device *aodev)
+{
+    libxl__device_add_async(egc, domid, &libxl__vtpm_devtype, vtpm, aodev);
+}
+
+static int libxl__vtpm_from_xenstore(libxl__gc *gc, const char *libxl_path,
+                                     libxl_devid devid,
+                                     libxl_device_vtpm *vtpm)
+{
+    int rc;
+    const char *be_path;
+    char *uuid;
+
+    vtpm->devid = devid;
+
+    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
+                                  GCSPRINTF("%s/backend", libxl_path),
+                                  &be_path);
+    if (rc) return rc;
+
+    rc = libxl__backendpath_parse_domid(gc, be_path, &vtpm->backend_domid);
+    if (rc) return rc;
+
+    uuid = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/uuid", be_path));
+    if (uuid) {
+        if(libxl_uuid_from_string(&(vtpm->uuid), uuid)) {
+            LOGD(ERROR, vtpm->backend_domid, "%s/uuid is a malformed uuid?? "
+                               "(%s) Probably a bug!!\n", be_path, uuid);
+            return ERROR_FAIL;
+        }
+    }
+
+    return 0;
+}
+
+int libxl_device_vtpm_getinfo(libxl_ctx *ctx,
+                              uint32_t domid,
+                              const libxl_device_vtpm *vtpm,
+                              libxl_vtpminfo *vtpminfo)
+{
+    GC_INIT(ctx);
+    char *libxl_path, *vtpmpath;
+    char *val;
+    int rc = 0;
+
+    libxl_vtpminfo_init(vtpminfo);
+    vtpminfo->devid = vtpm->devid;
+
+    vtpmpath = libxl__domain_device_frontend_path(gc, domid, vtpminfo->devid,
+                                                  LIBXL__DEVICE_KIND_VTPM);
+    libxl_path = libxl__domain_device_libxl_path(gc, domid, vtpminfo->devid,
+                                                 LIBXL__DEVICE_KIND_VTPM);
+    vtpminfo->backend = xs_read(ctx->xsh, XBT_NULL,
+          GCSPRINTF("%s/backend", libxl_path), NULL);
+    if (!vtpminfo->backend) {
+        goto err;
+    }
+
+    rc = libxl__backendpath_parse_domid(gc, vtpminfo->backend,
+                                        &vtpminfo->backend_id);
+    if (rc) goto exit;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/state", vtpmpath));
+    vtpminfo->state = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/event-channel", vtpmpath));
+    vtpminfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/ring-ref", vtpmpath));
+    vtpminfo->rref = val ? strtoul(val, NULL, 10) : -1;
+
+    vtpminfo->frontend = xs_read(ctx->xsh, XBT_NULL,
+          GCSPRINTF("%s/frontend", libxl_path), NULL);
+    vtpminfo->frontend_id = domid;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/uuid", libxl_path));
+    if(val == NULL) {
+       LOGD(ERROR, domid, "%s/uuid does not exist!", vtpminfo->backend);
+       goto err;
+    }
+    if(libxl_uuid_from_string(&(vtpminfo->uuid), val)) {
+       LOGD(ERROR, domid,
+             "%s/uuid is a malformed uuid?? (%s) Probably a bug!\n",
+             vtpminfo->backend, val);
+       goto err;
+    }
+
+    goto exit;
+err:
+    rc = ERROR_FAIL;
+exit:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_devid_to_device_vtpm(libxl_ctx *ctx,
+                               uint32_t domid,
+                               int devid,
+                               libxl_device_vtpm *vtpm)
+{
+    GC_INIT(ctx);
+    libxl_device_vtpm *vtpms;
+    int nb, i;
+    int rc;
+
+    vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
+    if (!vtpms)
+        return ERROR_FAIL;
+
+    libxl_device_vtpm_init(vtpm);
+    rc = 1;
+    for (i = 0; i < nb; ++i) {
+        if(devid == vtpms[i].devid) {
+            vtpm->backend_domid = vtpms[i].backend_domid;
+            vtpm->devid = vtpms[i].devid;
+            libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
+            rc = 0;
+            break;
+        }
+    }
+
+    libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
+    GC_FREE;
+    return rc;
+}
+
+static int libxl_device_vtpm_compare(const libxl_device_vtpm *d1,
+                                     const libxl_device_vtpm *d2)
+{
+    return COMPARE_DEVID(d1, d2);
+}
+
+int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
+                            libxl_uuid* uuid, libxl_device_vtpm *vtpm)
+{
+    GC_INIT(ctx);
+    libxl_device_vtpm *vtpms;
+    int nb, i;
+    int rc;
+
+    vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
+    if (!vtpms)
+        return ERROR_FAIL;
+
+    memset(vtpm, 0, sizeof (libxl_device_vtpm));
+    rc = 1;
+    for (i = 0; i < nb; ++i) {
+        if(!libxl_uuid_compare(uuid, &vtpms[i].uuid)) {
+            vtpm->backend_domid = vtpms[i].backend_domid;
+            vtpm->devid = vtpms[i].devid;
+            libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
+            rc = 0;
+            break;
+        }
+    }
+
+    libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
+    GC_FREE;
+    return rc;
+}
+
+static void libxl_device_vtpm_update_config(libxl__gc *gc, void *d, void *s)
+{
+    libxl__update_config_vtpm(gc, d, s);
+}
+
+static LIBXL_DEFINE_UPDATE_DEVID(vtpm)
+static LIBXL_DEFINE_DEVICE_FROM_TYPE(vtpm)
+static LIBXL_DEFINE_DEVICES_ADD(vtpm)
+
+LIBXL_DEFINE_DEVICE_ADD(vtpm)
+LIBXL_DEFINE_DEVICE_REMOVE(vtpm)
+LIBXL_DEFINE_DEVICE_LIST(vtpm)
+
+DEFINE_DEVICE_TYPE_STRUCT(vtpm, VTPM,
+    .update_config = libxl_device_vtpm_update_config,
+    .from_xenstore = (device_from_xenstore_fn_t)libxl__vtpm_from_xenstore,
+    .set_xenstore_config = (device_set_xenstore_config_fn_t)
+                           libxl__set_xenstore_vtpm,
+);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
new file mode 100644 (file)
index 0000000..7d95506
--- /dev/null
@@ -0,0 +1,852 @@
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+
+#include <xenctrl_dom.h>
+
+int libxl__arch_domain_prepare_config(libxl__gc *gc,
+                                      libxl_domain_config *d_config,
+                                      struct xen_domctl_createdomain *config)
+{
+    switch(d_config->c_info.type) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
+        break;
+    case LIBXL_DOMAIN_TYPE_PVH:
+        config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
+        break;
+    case LIBXL_DOMAIN_TYPE_PV:
+        config->arch.emulation_flags = 0;
+        break;
+    default:
+        abort();
+    }
+
+    return 0;
+}
+
+int libxl__arch_domain_save_config(libxl__gc *gc,
+                                   libxl_domain_config *d_config,
+                                   libxl__domain_build_state *state,
+                                   const struct xen_domctl_createdomain *config)
+{
+    return 0;
+}
+
+static const char *e820_names(int type)
+{
+    switch (type) {
+        case E820_RAM: return "RAM";
+        case E820_RESERVED: return "Reserved";
+        case E820_ACPI: return "ACPI";
+        case E820_NVS: return "ACPI NVS";
+        case E820_UNUSABLE: return "Unusable";
+        default: break;
+    }
+    return "Unknown";
+}
+
+static int e820_sanitize(libxl__gc *gc, struct e820entry src[],
+                         uint32_t *nr_entries,
+                         unsigned long map_limitkb,
+                         unsigned long balloon_kb)
+{
+    uint64_t delta_kb = 0, start = 0, start_kb = 0, last = 0, ram_end;
+    uint32_t i, idx = 0, nr;
+    struct e820entry e820[E820MAX];
+
+    if (!src || !map_limitkb || !nr_entries)
+        return ERROR_INVAL;
+
+    nr = *nr_entries;
+    if (!nr)
+        return ERROR_INVAL;
+
+    if (nr > E820MAX)
+        return ERROR_NOMEM;
+
+    /* Weed out anything under 1MB */
+    for (i = 0; i < nr; i++) {
+        if (src[i].addr > 0x100000)
+            continue;
+
+        src[i].type = 0;
+        src[i].size = 0;
+        src[i].addr = -1ULL;
+    }
+
+    /* Find the lowest and highest entry in E820, skipping over
+     * undesired entries. */
+    start = -1ULL;
+    last = 0;
+    for (i = 0; i < nr; i++) {
+        if ((src[i].type == E820_RAM) ||
+            (src[i].type == E820_UNUSABLE) ||
+            (src[i].type == 0))
+            continue;
+
+        start = src[i].addr < start ? src[i].addr : start;
+        last = src[i].addr + src[i].size > last ?
+               src[i].addr + src[i].size > last : last;
+    }
+    if (start > 1024)
+        start_kb = start >> 10;
+
+    /* Add the memory RAM region for the guest */
+    e820[idx].addr = 0;
+    e820[idx].size = (uint64_t)map_limitkb << 10;
+    e820[idx].type = E820_RAM;
+
+    /* .. and trim if neccessary */
+    if (start_kb && map_limitkb > start_kb) {
+        delta_kb = map_limitkb - start_kb;
+        if (delta_kb)
+            e820[idx].size -= (uint64_t)(delta_kb << 10);
+    }
+    /* Note: We don't touch balloon_kb here. Will add it at the end. */
+    ram_end = e820[idx].addr + e820[idx].size;
+    idx ++;
+
+    LOG(DEBUG, "Memory: %"PRIu64"kB End of RAM: " \
+        "0x%"PRIx64" (PFN) Delta: %"PRIu64"kB, PCI start: %"PRIu64"kB " \
+        "(0x%"PRIx64" PFN), Balloon %"PRIu64"kB\n", (uint64_t)map_limitkb,
+        ram_end >> 12, delta_kb, start_kb ,start >> 12,
+        (uint64_t)balloon_kb);
+
+
+    /* This whole code below is to guard against if the Intel IGD is passed into
+     * the guest. If we don't pass in IGD, this whole code can be ignored.
+     *
+     * The reason for this code is that Intel boxes fill their E820 with
+     * E820_RAM amongst E820_RESERVED and we can't just ditch those E820_RAM.
+     * That is b/c any "gaps" in the E820 is considered PCI I/O space by
+     * Linux and it would be utilized by the Intel IGD as I/O space while
+     * in reality it was an RAM region.
+     *
+     * What this means is that we have to walk the E820 and for any region
+     * that is RAM and below 4GB and above ram_end, needs to change its type
+     * to E820_UNUSED. We also need to move some of the E820_RAM regions if
+     * the overlap with ram_end. */
+    for (i = 0; i < nr; i++) {
+        uint64_t end = src[i].addr + src[i].size;
+
+        /* We don't care about E820_UNUSABLE, but we need to
+         * change the type to zero b/c the loop after this
+         * sticks E820_UNUSABLE on the guest's E820 but ignores
+         * the ones with type zero. */
+        if ((src[i].type == E820_UNUSABLE) ||
+            /* Any region that is within the "RAM region" can
+             * be safely ditched. */
+            (end < ram_end)) {
+                src[i].type = 0;
+                continue;
+        }
+
+        /* Look only at RAM regions. */
+        if (src[i].type != E820_RAM)
+            continue;
+
+        /* We only care about RAM regions below 4GB. */
+        if (src[i].addr >= (1ULL<<32))
+            continue;
+
+        /* E820_RAM overlaps with our RAM region. Move it */
+        if (src[i].addr < ram_end) {
+            uint64_t delta;
+
+            src[i].type = E820_UNUSABLE;
+            delta = ram_end - src[i].addr;
+            /* The end < ram_end should weed this out */
+            if (src[i].size < delta)
+                src[i].type = 0;
+            else {
+                src[i].size -= delta;
+                src[i].addr = ram_end;
+            }
+            if (src[i].addr + src[i].size != end) {
+                /* We messed up somewhere */
+                src[i].type = 0;
+                LOGE(ERROR, "Computed E820 wrongly. Continuing on.");
+            }
+        }
+        /* Lastly, convert the RAM to UNSUABLE. Look in the Linux kernel
+           at git commit 2f14ddc3a7146ea4cd5a3d1ecd993f85f2e4f948
+            "xen/setup: Inhibit resource API from using System RAM E820
+           gaps as PCI mem gaps" for full explanation. */
+        if (end > ram_end)
+            src[i].type = E820_UNUSABLE;
+    }
+
+    /* Check if there is a region between ram_end and start. */
+    if (start > ram_end) {
+        int add_unusable = 1;
+        for (i = 0; i < nr && add_unusable; i++) {
+            if (src[i].type != E820_UNUSABLE)
+                continue;
+            if (ram_end != src[i].addr)
+                continue;
+            if (start != src[i].addr + src[i].size) {
+                /* there is one, adjust it */
+                src[i].size = start - src[i].addr;
+            }
+            add_unusable = 0;
+        }
+        /* .. and if not present, add it in. This is to guard against
+           the Linux guest assuming that the gap between the end of
+           RAM region and the start of the E820_[ACPI,NVS,RESERVED]
+           is PCI I/O space. Which it certainly is _not_. */
+        if (add_unusable) {
+            e820[idx].type = E820_UNUSABLE;
+            e820[idx].addr = ram_end;
+            e820[idx].size = start - ram_end;
+            idx++;
+        }
+    }
+    /* Almost done: copy them over, ignoring the undesireable ones */
+    for (i = 0; i < nr; i++) {
+        if ((src[i].type == E820_RAM) ||
+            (src[i].type == 0))
+            continue;
+
+        e820[idx].type = src[i].type;
+        e820[idx].addr = src[i].addr;
+        e820[idx].size = src[i].size;
+        idx++;
+    }
+    /* At this point we have the mapped RAM + E820 entries from src. */
+    if (balloon_kb || delta_kb) {
+        /* and if we truncated the RAM region, then add it to the end. */
+        e820[idx].type = E820_RAM;
+        e820[idx].addr = (uint64_t)(1ULL << 32) > last ?
+                         (uint64_t)(1ULL << 32) : last;
+        /* also add the balloon memory to the end. */
+        e820[idx].size = (uint64_t)(delta_kb << 10) +
+                         (uint64_t)(balloon_kb << 10);
+        idx++;
+
+    }
+    nr = idx;
+
+    for (i = 0; i < nr; i++) {
+      LOG(DEBUG, ":\t[%"PRIx64" -> %"PRIx64"] %s", e820[i].addr >> 12,
+          (e820[i].addr + e820[i].size) >> 12, e820_names(e820[i].type));
+    }
+
+    /* Done: copy the sanitized version. */
+    *nr_entries = nr;
+    memcpy(src, e820, nr * sizeof(struct e820entry));
+    return 0;
+}
+
+static int e820_host_sanitize(libxl__gc *gc,
+                              libxl_domain_build_info *b_info,
+                              struct e820entry map[],
+                              uint32_t *nr)
+{
+    int rc;
+
+    rc = xc_get_machine_memory_map(CTX->xch, map, *nr);
+    if (rc < 0)
+        return ERROR_FAIL;
+
+    *nr = rc;
+
+    rc = e820_sanitize(gc, map, nr, b_info->target_memkb,
+                       (b_info->max_memkb - b_info->target_memkb) +
+                       b_info->u.pv.slack_memkb);
+    return rc;
+}
+
+static int libxl__e820_alloc(libxl__gc *gc, uint32_t domid,
+        libxl_domain_config *d_config)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    int rc;
+    uint32_t nr;
+    struct e820entry map[E820MAX];
+    libxl_domain_build_info *b_info;
+
+    if (d_config == NULL || d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV)
+        return ERROR_INVAL;
+
+    b_info = &d_config->b_info;
+    if (!libxl_defbool_val(b_info->u.pv.e820_host))
+        return ERROR_INVAL;
+
+    nr = E820MAX;
+    rc = e820_host_sanitize(gc, b_info, map, &nr);
+    if (rc)
+        return ERROR_FAIL;
+
+    rc = xc_domain_set_memory_map(ctx->xch, domid, map, nr);
+
+    if (rc < 0)
+        return ERROR_FAIL;
+
+    return 0;
+}
+
+static unsigned long timer_mode(const libxl_domain_build_info *info)
+{
+    const libxl_timer_mode mode = info->timer_mode;
+    assert(mode >= LIBXL_TIMER_MODE_DELAY_FOR_MISSED_TICKS &&
+           mode <= LIBXL_TIMER_MODE_ONE_MISSED_TICK_PENDING);
+    return ((unsigned long)mode);
+}
+
+static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid,
+                                     const libxl_domain_build_info *info)
+{
+    libxl_bitmap enlightenments;
+    libxl_viridian_enlightenment v;
+    uint64_t mask = 0;
+
+    libxl_bitmap_init(&enlightenments);
+    libxl_bitmap_alloc(CTX, &enlightenments,
+                       LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH);
+
+    if (libxl_defbool_val(info->u.hvm.viridian)) {
+        /* Enable defaults */
+        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE);
+        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ);
+        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT);
+        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST);
+        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL);
+    }
+
+    libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) {
+        if (libxl_bitmap_test(&info->u.hvm.viridian_disable, v)) {
+            LOG(ERROR, "%s group both enabled and disabled",
+                libxl_viridian_enlightenment_to_string(v));
+            goto err;
+        }
+        if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
+            libxl_bitmap_set(&enlightenments, v);
+    }
+
+    libxl_for_each_set_bit(v, info->u.hvm.viridian_disable)
+        if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
+            libxl_bitmap_reset(&enlightenments, v);
+
+    /* The base set is a pre-requisite for all others */
+    if (!libxl_bitmap_is_empty(&enlightenments) &&
+        !libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
+        LOG(ERROR, "base group not enabled");
+        goto err;
+    }
+
+    libxl_for_each_set_bit(v, enlightenments)
+        LOG(DETAIL, "%s group enabled", libxl_viridian_enlightenment_to_string(v));
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
+        mask |= HVMPV_base_freq;
+
+        if (!libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ))
+            mask |= HVMPV_no_freq;
+    }
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT))
+        mask |= HVMPV_time_ref_count;
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_REFERENCE_TSC))
+        mask |= HVMPV_reference_tsc;
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_REMOTE_TLB_FLUSH))
+        mask |= HVMPV_hcall_remote_tlb_flush;
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST))
+        mask |= HVMPV_apic_assist;
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL))
+        mask |= HVMPV_crash_ctl;
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_SYNIC))
+        mask |= HVMPV_synic;
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_STIMER))
+        mask |= HVMPV_time_ref_count | HVMPV_synic | HVMPV_stimer;
+
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_IPI))
+        mask |= HVMPV_hcall_ipi;
+
+    if (mask != 0 &&
+        xc_hvm_param_set(CTX->xch,
+                         domid,
+                         HVM_PARAM_VIRIDIAN,
+                         mask) != 0) {
+        LOGE(ERROR, "Couldn't set viridian feature mask (0x%"PRIx64")", mask);
+        goto err;
+    }
+
+    libxl_bitmap_dispose(&enlightenments);
+    return 0;
+
+err:
+    libxl_bitmap_dispose(&enlightenments);
+    return ERROR_FAIL;
+}
+
+static int hvm_set_conf_params(libxl__gc *gc, uint32_t domid,
+                               const libxl_domain_build_info *info)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    xc_interface *xch = ctx->xch;
+    int ret = ERROR_FAIL;
+    unsigned int altp2m = info->altp2m;
+
+    switch(info->type) {
+    case LIBXL_DOMAIN_TYPE_HVM:
+        /* The config parameter "altp2m" replaces the parameter "altp2mhvm". For
+         * legacy reasons, both parameters are accepted on x86 HVM guests.
+         *
+         * If the legacy field info->u.hvm.altp2m is set, activate altp2m.
+         * Otherwise set altp2m based on the field info->altp2m. */
+        if (info->altp2m == LIBXL_ALTP2M_MODE_DISABLED &&
+            libxl_defbool_val(info->u.hvm.altp2m))
+            altp2m = libxl_defbool_val(info->u.hvm.altp2m);
+
+        if (xc_hvm_param_set(xch, domid, HVM_PARAM_HPET_ENABLED,
+                             libxl_defbool_val(info->u.hvm.hpet))) {
+            LOG(ERROR, "Couldn't set HVM_PARAM_HPET_ENABLED");
+            goto out;
+        }
+        if (xc_hvm_param_set(xch, domid, HVM_PARAM_VPT_ALIGN,
+                             libxl_defbool_val(info->u.hvm.vpt_align))) {
+            LOG(ERROR, "Couldn't set HVM_PARAM_VPT_ALIGN");
+            goto out;
+        }
+        if (info->u.hvm.mca_caps &&
+            xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_MCA_CAP,
+                             info->u.hvm.mca_caps)) {
+            LOG(ERROR, "Couldn't set HVM_PARAM_MCA_CAP");
+            goto out;
+        }
+
+        /* Fallthrough */
+    case LIBXL_DOMAIN_TYPE_PVH:
+        if (xc_hvm_param_set(xch, domid, HVM_PARAM_TIMER_MODE,
+                             timer_mode(info))) {
+            LOG(ERROR, "Couldn't set HVM_PARAM_TIMER_MODE");
+            goto out;
+        }
+        if (xc_hvm_param_set(xch, domid, HVM_PARAM_NESTEDHVM,
+                             libxl_defbool_val(info->nested_hvm))) {
+            LOG(ERROR, "Couldn't set HVM_PARAM_NESTEDHVM");
+            goto out;
+        }
+        if (xc_hvm_param_set(xch, domid, HVM_PARAM_ALTP2M, altp2m)) {
+            LOG(ERROR, "Couldn't set HVM_PARAM_ALTP2M");
+            goto out;
+        }
+        break;
+
+    default:
+        abort();
+    }
+
+    ret = 0;
+
+ out:
+    return ret;
+}
+
+int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
+        uint32_t domid)
+{
+    const libxl_domain_build_info *info = &d_config->b_info;
+    int ret = 0;
+    int tsc_mode;
+    uint32_t rtc_timeoffset;
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+
+    if (info->type != LIBXL_DOMAIN_TYPE_PV &&
+        (ret = hvm_set_conf_params(gc, domid, info)) != 0)
+        goto out;
+
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        (ret = hvm_set_viridian_features(gc, domid, info)) != 0)
+        goto out;
+
+    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PV)
+        xc_domain_set_memmap_limit(ctx->xch, domid,
+                                   (d_config->b_info.max_memkb +
+                                    d_config->b_info.u.pv.slack_memkb));
+
+    switch (d_config->b_info.tsc_mode) {
+    case LIBXL_TSC_MODE_DEFAULT:
+        tsc_mode = 0;
+        break;
+    case LIBXL_TSC_MODE_ALWAYS_EMULATE:
+        tsc_mode = 1;
+        break;
+    case LIBXL_TSC_MODE_NATIVE:
+        tsc_mode = 2;
+        break;
+    case LIBXL_TSC_MODE_NATIVE_PARAVIRT:
+        LOGD(ERROR, domid, "TSC Mode native_paravirt (a.k.a PVRDTSCP) has been removed");
+        ret = ERROR_FEATURE_REMOVED;
+        goto out;
+    default:
+        abort();
+    }
+
+    if (xc_domain_set_tsc_info(ctx->xch, domid, tsc_mode, 0, 0, 0)) {
+        LOGE(ERROR, "xc_domain_set_tsc_info() failed");
+        ret = ERROR_FAIL;
+        goto out;
+    }
+
+    rtc_timeoffset = d_config->b_info.rtc_timeoffset;
+    if (libxl_defbool_val(d_config->b_info.localtime)) {
+        time_t t;
+        struct tm *tm, result;
+
+        t = time(NULL);
+        tm = localtime_r(&t, &result);
+
+        if (!tm) {
+            LOGED(ERROR, domid, "Failed to call localtime_r");
+            ret = ERROR_FAIL;
+            goto out;
+        }
+
+        rtc_timeoffset += tm->tm_gmtoff;
+    }
+
+    if (rtc_timeoffset)
+        xc_domain_set_time_offset(ctx->xch, domid, rtc_timeoffset);
+
+    if (d_config->b_info.type != LIBXL_DOMAIN_TYPE_PV) {
+        unsigned long shadow = DIV_ROUNDUP(d_config->b_info.shadow_memkb,
+                                           1024);
+        xc_shadow_control(ctx->xch, domid, XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION,
+                          NULL, 0, &shadow, 0, NULL);
+    }
+
+    if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
+            libxl_defbool_val(d_config->b_info.u.pv.e820_host)) {
+        ret = libxl__e820_alloc(gc, domid, d_config);
+        if (ret) {
+            LOGED(ERROR, domid, "Failed while collecting E820 with: %d (errno:%d)\n",
+                 ret, errno);
+        }
+    }
+
+out:
+    return ret;
+}
+
+int libxl__arch_extra_memory(libxl__gc *gc,
+                             const libxl_domain_build_info *info,
+                             uint64_t *out)
+{
+    *out = LIBXL_MAXMEM_CONSTANT;
+
+    return 0;
+}
+
+int libxl__arch_domain_init_hw_description(libxl__gc *gc,
+                                           libxl_domain_build_info *info,
+                                           libxl__domain_build_state *state,
+                                           struct xc_dom_image *dom)
+{
+    return 0;
+}
+
+int libxl__arch_build_dom_finish(libxl__gc *gc,
+                                 libxl_domain_build_info *info,
+                                 struct xc_dom_image *dom,
+                                 libxl__domain_build_state *state)
+{
+    return 0;
+}
+
+/* Return 0 on success, ERROR_* on failure. */
+int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
+                                      uint32_t domid,
+                                      libxl_domain_build_info *b_info,
+                                      libxl__domain_build_state *state)
+{
+    int nid, nr_vmemrange, rc;
+    uint32_t nr_e820, e820_count;
+    struct e820entry map[E820MAX];
+    xen_vmemrange_t *vmemranges;
+    unsigned int array_size;
+
+    /* If e820_host is not set, call the generic function */
+    if (!(b_info->type == LIBXL_DOMAIN_TYPE_PV &&
+          libxl_defbool_val(b_info->u.pv.e820_host)))
+        return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, b_info,
+                                                       state);
+
+    assert(state->vmemranges == NULL);
+
+    nr_e820 = E820MAX;
+    rc = e820_host_sanitize(gc, b_info, map, &nr_e820);
+    if (rc) goto out;
+
+    e820_count = 0;
+    nr_vmemrange = 0;
+    vmemranges = NULL;
+    array_size = 0;
+    for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
+        libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
+        uint64_t remaining_bytes = (p->memkb << 10), bytes;
+
+        while (remaining_bytes > 0) {
+            if (e820_count >= nr_e820) {
+                rc = ERROR_NOMEM;
+                goto out;
+            }
+
+            /* Skip non RAM region */
+            if (map[e820_count].type != E820_RAM) {
+                e820_count++;
+                continue;
+            }
+
+            if (nr_vmemrange >= array_size) {
+                array_size += 32;
+                GCREALLOC_ARRAY(vmemranges, array_size);
+            }
+
+            bytes = map[e820_count].size >= remaining_bytes ?
+                remaining_bytes : map[e820_count].size;
+
+            vmemranges[nr_vmemrange].start = map[e820_count].addr;
+            vmemranges[nr_vmemrange].end = map[e820_count].addr + bytes;
+
+            if (map[e820_count].size >= remaining_bytes) {
+                map[e820_count].addr += bytes;
+                map[e820_count].size -= bytes;
+            } else {
+                e820_count++;
+            }
+
+            remaining_bytes -= bytes;
+
+            vmemranges[nr_vmemrange].flags = 0;
+            vmemranges[nr_vmemrange].nid = nid;
+            nr_vmemrange++;
+        }
+    }
+
+    state->vmemranges = vmemranges;
+    state->num_vmemranges = nr_vmemrange;
+
+    rc = 0;
+out:
+    return rc;
+}
+
+int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
+{
+    int ret;
+
+    ret = xc_physdev_map_pirq(CTX->xch, domid, irq, &irq);
+    if (ret)
+        return ret;
+
+    ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
+
+    return ret;
+}
+
+/*
+ * Here we're just trying to set these kinds of e820 mappings:
+ *
+ * #1. Low memory region
+ *
+ * Low RAM starts at least from 1M to make sure all standard regions
+ * of the PC memory map, like BIOS, VGA memory-mapped I/O and vgabios,
+ * have enough space.
+ * Note: Those stuffs below 1M are still constructed with multiple
+ * e820 entries by hvmloader. At this point we don't change anything.
+ *
+ * #2. RDM region if it exists
+ *
+ * #3. High memory region if it exists
+ *
+ * Note: these regions are not overlapping since we already check
+ * to adjust them. Please refer to libxl__domain_device_construct_rdm().
+ */
+#define GUEST_LOW_MEM_START_DEFAULT 0x100000
+static int domain_construct_memmap(libxl__gc *gc,
+                                   libxl_domain_config *d_config,
+                                   uint32_t domid,
+                                   struct xc_dom_image *dom)
+{
+    int rc = 0;
+    unsigned int nr = 0, i;
+    /* We always own at least one lowmem entry. */
+    unsigned int e820_entries = 1;
+    struct e820entry *e820 = NULL;
+    uint64_t highmem_size =
+                    dom->highmem_end ? dom->highmem_end - (1ull << 32) : 0;
+    uint32_t lowmem_start = dom->device_model ? GUEST_LOW_MEM_START_DEFAULT : 0;
+    unsigned page_size = XC_DOM_PAGE_SIZE(dom);
+
+    /* Add all rdm entries. */
+    for (i = 0; i < d_config->num_rdms; i++)
+        if (d_config->rdms[i].policy != LIBXL_RDM_RESERVE_POLICY_INVALID)
+            e820_entries++;
+
+    /* Add the HVM special pages to PVH memmap as RESERVED. */
+    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH)
+        e820_entries++;
+
+    /* If we should have a highmem range. */
+    if (highmem_size)
+        e820_entries++;
+
+    for (i = 0; i < MAX_ACPI_MODULES; i++)
+        if (dom->acpi_modules[i].length)
+            e820_entries++;
+
+    if (e820_entries >= E820MAX) {
+        LOGD(ERROR, domid, "Ooops! Too many entries in the memory map!");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    e820 = libxl__malloc(gc, sizeof(struct e820entry) * e820_entries);
+
+    /* Low memory */
+    e820[nr].addr = lowmem_start;
+    e820[nr].size = dom->lowmem_end - lowmem_start;
+    e820[nr].type = E820_RAM;
+    nr++;
+
+    /* RDM mapping */
+    for (i = 0; i < d_config->num_rdms; i++) {
+        if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
+            continue;
+
+        e820[nr].addr = d_config->rdms[i].start;
+        e820[nr].size = d_config->rdms[i].size;
+        e820[nr].type = E820_RESERVED;
+        nr++;
+    }
+
+    /* HVM special pages */
+    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH) {
+        e820[nr].addr = (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES)
+                        << XC_PAGE_SHIFT;
+        e820[nr].size = X86_HVM_NR_SPECIAL_PAGES << XC_PAGE_SHIFT;
+        e820[nr].type = E820_RESERVED;
+        nr++;
+    }
+
+    for (i = 0; i < MAX_ACPI_MODULES; i++) {
+        if (dom->acpi_modules[i].length) {
+            e820[nr].addr = dom->acpi_modules[i].guest_addr_out & ~(page_size - 1);
+            e820[nr].size = dom->acpi_modules[i].length +
+                (dom->acpi_modules[i].guest_addr_out & (page_size - 1));
+            e820[nr].type = E820_ACPI;
+            nr++;
+        }
+    }
+
+    /* High memory */
+    if (highmem_size) {
+        e820[nr].addr = ((uint64_t)1 << 32);
+        e820[nr].size = highmem_size;
+        e820[nr].type = E820_RAM;
+    }
+
+    if (xc_domain_set_memory_map(CTX->xch, domid, e820, e820_entries) != 0) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    dom->e820 = e820;
+    dom->e820_entries = e820_entries;
+
+out:
+    return rc;
+}
+
+int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
+                                               uint32_t domid,
+                                               libxl_domain_config *d_config,
+                                               struct xc_dom_image *dom)
+{
+    libxl_domain_build_info *const info = &d_config->b_info;
+    int rc;
+
+    if (info->type == LIBXL_DOMAIN_TYPE_PV)
+        return 0;
+
+    if (info->type == LIBXL_DOMAIN_TYPE_PVH) {
+        rc = libxl__dom_load_acpi(gc, info, dom);
+        if (rc != 0) {
+            LOGE(ERROR, "libxl_dom_load_acpi failed");
+            return rc;
+        }
+    }
+
+    rc = domain_construct_memmap(gc, d_config, domid, dom);
+    if (rc != 0)
+        LOGE(ERROR, "setting domain memory map failed");
+
+    return rc;
+}
+
+void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
+                                               libxl_domain_create_info *c_info)
+{
+}
+
+void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
+                                              libxl_domain_build_info *b_info)
+{
+    libxl_defbool_setdefault(&b_info->acpi, true);
+}
+
+int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
+                                            uint32_t domid,
+                                            libxl_domain_config *d_config,
+                                            const libxl_physinfo *physinfo)
+{
+    int rc;
+    libxl_domain_create_info *const c_info = &d_config->c_info;
+
+    if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED &&
+        c_info->type == LIBXL_DOMAIN_TYPE_PVH) {
+        LOGD(ERROR, domid,
+             "passthrough not yet supported for x86 PVH guests\n");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    const char *whynot_pt_share =
+        c_info->type == LIBXL_DOMAIN_TYPE_PV ? "not valid for PV domain" :
+        !physinfo->cap_iommu_hap_pt_share ? "not supported on this platform" :
+        !libxl_defbool_val(d_config->c_info.hap) ?"only valid for HAP guests":
+        NULL;
+
+    if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
+        c_info->passthrough = whynot_pt_share
+            ? LIBXL_PASSTHROUGH_SYNC_PT : LIBXL_PASSTHROUGH_SHARE_PT;
+    }
+
+    if (c_info->passthrough == LIBXL_PASSTHROUGH_SHARE_PT && whynot_pt_share) {
+        LOGD(ERROR, domid,
+             "passthrough=\"share_pt\" %s\n",
+             whynot_pt_share);
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    rc = 0;
+ out:
+    return rc;
+}
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_x86_acpi.c b/tools/libs/light/libxl_x86_acpi.c
new file mode 100644 (file)
index 0000000..3df86c7
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ *
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include "libxl_internal.h"
+#include "libxl_arch.h"
+#include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/e820.h>
+#include "libacpi/libacpi.h"
+
+#include <xenctrl_dom.h>
+
+ /* Number of pages holding ACPI tables */
+#define NUM_ACPI_PAGES 16
+
+struct libxl_acpi_ctxt {
+    struct acpi_ctxt c;
+
+    unsigned int page_size;
+    unsigned int page_shift;
+
+    /* Memory allocator */
+    unsigned long alloc_base_paddr;
+    unsigned long alloc_base_vaddr;
+    unsigned long alloc_currp;
+    unsigned long alloc_end;
+};
+
+extern const unsigned char dsdt_pvh[];
+extern const unsigned int dsdt_pvh_len;
+
+/* Assumes contiguous physical space */
+static unsigned long virt_to_phys(struct acpi_ctxt *ctxt, void *v)
+{
+    struct libxl_acpi_ctxt *libxl_ctxt =
+        CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
+
+    return (((unsigned long)v - libxl_ctxt->alloc_base_vaddr) +
+            libxl_ctxt->alloc_base_paddr);
+}
+
+static void *mem_alloc(struct acpi_ctxt *ctxt,
+                       uint32_t size, uint32_t align)
+{
+    struct libxl_acpi_ctxt *libxl_ctxt =
+        CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
+    unsigned long s, e;
+
+    /* Align to at least 16 bytes. */
+    if (align < 16)
+        align = 16;
+
+    s = (libxl_ctxt->alloc_currp + align) & ~((unsigned long)align - 1);
+    e = s + size - 1;
+
+    /* TODO: Reallocate memory */
+    if ((e < s) || (e >= libxl_ctxt->alloc_end))
+        return NULL;
+
+    while (libxl_ctxt->alloc_currp >> libxl_ctxt->page_shift != 
+           e >> libxl_ctxt->page_shift)
+        libxl_ctxt->alloc_currp += libxl_ctxt->page_size;
+
+    libxl_ctxt->alloc_currp = e;
+
+    return (void *)s;
+}
+
+static void acpi_mem_free(struct acpi_ctxt *ctxt,
+                          void *v, uint32_t size)
+{
+}
+
+static uint32_t acpi_lapic_id(unsigned cpu)
+{
+    return cpu * 2;
+}
+
+static int init_acpi_config(libxl__gc *gc, 
+                            struct xc_dom_image *dom,
+                            const libxl_domain_build_info *b_info,
+                            struct acpi_config *config)
+{
+    xc_interface *xch = dom->xch;
+    uint32_t domid = dom->guest_domid;
+    xc_dominfo_t info;
+    struct hvm_info_table *hvminfo;
+    int i, r, rc;
+
+    config->dsdt_anycpu = config->dsdt_15cpu = dsdt_pvh;
+    config->dsdt_anycpu_len = config->dsdt_15cpu_len = dsdt_pvh_len;
+
+    r = xc_domain_getinfo(xch, domid, 1, &info);
+    if (r < 0) {
+        LOG(ERROR, "getdomaininfo failed (rc=%d)", r);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    hvminfo = libxl__zalloc(gc, sizeof(*hvminfo));
+
+    hvminfo->apic_mode = libxl_defbool_val(b_info->apic);
+
+    if (dom->nr_vnodes) {
+        unsigned int *vcpu_to_vnode, *vdistance;
+        struct xen_vmemrange *vmemrange;
+        struct acpi_numa *numa = &config->numa;
+
+        r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
+                               &numa->nr_vmemranges,
+                               &hvminfo->nr_vcpus, NULL, NULL, NULL);
+        if (r) {
+            LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        vmemrange = libxl__zalloc(gc, dom->nr_vmemranges * sizeof(*vmemrange));
+        vdistance = libxl__zalloc(gc, dom->nr_vnodes * sizeof(*vdistance));
+        vcpu_to_vnode = libxl__zalloc(gc, hvminfo->nr_vcpus *
+                                      sizeof(*vcpu_to_vnode));
+        r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
+                               &numa->nr_vmemranges, &hvminfo->nr_vcpus,
+                               vmemrange, vdistance, vcpu_to_vnode);
+        if (r) {
+            LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        numa->vmemrange = vmemrange;
+        numa->vdistance = vdistance;
+        numa->vcpu_to_vnode = vcpu_to_vnode;
+    } else {
+        hvminfo->nr_vcpus = info.max_vcpu_id + 1;
+    }
+
+    for (i = 0; i < hvminfo->nr_vcpus; i++)
+        hvminfo->vcpu_online[i / 8] |= 1 << (i & 7);
+
+    config->hvminfo = hvminfo;
+
+    config->lapic_base_address = LAPIC_BASE_ADDRESS;
+    config->lapic_id = acpi_lapic_id;
+    config->acpi_revision = 5;
+
+    rc = 0;
+out:
+    return rc;
+}
+
+int libxl__dom_load_acpi(libxl__gc *gc,
+                         const libxl_domain_build_info *b_info,
+                         struct xc_dom_image *dom)
+{
+    struct acpi_config config = {0};
+    struct libxl_acpi_ctxt libxl_ctxt;
+    int rc = 0, acpi_pages_num;
+    void *acpi_pages;
+    unsigned long page_mask;
+
+    if (b_info->type != LIBXL_DOMAIN_TYPE_PVH)
+        goto out;
+
+    libxl_ctxt.page_size = XC_DOM_PAGE_SIZE(dom);
+    libxl_ctxt.page_shift =  XC_DOM_PAGE_SHIFT(dom);
+    page_mask = (1UL << libxl_ctxt.page_shift) - 1;
+
+    libxl_ctxt.c.mem_ops.alloc = mem_alloc;
+    libxl_ctxt.c.mem_ops.v2p = virt_to_phys;
+    libxl_ctxt.c.mem_ops.free = acpi_mem_free;
+
+    rc = init_acpi_config(gc, dom, b_info, &config);
+    if (rc) {
+        LOG(ERROR, "init_acpi_config failed (rc=%d)", rc);
+        goto out;
+    }
+
+    config.rsdp = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
+    config.infop = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
+    /* Pages to hold ACPI tables */
+    acpi_pages =  libxl__malloc(gc, (NUM_ACPI_PAGES + 1) *
+                                libxl_ctxt.page_size);
+
+    /*
+     * Set up allocator memory.
+     * Start next to acpi_info page to avoid fracturing e820.
+     */
+    libxl_ctxt.alloc_base_paddr = ACPI_INFO_PHYSICAL_ADDRESS +
+        libxl_ctxt.page_size;
+    libxl_ctxt.alloc_base_vaddr = libxl_ctxt.alloc_currp =
+        (unsigned long)acpi_pages;
+    libxl_ctxt.alloc_end = (unsigned long)acpi_pages +
+        (NUM_ACPI_PAGES * libxl_ctxt.page_size);
+
+    /* Build the tables. */
+    rc = acpi_build_tables(&libxl_ctxt.c, &config);
+    if (rc) {
+        LOG(ERROR, "acpi_build_tables failed with %d", rc);
+        goto out;
+    }
+
+    /* Calculate how many pages are needed for the tables. */
+    acpi_pages_num =
+        ((libxl_ctxt.alloc_currp - (unsigned long)acpi_pages)
+         >> libxl_ctxt.page_shift) +
+        ((libxl_ctxt.alloc_currp & page_mask) ? 1 : 0);
+
+    dom->acpi_modules[0].data = (void *)config.rsdp;
+    dom->acpi_modules[0].length = 64;
+    /*
+     * Some Linux versions cannot properly process hvm_start_info.rsdp_paddr
+     * and so we need to put RSDP in location that can be discovered by ACPI's
+     * standard search method, in R-O BIOS memory (we chose last 64 bytes)
+     */
+    if (strcmp(dom->parms.guest_os, "linux") ||
+        elf_xen_feature_get(XENFEAT_linux_rsdp_unrestricted,
+                            dom->parms.f_supported))
+        dom->acpi_modules[0].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
+            (1 + acpi_pages_num) * libxl_ctxt.page_size;
+    else
+        dom->acpi_modules[0].guest_addr_out = 0x100000 - 64;
+
+    dom->acpi_modules[1].data = (void *)config.infop;
+    dom->acpi_modules[1].length = 4096;
+    dom->acpi_modules[1].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS;
+
+    dom->acpi_modules[2].data = acpi_pages;
+    dom->acpi_modules[2].length = acpi_pages_num  << libxl_ctxt.page_shift;
+    dom->acpi_modules[2].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
+        libxl_ctxt.page_size;
+
+out:
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_x86_acpi.h b/tools/libs/light/libxl_x86_acpi.h
new file mode 100644 (file)
index 0000000..d404637
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ *
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef LIBXL_X86_ACPI_H
+#define LIBXL_X86_ACPI_H
+
+#include "libxl_internal.h"
+
+#define ASSERT(x) assert(x)
+
+static inline int test_bit(unsigned int b, const void *p)
+{
+    return !!(((const uint8_t *)p)[b>>3] & (1u<<(b&7)));
+}
+
+#endif /* LIBXL_X_86_ACPI_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/libxl_xshelp.c b/tools/libs/light/libxl_xshelp.c
new file mode 100644 (file)
index 0000000..751cd94
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array)
+{
+    char **kvs;
+    int i, length;
+
+    if (!array)
+        return NULL;
+
+    length = array->count;
+    if (!length)
+        return NULL;
+
+    kvs = libxl__calloc(gc, length + 2, sizeof(char *));
+    if (kvs) {
+        for (i = 0; i < length; i += 2) {
+            void *ptr;
+
+            flexarray_get(array, i, &ptr);
+            kvs[i] = (char *) ptr;
+            flexarray_get(array, i + 1, &ptr);
+            kvs[i + 1] = (char *) ptr;
+        }
+        kvs[i] = NULL;
+        kvs[i + 1] = NULL;
+    }
+    return kvs;
+}
+
+int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
+                           const char *dir, char *kvs[],
+                           struct xs_permissions *perms,
+                           unsigned int num_perms)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *path;
+    int i;
+
+    if (!kvs)
+        return 0;
+
+    for (i = 0; kvs[i] != NULL; i += 2) {
+        path = GCSPRINTF("%s/%s", dir, kvs[i]);
+        if (path && kvs[i + 1]) {
+            int length = strlen(kvs[i + 1]);
+            xs_write(ctx->xsh, t, path, kvs[i + 1], length);
+            if (perms)
+                xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
+        }
+    }
+    return 0;
+}
+
+int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
+                     const char *dir, char *kvs[])
+{
+    return libxl__xs_writev_perms(gc, t, dir, kvs, NULL, 0);
+}
+
+int libxl__xs_writev_atonce(libxl__gc *gc,
+                            const char *dir, char *kvs[])
+{
+    int rc;
+    xs_transaction_t t = XBT_NULL;
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        rc = libxl__xs_writev(gc, t, dir, kvs);
+        if (rc) goto out;
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc<0) goto out;
+    }
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+
+    return rc;
+
+}
+
+int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
+                      const char *path, const char *fmt, va_list ap)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *s;
+    bool ok;
+
+    s = libxl__vsprintf(gc, fmt, ap);
+
+    ok = xs_write(ctx->xsh, t, path, s, strlen(s));
+    if (!ok) {
+        LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, s);
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
+                     const char *path, const char *fmt, ...)
+{
+    va_list ap;
+    int rc;
+
+    va_start(ap, fmt);
+    rc = libxl__xs_vprintf(gc, t, path, fmt, ap);
+    va_end(ap);
+
+    return rc;
+}
+
+char * libxl__xs_read(libxl__gc *gc, xs_transaction_t t, const char *path)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *ptr;
+
+    ptr = xs_read(ctx->xsh, t, path, NULL);
+    libxl__ptr_add(gc, ptr);
+    return ptr;
+}
+
+char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *s = xs_get_domain_path(ctx->xsh, domid);
+    if (!s) {
+        LOGED(ERROR, domid, "Failed to get dompath");
+        return NULL;
+    }
+    libxl__ptr_add(gc, s);
+    return s;
+}
+
+char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
+                           const char *path, unsigned int *nb)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char **ret = NULL;
+    ret = xs_directory(ctx->xsh, t, path, nb);
+    libxl__ptr_add(gc, ret);
+    return ret;
+}
+
+int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
+                    const char *path, struct xs_permissions *perms,
+                    unsigned int num_perms)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    bool ok;
+
+    ok = xs_write(ctx->xsh, t, path, "", 0);
+    if (!ok) {
+        LOGE(ERROR, "xenstore write failed: `%s' = ''", path);
+        return ERROR_FAIL;
+    }
+
+    ok = xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
+    if (!ok) {
+        LOGE(ERROR, "xenstore set permissions failed on `%s'", path);
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid)
+{
+    char *s = GCSPRINTF("/libxl/%i", domid);
+    if (!s)
+        LOGD(ERROR, domid, "cannot allocate create paths");
+    return s;
+}
+
+int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
+                             const char *path, const char **result_out)
+{
+    char *result = libxl__xs_read(gc, t, path);
+    if (!result) {
+        LOGE(ERROR, "xenstore read failed: `%s'", path);
+        return ERROR_FAIL;
+    }
+    *result_out = result;
+    return 0;
+}
+
+int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
+                           const char *path, const char **result_out)
+{
+    char *result = libxl__xs_read(gc, t, path);
+    if (!result && errno != ENOENT) {
+        LOGE(ERROR, "xenstore read failed: `%s'", path);
+        return ERROR_FAIL;
+    }
+    *result_out = result;
+    return 0;
+}
+
+int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
+                            const char *path, const char *string)
+{
+    size_t length = strlen(string);
+    if (!xs_write(CTX->xsh, t, path, string, length)) {
+        LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, string);
+        return ERROR_FAIL;
+    }
+    return 0;
+}
+
+int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path)
+{
+    if (!xs_rm(CTX->xsh, t, path)) {
+        if (errno == ENOENT)
+            return 0;
+
+        LOGE(ERROR, "xenstore rm failed: `%s'", path);
+        return ERROR_FAIL;
+    }
+    return 0;
+}
+
+int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t)
+{
+    assert(!*t);
+    *t = xs_transaction_start(CTX->xsh);
+    if (!*t) {
+        LOGE(ERROR, "could not create xenstore transaction");
+        return ERROR_FAIL;
+    }
+    return 0;
+}
+
+int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t)
+{
+    assert(*t);
+
+    if (!xs_transaction_end(CTX->xsh, *t, 0)) {
+        *t = 0;
+        if (errno == EAGAIN)
+            return +1;
+
+        LOGE(ERROR, "could not commit xenstore transaction");
+        return ERROR_FAIL;
+    }
+
+    *t = 0;
+    return 0;
+}
+
+void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t)
+{
+    if (!*t)
+        return;
+
+    if (!xs_transaction_end(CTX->xsh, *t, 1))
+        LOGE(ERROR, "could not abort xenstore transaction");
+
+    *t = 0;
+}
+
+int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
+                           const char *user_path)
+{
+    unsigned int nb = 0;
+    char *path, *last, *val;
+    int rc;
+
+    /* A path and transaction must be provided by the caller */
+    assert(user_path && t);
+
+    path = libxl__strdup(gc, user_path);
+    if (!xs_rm(CTX->xsh, t, path)) {
+        if (errno != ENOENT)
+            LOGE(DEBUG, "unable to remove path %s", path);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    for (last = strrchr(path, '/'); last != NULL; last = strrchr(path, '/')) {
+        *last = '\0';
+
+        if (!strlen(path)) break;
+
+        val = libxl__xs_read(gc, t, path);
+        if (!val || strlen(val) != 0) break;
+
+        if (!libxl__xs_directory(gc, t, path, &nb) || nb != 0) break;
+
+        if (!xs_rm(CTX->xsh, t, path)) {
+            if (errno != ENOENT)
+                LOGE(DEBUG, "unable to remove path %s", path);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/osdeps.c b/tools/libs/light/osdeps.c
new file mode 100644 (file)
index 0000000..0e0b447
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ */
+
+#include "libxl_osdeps.h"
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#ifdef NEED_OWN_ASPRINTF
+
+int vasprintf(char **buffer, const char *fmt, va_list ap)
+{
+    int size = 0;
+    int nchars;
+
+    *buffer = 0;
+
+    nchars = vsnprintf(*buffer, 0, fmt, ap);
+
+    if (nchars >= size)
+    {
+        char *tmpbuff;
+        /* Reallocate buffer now that we know how much space is needed. */
+        size = nchars+1;
+        tmpbuff = (char*)realloc(*buffer, size);
+
+
+        if (tmpbuff == NULL) { /* we need to free it*/
+            free(*buffer);
+            return -1;
+        }
+
+        *buffer=tmpbuff;
+        /* Try again. */
+        nchars = vsnprintf(*buffer, size, fmt, ap);
+    }
+
+    if (nchars < 0) return nchars;
+    return size;
+}
+
+int asprintf(char **buffer, char *fmt, ...)
+{
+    int status;
+    va_list ap;
+
+    va_start (ap, fmt);
+    status = vasprintf (buffer, fmt, ap);
+    va_end (ap);
+    return status;
+}
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libs/light/test_common.c b/tools/libs/light/test_common.c
new file mode 100644 (file)
index 0000000..c6bbbab
--- /dev/null
@@ -0,0 +1,57 @@
+#include "test_common.h"
+
+libxl_ctx *ctx;
+
+void test_common_setup(int level)
+{
+    xentoollog_logger_stdiostream *logger_s
+        = xtl_createlogger_stdiostream(stderr, level,  0);
+    assert(logger_s);
+
+    xentoollog_logger *logger = (xentoollog_logger*)logger_s;
+
+    int rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, logger);
+    assert(!rc);
+}
+
+struct timeval now;
+
+void test_common_get_now(void)
+{
+    int r = gettimeofday(&now, 0);  assert(!r);
+}
+
+int poll_nfds, poll_nfds_allocd;
+struct pollfd *poll_fds;
+int poll_timeout;
+
+void test_common_beforepoll(void)
+{
+    for (;;) {
+        test_common_get_now();
+
+        poll_timeout = -1;
+        poll_nfds = poll_nfds_allocd;
+        int rc = libxl_osevent_beforepoll(ctx, &poll_nfds, poll_fds,
+                                          &poll_timeout, now);
+        if (!rc) return;
+        assert(rc == ERROR_BUFFERFULL);
+
+        assert(poll_nfds > poll_nfds_allocd);
+        poll_fds = realloc(poll_fds, poll_nfds * sizeof(poll_fds[0]));
+        assert(poll_fds);
+        poll_nfds_allocd = poll_nfds;
+    }
+}
+
+void test_common_dopoll(void) {
+    errno = 0;
+    int r = poll(poll_fds, poll_nfds, poll_timeout);
+    fprintf(stderr, "poll: r=%d errno=%s\n", r, strerror(errno));
+}
+
+void test_common_afterpoll(void)
+{
+    test_common_get_now();
+    libxl_osevent_afterpoll(ctx, poll_nfds, poll_fds, now);
+}
diff --git a/tools/libs/light/test_common.h b/tools/libs/light/test_common.h
new file mode 100644 (file)
index 0000000..10c7166
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef TEST_COMMON_H
+#define TEST_COMMON_H
+
+#include "libxl.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+void test_common_setup(int level);
+
+extern libxl_ctx *ctx;
+
+void test_common_get_now(void);
+
+extern struct timeval now;
+
+void test_common_beforepoll(void);
+void test_common_dopoll(void);
+void test_common_afterpoll(void);
+
+extern int poll_nfds, poll_nfds_allocd;
+extern struct pollfd *poll_fds;
+extern int poll_timeout;
+
+#endif /*TEST_COMMON_H*/
diff --git a/tools/libs/light/test_fdderegrace.c b/tools/libs/light/test_fdderegrace.c
new file mode 100644 (file)
index 0000000..f57965f
--- /dev/null
@@ -0,0 +1,56 @@
+#include "test_common.h"
+#include "libxl_test_fdevent.h"
+
+int main(int argc, char **argv) {
+    int rc, i;
+    libxl_asyncop_how how;
+    libxl_event *event;
+
+    test_common_setup(XTL_DEBUG);
+
+    how.callback = NULL;
+    how.u.for_event = 1;
+
+    int fd = open("/dev/null", O_RDONLY);
+    assert(fd > 0);
+
+    rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
+    assert(!rc);
+
+    test_common_beforepoll();
+
+    rc = libxl_ao_abort(ctx, &how);
+    assert(!rc);
+
+    rc = libxl_event_check(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0);
+    assert(!rc);
+    assert(event);
+    assert(event->for_user == how.u.for_event);
+    assert(event->type == LIBXL_EVENT_TYPE_OPERATION_COMPLETE);
+    assert(event->u.operation_complete.rc == ERROR_ABORTED);
+
+    close(fd);
+
+    test_common_dopoll();
+
+    for (i=0; i<poll_nfds; i++) {
+        if (poll_fds[i].fd == fd && (poll_fds[i].revents & POLLNVAL)) {
+            fprintf(stderr, "POLLNVAL on fd=%d in slot i=%d as expected\n",
+                    fd, i);
+            goto found;
+        }
+    }
+    abort();
+ found:;
+
+    int fd2 = open("/dev/null", O_RDONLY);
+    assert(fd2 == fd);
+
+    how.u.for_event++;
+    rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
+    assert(!rc);
+
+    test_common_afterpoll();
+
+    fprintf(stderr, "complete\n");
+}
diff --git a/tools/libs/light/test_timedereg.c b/tools/libs/light/test_timedereg.c
new file mode 100644 (file)
index 0000000..0081ce3
--- /dev/null
@@ -0,0 +1,11 @@
+#include "test_common.h"
+#include "libxl_test_timedereg.h"
+
+int main(int argc, char **argv) {
+    int rc;
+
+    test_common_setup(XTL_DEBUG);
+
+    rc = libxl_test_timedereg(ctx, 0);
+    assert(!rc);
+}
index a9dc2ce99455f3248336ade311f3dfbcf3e811fc..685f368aedc7bc64dbfaed5b43bbcbb28fa41c89 100644 (file)
@@ -26,3 +26,5 @@ LIBS_LIBS += vchan
 USELIBS_vchan := toollog store gnttab evtchn
 LIBS_LIBS += stat
 USELIBS_stat := ctrl store
+LIBS_LIBS += light
+USELIBS_light := toollog evtchn toolcore ctrl store hypfs guest
index 51c4c22f228363830b2dcffdd1034f9ed50da85c..8ab7c9d3f00afbb44553f740b2b6ab300134ec93 100644 (file)
@@ -5,9 +5,6 @@
 XEN_ROOT = $(CURDIR)/../..
 include $(XEN_ROOT)/tools/Rules.mk
 
-MAJOR = 4.15
-MINOR = 0
-
 XLUMAJOR = 4.15
 XLUMINOR = 0
 
@@ -15,107 +12,11 @@ CFLAGS += -Werror -Wno-format-zero-length -Wmissing-declarations \
        -Wno-declaration-after-statement -Wformat-nonliteral
 CFLAGS += -I. -fPIC
 
-ifeq ($(CONFIG_Linux),y)
-LIBUUID_LIBS += -luuid
-endif
-
-LIBXL_LIBS =
-LIBXL_LIBS = $(LDLIBS_libxentoollog) $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenhypfs) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(PTYFUNCS_LIBS) $(LIBUUID_LIBS)
-ifeq ($(CONFIG_LIBNL),y)
-LIBXL_LIBS += $(LIBNL3_LIBS)
-endif
-ifeq ($(CONFIG_Linux),y)
-LIBXL_LIBS += -lrt
-endif
-
-CFLAGS_LIBXL += $(CFLAGS_libxentoollog)
-CFLAGS_LIBXL += $(CFLAGS_libxentoolcore)
-CFLAGS_LIBXL += $(CFLAGS_libxenevtchn)
-CFLAGS_LIBXL += $(CFLAGS_libxenctrl)
-CFLAGS_LIBXL += $(CFLAGS_libxenguest)
-CFLAGS_LIBXL += $(CFLAGS_libxenhypfs)
-CFLAGS_LIBXL += $(CFLAGS_libxenstore)
-ifeq ($(CONFIG_LIBNL),y)
-CFLAGS_LIBXL += $(LIBNL3_CFLAGS)
-endif
-CFLAGS_LIBXL += -Wshadow
-ifeq ($(debug),y)
-CFLAGS_LIBXL += -DCONFIG_DEBUG
-endif
-
-LIBXL_LIBS-$(CONFIG_ARM) += -lfdt
-
 CFLAGS += $(PTHREAD_CFLAGS)
 LDFLAGS += $(PTHREAD_LDFLAGS)
-LIBXL_LIBS += $(PTHREAD_LIBS)
-LIBXL_LIBS += $(LIBXL_LIBS-y)
 
 LIBXLU_LIBS = $(LDLIBS_libxenlight)
 
-LIBXL_OBJS-y = osdeps.o libxl_paths.o libxl_bootloader.o flexarray.o
-
-ifeq ($(CONFIG_LIBNL),y)
-LIBXL_OBJS-y += libxl_netbuffer.o
-else
-LIBXL_OBJS-y += libxl_nonetbuffer.o
-endif
-
-ifeq ($(CONFIG_X86),y)
-LIBXL_OBJS-y += libxl_convert_callout.o
-else
-LIBXL_OBJS-y += libxl_no_convert_callout.o
-endif
-
-LIBXL_OBJS-y += libxl_remus.o libxl_checkpoint_device.o libxl_remus_disk_drbd.o
-
-ifeq ($(CONFIG_LIBNL),y)
-LIBXL_OBJS-y += libxl_colo_restore.o libxl_colo_save.o
-LIBXL_OBJS-y += libxl_colo_qdisk.o
-LIBXL_OBJS-y += libxl_colo_proxy.o
-LIBXL_OBJS-y += libxl_colo_nic.o
-else
-LIBXL_OBJS-y += libxl_no_colo.o
-endif
-
-ACPI_PATH  = $(XEN_ROOT)/tools/libacpi
-DSDT_FILES-$(CONFIG_X86) = dsdt_pvh.c
-ACPI_OBJS  = $(patsubst %.c,%.o,$(DSDT_FILES-y)) build.o static_tables.o
-$(DSDT_FILES-y): acpi
-$(ACPI_OBJS): CFLAGS += -I. -DLIBACPI_STDUTILS=\"$(CURDIR)/libxl_x86_acpi.h\"
-vpath build.c $(ACPI_PATH)/
-vpath static_tables.c $(ACPI_PATH)/
-LIBXL_OBJS-$(CONFIG_X86) += $(ACPI_OBJS)
-
-.PHONY: acpi
-acpi:
-       $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
-
-LIBXL_OBJS-$(CONFIG_X86) += libxl_cpuid.o libxl_x86.o libxl_psr.o libxl_x86_acpi.o
-LIBXL_OBJS-$(CONFIG_ARM) += libxl_nocpuid.o libxl_arm.o libxl_libfdt_compat.o
-ifeq ($(CONFIG_ARM_64),y)
-DSDT_FILES-y = dsdt_anycpu_arm.c
-LIBXL_OBJS-y += libxl_arm_acpi.o $(patsubst %.c,%.o,$(DSDT_FILES-y))
-dsdt_anycpu_arm.c:
-       $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)"
-else
-LIBXL_OBJS-$(CONFIG_ARM) += libxl_arm_no_acpi.o
-endif
-
-ifeq ($(CONFIG_NetBSD),y)
-LIBXL_OBJS-y += libxl_netbsd.o
-else
-ifeq ($(CONFIG_Linux),y)
-LIBXL_OBJS-y += libxl_linux.o
-else
-ifeq ($(CONFIG_FreeBSD),y)
-LIBXL_OBJS-y += libxl_freebsd.o
-else
-$(error Your Operating System is not supported by libxenlight, \
-please check libxl_linux.c and libxl_netbsd.c to see how to get it ported)
-endif
-endif
-endif
-
 ifeq ($(FLEX),)
 %.c %.h:: %.l
        $(warning Flex is needed to rebuild some libxl parsers and \
@@ -128,80 +29,16 @@ ifeq ($(BISON),)
                  scanners, please install it an rerun configure)
 endif
 
-LIBXL_LIBS += -lyajl
-
-LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \
-                       libxl_dom.o libxl_exec.o libxl_xshelp.o libxl_device.o \
-                       libxl_internal.o libxl_utils.o libxl_uuid.o \
-                       libxl_json.o libxl_aoutils.o libxl_numa.o libxl_vnuma.o \
-                       libxl_stream_read.o libxl_stream_write.o \
-                       libxl_save_callout.o _libxl_save_msgs_callout.o \
-                       libxl_qmp.o libxl_event.o libxl_fork.o \
-                       libxl_dom_suspend.o libxl_dom_save.o libxl_usb.o \
-                       libxl_vtpm.o libxl_nic.o libxl_disk.o libxl_console.o \
-                       libxl_cpupool.o libxl_mem.o libxl_sched.o libxl_tmem.o \
-                       libxl_9pfs.o libxl_domain.o libxl_vdispl.o \
-                       libxl_pvcalls.o libxl_vsnd.o libxl_vkb.o $(LIBXL_OBJS-y)
-LIBXL_OBJS += libxl_genid.o
-LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o
-
-LIBXL_TESTS += timedereg
-LIBXL_TESTS_PROGS = $(LIBXL_TESTS) fdderegrace
-LIBXL_TESTS_INSIDE = $(LIBXL_TESTS) fdevent
-
-# Each entry FOO in LIBXL_TESTS has two main .c files:
-#   libxl_test_FOO.c  "inside libxl" code to support the test case
-#   test_FOO.c        "outside libxl" code to exercise the test case
-# Conventionally there will also be:
-#   libxl_test_FOO.h  interface between the "inside" and "outside" parts
-# The "inside libxl" file is compiled exactly like a piece of libxl, and the
-# "outside libxl" file is compiled exactly like a piece of application
-# code.  They must share information via explicit libxl entrypoints.
-# Unlike proper parts of libxl, it is permissible for libxl_test_FOO.c
-# to use private global variables for its state.  Note that all the
-# "inside" parts are compiled into a single test library, so their
-# symbol names must be unique.
-#
-# To run these tests, either use LD_PRELOAD to get libxenlight_test.so
-# loaded, or rename it to libxenlight.so so it is the target of the
-# appropriate symlinks.
-
-LIBXL_TEST_OBJS += $(foreach t, $(LIBXL_TESTS_INSIDE),libxl_test_$t.o)
-TEST_PROG_OBJS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t.o) test_common.o
-TEST_PROGS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t)
-
-$(LIBXL_OBJS) $(LIBXL_TEST_OBJS): CFLAGS += $(CFLAGS_LIBXL) -include $(XEN_ROOT)/tools/config.h
-
-AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h _libxl_list.h _paths.h \
-       libxlu_disk_l.h _libxl_save_msgs_callout.h _libxl_save_msgs_helper.h
+AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h libxlu_disk_l.h
 AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c
-AUTOSRCS += _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c
 LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \
        libxlu_disk_l.o libxlu_disk.o libxlu_vif.o libxlu_pci.o
 $(LIBXLU_OBJS): CFLAGS += $(CFLAGS_libxenctrl) # For xentoollog.h
 
-$(TEST_PROG_OBJS) _libxl.api-for-check: CFLAGS += $(CFLAGS_libxentoollog) $(CFLAGS_libxentoolcore)
-
-CLIENTS = testidl libxl-save-helper
-
-libxl_dom.o: CFLAGS += -I$(XEN_ROOT)/tools  # include libacpi/x86.h
-libxl_x86_acpi.o: CFLAGS += -I$(XEN_ROOT)/tools
-
-SAVE_HELPER_OBJS = libxl_save_helper.o _libxl_save_msgs_helper.o
-$(SAVE_HELPER_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenevtchn) $(CFLAGS_libxenguest)
-
-PKG_CONFIG = xenlight.pc xlutil.pc
-PKG_CONFIG_VERSION := $(MAJOR).$(MINOR)
+PKG_CONFIG = xlutil.pc
 
 ifneq ($(CONFIG_LIBXC_MINIOS),y)
 PKG_CONFIG_INST := $(PKG_CONFIG)
-xenlight.pc: PKG_CONFIG_NAME = Xenlight
-xenlight.pc: PKG_CONFIG_DESC = The Xenlight library for Xen hypervisor
-xenlight.pc: PKG_CONFIG_VERSION = $(MAJOR).$(MINOR)
-xenlight.pc: PKG_CONFIG_VARS = xenfirmwaredir=$(XENFIRMWAREDIR) libexec_bin=$(LIBEXEC_BIN)
-xenlight.pc: PKG_CONFIG_USELIBS = $(SHLIB_libxenlight)
-xenlight.pc: PKG_CONFIG_LIB = xenlight
-xenlight.pc: PKG_CONFIG_REQPRIV = xentoollog,xenevtchn,xencontrol,xenguest,xenstore,xenhypfs
 xlutil.pc: PKG_CONFIG_NAME = Xlutil
 xlutil.pc: PKG_CONFIG_DESC = The xl utility library for Xen hypervisor
 xlutil.pc: PKG_CONFIG_VERSION = $(XLUMAJOR).$(XLUMINOR)
@@ -215,13 +52,6 @@ endif
 
 PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc))
 
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_NAME = Xenlight
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_DESC = The Xenlight library for Xen hypervisor
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_VERSION = $(MAJOR).$(MINOR)
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_VARS = xenfirmwaredir=$(XENFIRMWAREDIR) libexec_bin=$(LIBEXEC_BIN)
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_USELIBS = $(SHLIB_libxenlight)
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_LIB = xenlight
-$(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_REQPRIV = xentoollog,xenevtchn,xencontrol,xenguest,xenstore,xenhypfs
 $(PKG_CONFIG_DIR)/xlutil.pc: PKG_CONFIG_NAME = Xlutil
 $(PKG_CONFIG_DIR)/xlutil.pc: PKG_CONFIG_DESC = The xl utility library for Xen hypervisor
 $(PKG_CONFIG_DIR)/xlutil.pc: PKG_CONFIG_VERSION = $(XLUMAJOR).$(XLUMINOR)
@@ -233,19 +63,10 @@ $(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(CURDIR)
 $(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR)
 $(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude)
 
-testidl.o: CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenlight)
-testidl.c: libxl_types.idl gentest.py libxl.h $(AUTOINCS)
-       $(PYTHON) gentest.py libxl_types.idl testidl.c.new
-       mv testidl.c.new testidl.c
-
 .PHONY: all
-all: $(CLIENTS) $(TEST_PROGS) $(PKG_CONFIG) $(PKG_CONFIG_LOCAL) \
-               libxenlight.so libxenlight.a libxlutil.so libxlutil.a \
-       $(AUTOSRCS) $(AUTOINCS)
+all: libxlutil.so libxlutil.a $(AUTOSRCS) $(AUTOINCS) $(PKG_CONFIG) $(PKG_CONFIG_LOCAL)
 
-$(LIBXL_OBJS) $(LIBXLU_OBJS) $(SAVE_HELPER_OBJS) \
-               $(LIBXL_TEST_OBJS) $(TEST_PROG_OBJS): \
-       $(AUTOINCS) libxl.api-ok
+$(LIBXLU_OBJS): $(AUTOINCS)
 
 %.c %.h:: %.y
        @rm -f $*.[ch]
@@ -258,123 +79,42 @@ $(LIBXL_OBJS) $(LIBXLU_OBJS) $(SAVE_HELPER_OBJS) \
 genpath-target = $(call buildmakevars2header,_paths.h)
 $(eval $(genpath-target))
 
-libxl.api-ok: check-libxl-api-rules _libxl.api-for-check
-       $(PERL) $^
-       touch $@
-
-_%.api-for-check: %.h $(AUTOINCS)
-       $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$*.o) -c -E $< $(APPEND_CFLAGS) \
-               -DLIBXL_EXTERNAL_CALLERS_ONLY=LIBXL_EXTERNAL_CALLERS_ONLY \
-               >$@.new
-       mv -f $@.new $@
-
-_libxl_list.h: $(XEN_INCLUDE)/xen-external/bsd-sys-queue-h-seddery $(XEN_INCLUDE)/xen-external/bsd-sys-queue.h
-       $(PERL) $^ --prefix=libxl >$@.new
-       $(call move-if-changed,$@.new,$@)
-
-_libxl_save_msgs_helper.c _libxl_save_msgs_callout.c \
-_libxl_save_msgs_helper.h _libxl_save_msgs_callout.h: \
-               libxl_save_msgs_gen.pl
-       $(PERL) -w $< $@ >$@.new
-       $(call move-if-changed,$@.new,$@)
-
-libxl.h: _libxl_types.h _libxl_list.h
-libxl_json.h: _libxl_types_json.h
-libxl_internal.h: _libxl_types_internal.h _libxl_types_private.h _libxl_types_internal_private.h _paths.h
-libxl_internal_json.h: _libxl_types_internal_json.h
-xl.h: _paths.h
-
-$(LIBXL_OBJS) $(LIBXL_TEST_OBJS) $(LIBXLU_OBJS) \
-       $(TEST_PROG_OBJS) $(SAVE_HELPER_OBJS): libxl.h
-$(LIBXL_OBJS) $(LIBXL_TEST_OBJS): libxl_internal.h
-
-_libxl_type%.h _libxl_type%_json.h _libxl_type%_private.h _libxl_type%.c: libxl_type%.idl gentypes.py idl.py
-       $(eval stem = $(notdir $*))
-       $(PYTHON) gentypes.py libxl_type$(stem).idl __libxl_type$(stem).h __libxl_type$(stem)_private.h \
-               __libxl_type$(stem)_json.h  __libxl_type$(stem).c
-       $(call move-if-changed,__libxl_type$(stem).h,_libxl_type$(stem).h)
-       $(call move-if-changed,__libxl_type$(stem)_private.h,_libxl_type$(stem)_private.h)
-       $(call move-if-changed,__libxl_type$(stem)_json.h,_libxl_type$(stem)_json.h)
-       $(call move-if-changed,__libxl_type$(stem).c,_libxl_type$(stem).c)
-
-libxenlight.so: libxenlight.so.$(MAJOR)
-       $(SYMLINK_SHLIB) $< $@
-
-libxenlight.so.$(MAJOR): libxenlight.so.$(MAJOR).$(MINOR)
-       $(SYMLINK_SHLIB) $< $@
-
-libxenlight.so.$(MAJOR).$(MINOR): $(LIBXL_OBJS)
-       $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBXL_LIBS) $(APPEND_LDFLAGS)
-
-libxenlight_test.so: $(LIBXL_OBJS) $(LIBXL_TEST_OBJS)
-       $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBXL_LIBS) $(APPEND_LDFLAGS)
-
-libxenlight.a: $(LIBXL_OBJS)
-       $(AR) rcs libxenlight.a $^
-
 libxlutil.so: libxlutil.so.$(XLUMAJOR)
        $(SYMLINK_SHLIB) $< $@
 
 libxlutil.so.$(XLUMAJOR): libxlutil.so.$(XLUMAJOR).$(XLUMINOR)
        $(SYMLINK_SHLIB) $< $@
 
-libxlutil.so.$(XLUMAJOR).$(XLUMINOR): $(LIBXLU_OBJS) libxenlight.so
+libxlutil.so.$(XLUMAJOR).$(XLUMINOR): $(LIBXLU_OBJS)
        $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxlutil.so.$(XLUMAJOR) $(SHLIB_LDFLAGS) -o $@ $(LIBXLU_OBJS) $(LIBXLU_LIBS) $(APPEND_LDFLAGS)
 
 libxlutil.a: $(LIBXLU_OBJS)
        $(AR) rcs libxlutil.a $^
 
-test_%: test_%.o test_common.o libxenlight_test.so
-       $(CC) $(LDFLAGS) -o $@ $^ $(filter-out %libxenlight.so, $(LDLIBS_libxenlight)) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) -lyajl $(APPEND_LDFLAGS)
-
-libxl-save-helper: $(SAVE_HELPER_OBJS) libxenlight.so
-       $(CC) $(LDFLAGS) -o $@ $(SAVE_HELPER_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
-
-testidl: testidl.o libxenlight.so
-       $(CC) $(LDFLAGS) -o $@ testidl.o $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) $(LDLIBS_libxentoolcore) $(APPEND_LDFLAGS)
-
 .PHONY: install
 install: all
        $(INSTALL_DIR) $(DESTDIR)$(libdir)
        $(INSTALL_DIR) $(DESTDIR)$(includedir)
-       $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
-       $(INSTALL_PROG) libxl-save-helper $(DESTDIR)$(LIBEXEC_BIN)
-       $(INSTALL_SHLIB) libxenlight.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)
-       $(SYMLINK_SHLIB) libxenlight.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenlight.so.$(MAJOR)
-       $(SYMLINK_SHLIB) libxenlight.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenlight.so
-       $(INSTALL_DATA) libxenlight.a $(DESTDIR)$(libdir)
        $(INSTALL_SHLIB) libxlutil.so.$(XLUMAJOR).$(XLUMINOR) $(DESTDIR)$(libdir)
        $(SYMLINK_SHLIB) libxlutil.so.$(XLUMAJOR).$(XLUMINOR) $(DESTDIR)$(libdir)/libxlutil.so.$(XLUMAJOR)
        $(SYMLINK_SHLIB) libxlutil.so.$(XLUMAJOR) $(DESTDIR)$(libdir)/libxlutil.so
        $(INSTALL_DATA) libxlutil.a $(DESTDIR)$(libdir)
-       $(INSTALL_DATA) libxl.h libxl_event.h libxl_json.h _libxl_types.h _libxl_types_json.h _libxl_list.h libxl_utils.h libxl_uuid.h libxlutil.h $(DESTDIR)$(includedir)
-       $(INSTALL_DATA) xenlight.pc $(DESTDIR)$(PKG_INSTALLDIR)
+       $(INSTALL_DATA) libxlutil.h $(DESTDIR)$(includedir)
        $(INSTALL_DATA) xlutil.pc $(DESTDIR)$(PKG_INSTALLDIR)
 
 .PHONY: uninstall
 uninstall:
-       rm -f $(addprefix $(DESTDIR)$(PKG_INSTALLDIR)/,xlutil.pc xenlight.pc)
-       rm -f $(addprefix $(DESTDIR)$(includedir)/,libxl.h libxl_event.h libxl_json.h _libxl_types.h _libxl_types_json.h _libxl_list.h libxl_utils.h libxl_uuid.h libxlutil.h)
+       rm -f $(DESTDIR)$(PKG_INSTALLDIR)/xlutil.pc
+       rm -f $(DESTDIR)$(includedir)/libxlutil.h
        rm -f $(DESTDIR)$(libdir)/libxlutil.a
        rm -f $(DESTDIR)$(libdir)/libxlutil.so
        rm -f $(DESTDIR)$(libdir)/libxlutil.so.$(XLUMAJOR)
        rm -f $(DESTDIR)$(libdir)/libxlutil.so.$(XLUMAJOR).$(XLUMINOR)
-       rm -f $(DESTDIR)$(libdir)/libxenlight.a
-       rm -f $(DESTDIR)$(libdir)/libxenlight.so
-       rm -f $(DESTDIR)$(libdir)/libxenlight.so.$(MAJOR)
-       rm -f $(DESTDIR)$(libdir)/libxenlight.so.$(MAJOR).$(MINOR)
-       rm -f $(DESTDIR)$(LIBEXEC_BIN)/libxl-save-helper
 
 .PHONY: clean
 clean:
-       $(RM) -f _*.h *.o *.so* *.a $(CLIENTS) $(DEPS_RM)
-       $(RM) -f _*.c *.pyc _paths.*.tmp _*.api-for-check
-       $(RM) -f testidl.c.new testidl.c *.api-ok
-       $(RM) -f $(TEST_PROGS)
-       $(RM) -f xenlight.pc
+       $(RM) -f _*.h *.o *.so* *.a $(DEPS_RM)
        $(RM) -f xlutil.pc
-       $(RM) -rf __pycache__
-       $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) clean
 
 distclean: clean
 
diff --git a/tools/libxl/check-libxl-api-rules b/tools/libxl/check-libxl-api-rules
deleted file mode 100755 (executable)
index 18ff39c..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/perl -w
-use strict;
-our $needed=0;
-our $speclineoffset=0;
-our $specfile;
-while (<>) {
-    if (m/^\# (\d+) \"(.*)\"$/) {
-        $speclineoffset = $1 - $. -1;
-        $specfile = $2;
-    }
-    my $file = defined($specfile) ? $specfile : $ARGV;
-    my $line = $speclineoffset + $.;
-    if (m/libxl_asyncop_how[^;]/) {
-        $needed=1;
-    }
-    if (m/LIBXL_EXTERNAL_CALLERS_ONLY/) {
-        $needed=0;
-    }
-    next unless $needed;
-    if (m/\;/) {
-        die "$file:$line:missing LIBXL_EXTERNAL_CALLERS_ONLY";
-    }
-}
diff --git a/tools/libxl/flexarray.c b/tools/libxl/flexarray.c
deleted file mode 100644 (file)
index fe40e76..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_internal.h"
-#include <stdarg.h>
-
-/*
- * It is safe to store gc in the struct because:
- * - If it an actual gc, then the flexarray should not be used after the gc
- *   have been freed.
- * - If it is a NOGC, then this point to a structure embedded in libxl_ctx,
- *   therefore will survive across several libxl calls.
- */
-
-flexarray_t *flexarray_make(libxl__gc *gc, int size, int autogrow)
-{
-    flexarray_t *array;
-
-    GCNEW(array);
-    array->size = size;
-    array->autogrow = autogrow;
-    array->count = 0;
-    array->gc = gc;
-    GCNEW_ARRAY(array->data, size);
-
-    return array;
-}
-
-void flexarray_free(flexarray_t *array)
-{
-    assert(!libxl__gc_is_real(array->gc));
-    free(array->data);
-    free(array);
-}
-
-void flexarray_grow(flexarray_t *array, int extents)
-{
-    int newsize;
-    libxl__gc *gc = array->gc;
-
-    newsize = array->size + extents;
-    GCREALLOC_ARRAY(array->data, newsize);
-    array->size += extents;
-}
-
-int flexarray_set(flexarray_t *array, unsigned int idx, void *ptr)
-{
-    if (idx >= array->size) {
-        int newsize;
-        if (!array->autogrow)
-            return 1;
-        newsize = (array->size * 2 < idx) ? idx + 1 : array->size * 2;
-        flexarray_grow(array, newsize - array->size);
-    }
-    if ( idx + 1 > array->count )
-        array->count = idx + 1;
-    array->data[idx] = ptr;
-    return 0;
-}
-
-int flexarray_append(flexarray_t *array, void *ptr)
-{
-    return flexarray_set(array, array->count, ptr);
-}
-
-int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2)
-{
-    int rc = flexarray_append(array, ptr1);
-    if (!rc)
-        rc = flexarray_append(array, ptr2);
-    return rc;
-}
-
-int flexarray_vappend(flexarray_t *array, ...)
-{
-    va_list va;
-    void *ptr;
-    int ret;
-
-    va_start(va, array);
-    for(ret = 0; (ptr = va_arg(va, void *)); ret++) {
-        if ( flexarray_append(array, ptr) )
-            break;
-    }
-    va_end(va);
-    return ret;
-}
-
-int flexarray_get(flexarray_t *array, int idx, void **ptr)
-{
-    if (idx >= array->size)
-        return 1;
-    *ptr = array->data[idx];
-    return 0;
-}
-
-void **flexarray_contents(flexarray_t *array)
-{
-    void **data;
-    data = array->data;
-    if (!libxl__gc_is_real(array->gc))
-        free(array);
-    return data;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/flexarray.h b/tools/libxl/flexarray.h
deleted file mode 100644 (file)
index a1e8647..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef FLEXARRAY_H
-#define FLEXARRAY_H
-
-struct libxl__gc;
-
-typedef struct flexarray {
-    int size;
-    int autogrow;
-    unsigned int count;
-    void **data; /* array of pointer */
-    struct libxl__gc *gc;
-} flexarray_t;
-
-/*
- * NOGC can be used with flexarrays, but flexarray_free will need to be called
- * to free the struct. The content of the flexarray will not be freed through
- * flexarray_free.
- */
-_hidden flexarray_t *flexarray_make(struct libxl__gc *gc_opt,
-                                    int size, int autogrow);
-_hidden void flexarray_free(flexarray_t *array);
-_hidden void flexarray_grow(flexarray_t *array, int extents);
-_hidden int flexarray_set(flexarray_t *array, unsigned int index, void *ptr);
-_hidden int flexarray_append(flexarray_t *array, void *ptr);
-_hidden int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2);
-_hidden int flexarray_vappend(flexarray_t *array, ...);
-_hidden int flexarray_get(flexarray_t *array, int index, void **ptr);
-
-_hidden void **flexarray_contents(flexarray_t *array);
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/gentest.py b/tools/libxl/gentest.py
deleted file mode 100644 (file)
index 1cc7eeb..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-#!/usr/bin/python
-
-from __future__ import print_function
-
-import os
-import sys
-import re
-import random
-
-import idl
-
-def randomize_char(c):
-    if random.random() < 0.5:
-        return str.lower(c)
-    else:
-        return str.upper(c)
-
-def randomize_case(s):
-    r = [randomize_char(c) for c in s]
-    return "".join(r)
-
-def randomize_enum(e):
-    return random.choice([v.name for v in e.values])
-
-handcoded = ["libxl_bitmap", "libxl_key_value_list",
-             "libxl_cpuid_policy_list", "libxl_string_list"]
-
-def gen_rand_init(ty, v, indent = "    ", parent = None):
-    s = ""
-    if isinstance(ty, idl.Enumeration):
-        s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), randomize_enum(ty))
-    elif isinstance(ty, idl.Array):
-        if parent is None:
-            raise Exception("Array type must have a parent")
-        s += "%s = test_rand(8);\n" % (parent + ty.lenvar.name)
-        s += "%s = calloc(%s, sizeof(*%s));\n" % \
-            (v, parent + ty.lenvar.name, v)
-        s += "assert(%s);\n" % (v, )
-        s += "{\n"
-        s += "    int i;\n"
-        s += "    for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
-        s += gen_rand_init(ty.elem_type, v+"[i]",
-                           indent + "        ", parent)
-        s += "}\n"
-    elif isinstance(ty, idl.KeyedUnion):
-        if parent is None:
-            raise Exception("KeyedUnion type must have a parent")
-        s += gen_rand_init(ty.keyvar.type, parent + ty.keyvar.name, indent, parent)
-        s += "switch (%s) {\n" % (parent + ty.keyvar.name)
-        for f in ty.fields:
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            s += "case %s:\n" % f.enumname
-            if f.type is not None:
-                s += gen_rand_init(f.type, fexpr, indent + "    ", nparent)
-            s += "    break;\n"
-        s += "}\n"
-    elif isinstance(ty, idl.Struct) \
-     and (parent is None or ty.json_gen_fn is None):
-        for f in [f for f in ty.fields if not f.const]:
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            s += gen_rand_init(f.type, fexpr, "", nparent)
-    elif hasattr(ty, "rand_init") and ty.rand_init is not None:
-        s += "%s(%s);\n" % (ty.rand_init,
-                            ty.pass_arg(v, isref=parent is None,
-                                        passby=idl.PASS_BY_REFERENCE))
-    elif ty.typename in ["libxl_uuid", "libxl_mac", "libxl_hwcap", "libxl_ms_vm_genid"]:
-        s += "rand_bytes((uint8_t *)%s, sizeof(*%s));\n" % (v,v)
-    elif ty.typename in ["libxl_domid", "libxl_devid"] or isinstance(ty, idl.Number):
-        s += "%s = test_rand(sizeof(%s) * 8);\n" % \
-             (ty.pass_arg(v, parent is None),
-              ty.pass_arg(v, parent is None))
-    elif ty.typename in ["bool"]:
-        s += "%s = test_rand(2);\n" % v
-    elif ty.typename in ["libxl_defbool"]:
-        s += "libxl_defbool_set(%s, test_rand(2));\n" % v
-    elif ty.typename in ["char *"]:
-        s += "%s = rand_str();\n" % v
-    elif ty.private:
-        pass
-    elif ty.typename in handcoded:
-        raise Exception("Gen for handcoded %s" % ty.typename)
-    else:
-        raise Exception("Cannot randomly init %s" % ty.typename)
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-if __name__ == '__main__':
-    if len(sys.argv) < 3:
-        print("Usage: gentest.py <idl> <implementation>", file=sys.stderr)
-        sys.exit(1)
-
-    random.seed(os.getenv('LIBXL_TESTIDL_SEED'))
-
-    (builtins,types) = idl.parse(sys.argv[1])
-
-    impl = sys.argv[2]
-    f = open(impl, "w")
-    f.write("""
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "libxl.h"
-#include "libxl_utils.h"
-
-static int test_rand(unsigned max)
-{
-    /* We are not using rand() for its cryptographic properies. */
-    return rand() % max;
-}
-
-static char *rand_str(void)
-{
-    int i, sz = test_rand(32);
-    char *s = malloc(sz+1);
-    assert(s);
-    for (i=0; i<sz; i++)
-        s[i] = 'a' + test_rand(26);
-    s[i] = '\\0';
-    return s;
-}
-
-static void rand_bytes(uint8_t *p, size_t sz)
-{
-    int i;
-    for (i=0; i<sz; i++)
-        p[i] = test_rand(256);
-}
-
-static void libxl_bitmap_rand_init(libxl_bitmap *bitmap)
-{
-    int i;
-    bitmap->size = test_rand(16);
-    bitmap->map = calloc(bitmap->size, sizeof(*bitmap->map));
-    assert(bitmap->map);
-    libxl_for_each_bit(i, *bitmap) {
-        if (test_rand(2))
-            libxl_bitmap_set(bitmap, i);
-        else
-            libxl_bitmap_reset(bitmap, i);
-    }
-}
-
-static void libxl_key_value_list_rand_init(libxl_key_value_list *pkvl)
-{
-    int i, nr_kvp = test_rand(16);
-    libxl_key_value_list kvl = calloc(nr_kvp+1, 2*sizeof(char *));
-    assert(kvl);
-
-    for (i = 0; i<2*nr_kvp; i += 2) {
-        kvl[i] = rand_str();
-        if (test_rand(8))
-            kvl[i+1] = rand_str();
-        else
-            kvl[i+1] = NULL;
-    }
-    kvl[i] = NULL;
-    kvl[i+1] = NULL;
-    *pkvl = kvl;
-}
-
-static void libxl_cpuid_policy_list_rand_init(libxl_cpuid_policy_list *pp)
-{
-    int i, nr_policies = test_rand(16);
-    struct {
-        const char *n;
-        int w;
-    } options[] = {
-      /* A random selection from libxl_cpuid_parse_config */
-        {"maxleaf",     32},
-        {"family",       8},
-        {"model",        8},
-        {"stepping",     4},
-        {"localapicid",  8},
-        {"proccount",    8},
-        {"clflush",      8},
-        {"brandid",      8},
-        {"f16c",         1},
-        {"avx",          1},
-        {"osxsave",      1},
-        {"xsave",        1},
-        {"aes",          1},
-        {"popcnt",       1},
-        {"movbe",        1},
-        {"x2apic",       1},
-        {"sse4.2",       1},
-        {"sse4.1",       1},
-        {"dca",          1},
-        {"pdcm",         1},
-        {"procpkg",      6},
-    };
-    const int nr_options = sizeof(options)/sizeof(options[0]);
-    char buf[64];
-    libxl_cpuid_policy_list p = NULL;
-
-    for (i = 0; i < nr_policies; i++) {
-        int opt = test_rand(nr_options);
-        int val = test_rand(1<<options[opt].w);
-        snprintf(buf, 64, \"%s=%#x\", options[opt].n, val);
-        libxl_cpuid_parse_config(&p, buf);
-    }
-    *pp = p;
-}
-
-static void libxl_string_list_rand_init(libxl_string_list *p)
-{
-    int i, nr = test_rand(16);
-    libxl_string_list l = calloc(nr+1, sizeof(char *));
-    assert(l);
-
-    for (i = 0; i<nr; i++) {
-        l[i] = rand_str();
-    }
-    l[i] = NULL;
-    *p = l;
-}
-""")
-    for ty in builtins + types:
-        if isinstance(ty, idl.Number): continue
-        if ty.typename not in handcoded:
-            f.write("static void %s_rand_init(%s);\n" % \
-                    (ty.typename,
-                     ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-            f.write("static void %s_rand_init(%s)\n" % \
-                    (ty.typename,
-                     ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-            f.write("{\n")
-            f.write(gen_rand_init(ty, "p"))
-            f.write("}\n")
-            f.write("\n")
-        ty.rand_init = "%s_rand_init" % ty.typename
-
-    f.write("""
-int main(int argc, char **argv)
-{
-""")
-
-    for ty in types:
-        f.write("    %s %s_val, %s_val_new;\n" % \
-                (ty.typename, ty.typename, ty.typename))
-    f.write("""
-    int rc;
-    char *s, *new_s, *json_string;
-    xentoollog_logger_stdiostream *logger;
-    libxl_ctx *ctx;
-
-    logger = xtl_createlogger_stdiostream(stderr, XTL_DETAIL, 0);
-    if (!logger) exit(1);
-
-    if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)logger)) {
-        fprintf(stderr, "cannot init xl context\\n");
-        exit(1);
-    }
-""")
-    f.write("    printf(\"Testing TYPE_to/from_json()\\n\");\n")
-    f.write("    printf(\"----------------------\\n\");\n")
-    f.write("    printf(\"\\n\");\n")
-    for ty in [t for t in types if t.json_gen_fn is not None]:
-        arg = ty.typename + "_val"
-        f.write("    %s_rand_init(%s);\n" % (ty.typename, \
-            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
-        if not isinstance(ty, idl.Enumeration):
-            iters = random.randrange(1,10)
-            while iters > 0:
-                f.write("    %s_init(%s_new);\n" % (ty.typename, \
-                    ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
-                iters -= 1
-        f.write("    s = %s_to_json(ctx, %s);\n" % \
-                (ty.typename, ty.pass_arg(arg, isref=False)))
-        f.write("    printf(\"%%s: %%s\\n\", \"%s\", s);\n" % ty.typename)
-        f.write("    if (s == NULL) abort();\n")
-        f.write("    rc = %s_from_json(ctx, &%s_val_new, s);\n" % \
-                (ty.typename, ty.typename))
-        f.write("    if (rc) abort();\n")
-        f.write("    new_s = %s_to_json(ctx, %s_new);\n" % \
-                (ty.typename, ty.pass_arg(arg, isref=False)))
-        f.write("    if (new_s == NULL) abort();\n")
-        f.write("    if (strcmp(s, new_s)) {\n")
-        f.write("        printf(\"Huh? Regenerated string different from original string.\\n\");\n")
-        f.write("        printf(\"Regenerated string: %s\\n\", new_s);\n")
-        f.write("        abort();\n")
-        f.write("    }\n")
-        f.write("    free(s);\n")
-        f.write("    free(new_s);\n")
-        if ty.dispose_fn is not None:
-            iters = random.randrange(1,10)
-            f.write("    %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
-            while iters > 0:
-                f.write("    %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
-                iters -= 1
-        f.write("\n")
-
-    f.write("    printf(\"Testing TYPE_copy()\\n\");\n")
-    f.write("    printf(\"----------------------\\n\");\n")
-    f.write("    printf(\"\\n\");\n")
-    for ty in [t for t in types if t.copy_fn is not None]:
-        f.write("    printf(\"Testing %s_copy, \");\n" % ty.typename)
-        arg = ty.typename + "_val"
-        f.write("    %s_init(%s);\n" % (ty.typename, \
-            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
-        f.write("    %s_rand_init(%s);\n" % (ty.typename, \
-            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
-        f.write("    %s_init(%s_new);\n" % (ty.typename, \
-            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
-        f.write("    %s_copy(ctx, %s_new, %s);\n" % (ty.typename, \
-            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE), \
-            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
-        f.write("    s = %s_to_json(ctx, %s);\n" % \
-                (ty.typename, ty.pass_arg(arg, isref=False)))
-        f.write("    if (s == NULL) abort();\n")
-        f.write("    new_s = %s_to_json(ctx, %s_new);\n" % \
-                (ty.typename, ty.pass_arg(arg, isref=False)))
-        f.write("    if (new_s == NULL) abort();\n")
-        f.write("    if (strcmp(s, new_s)) {\n")
-        f.write("        printf(\"Huh? Deep copy for %s failed. Regenerated string different from original string.\\n\");\n" \
-                % ty.typename)
-        f.write("        printf(\"Original string: %s\\n\", s);\n")
-        f.write("        printf(\"Regenerated string: %s\\n\", new_s);\n")
-        f.write("        abort();\n")
-        f.write("    }\n")
-        f.write("    free(s);\n")
-        f.write("    free(new_s);\n")
-        if ty.dispose_fn is not None:
-            f.write("    %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
-            f.write("    %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
-        f.write("    printf(\"done\\n\");\n")
-        f.write("\n")
-
-    f.write("    printf(\"\\n\");\n")
-    f.write("    printf(\"Testing Enumerations\\n\");\n")
-    f.write("    printf(\"--------------------\\n\");\n")
-    f.write("    printf(\"\\n\");\n")
-    for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
-        f.write("    printf(\"%s -- to string:\\n\");\n" % (ty.typename))
-        for v in ty.values:
-            f.write("    printf(\"\\t%s = %%d = \\\"%%s\\\"\\n\", " \
-                    "%s, %s_to_string(%s));\n" % \
-                    (v.valuename, v.name, ty.typename, v.name))
-        f.write("\n")
-
-        f.write("    printf(\"%s -- to JSON:\\n\");\n" % (ty.typename))
-        for v in ty.values:
-            f.write("    json_string = %s_to_json(ctx, %s);\n" % \
-                    (ty.typename, v.name))
-            f.write("    printf(\"\\t%s = %%d = %%s\", " \
-                    "%s, json_string);\n" %\
-                    (v.valuename, v.name))
-            f.write("    free(json_string);\n");
-            f.write("    json_string = NULL;\n");
-        f.write("\n")
-
-        f.write("    printf(\"%s -- from string:\\n\");\n" % (ty.typename))
-        for v in [v.valuename for v in ty.values] + ["AN INVALID VALUE"]:
-            n = randomize_case(v)
-            f.write("    %s_val = -1;\n" % (ty.typename))
-            f.write("    rc = %s_from_string(\"%s\", &%s_val);\n" %\
-                    (ty.typename, n, ty.typename))
-
-            f.write("    printf(\"\\t%s = \\\"%%s\\\" = %%d (rc %%d)\\n\", " \
-                    "\"%s\", %s_val, rc);\n" %\
-                    (v, n, ty.typename))
-        f.write("\n")
-
-    f.write("""
-
-    libxl_ctx_free(ctx);
-    xtl_logger_destroy((xentoollog_logger*)logger);
-
-    return 0;
-}
-""")
diff --git a/tools/libxl/gentypes.py b/tools/libxl/gentypes.py
deleted file mode 100644 (file)
index 9a45e45..0000000
+++ /dev/null
@@ -1,797 +0,0 @@
-#!/usr/bin/python
-
-from __future__ import print_function
-
-import sys
-import re
-
-import idl
-
-def libxl_C_instance_of(ty, instancename):
-    if isinstance(ty, idl.Aggregate) and ty.typename is None:
-        if instancename is None:
-            return libxl_C_type_define(ty)
-        else:
-            return libxl_C_type_define(ty) + " " + instancename
-
-    s = ""
-    if isinstance(ty, idl.Array):
-        s += libxl_C_instance_of(ty.lenvar.type, ty.lenvar.name) + ";\n"
-
-    return s + ty.typename + " " + instancename
-
-def libxl_C_type_define(ty, indent = ""):
-    s = ""
-    if isinstance(ty, idl.Enumeration):
-        if ty.typename is None:
-            s += "enum {\n"
-        else:
-            s += "typedef enum %s {\n" % ty.typename
-
-        for v in ty.values:
-            x = "%s = %d" % (v.name, v.value)
-            x = x.replace("\n", "\n    ")
-            s += "    " + x + ",\n"
-        if ty.typename is None:
-            s += "}"
-        else:
-            s += "} %s" % ty.typename
-
-    elif isinstance(ty, idl.Aggregate):
-        if isinstance(ty, idl.KeyedUnion):
-            s += libxl_C_instance_of(ty.keyvar.type, ty.keyvar.name) + ";\n"
-            
-        if ty.typename is None:
-            s += "%s {\n" % ty.kind
-        else:
-            s += "typedef %s %s {\n" % (ty.kind, ty.typename)
-
-        for f in ty.fields:
-            if isinstance(ty, idl.KeyedUnion) and f.type is None: continue
-            
-            x = libxl_C_instance_of(f.type, f.name)
-            if f.const:
-                x = "const " + x
-            x = x.replace("\n", "\n    ")
-            s += "    " + x + ";\n"
-        if ty.typename is None:
-            s += "}"
-        else:
-            s += "} %s" % ty.typename
-    else:
-        raise NotImplementedError("%s" % type(ty))
-    return s.replace("\n", "\n%s" % indent)
-
-def libxl_C_type_dispose(ty, v, indent = "    ", parent = None):
-    s = ""
-    if isinstance(ty, idl.KeyedUnion):
-        if parent is None:
-            raise Exception("KeyedUnion type must have a parent")
-        s += "switch (%s) {\n" % (parent + ty.keyvar.name)
-        for f in ty.fields:
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            s += "case %s:\n" % f.enumname
-            if f.type is not None:
-                s += libxl_C_type_dispose(f.type, fexpr, indent + "    ", nparent)
-            s += "    break;\n"
-        s += "}\n"
-    elif isinstance(ty, idl.Array):
-        if parent is None:
-            raise Exception("Array type must have a parent")
-        if ty.elem_type.dispose_fn is not None:
-            s += "{\n"
-            s += "    int i;\n"
-            s += "    for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
-            s += libxl_C_type_dispose(ty.elem_type, v+"[i]",
-                                      indent + "        ", parent)
-        if ty.dispose_fn is not None:
-            if ty.elem_type.dispose_fn is not None:
-                s += "    "
-            s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
-        if ty.elem_type.dispose_fn is not None:
-            s += "}\n"
-    elif isinstance(ty, idl.Struct) and (parent is None or ty.dispose_fn is None):
-        for f in [f for f in ty.fields if not f.const]:
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            s += libxl_C_type_dispose(f.type, fexpr, "", nparent)
-    else:
-        if ty.dispose_fn is not None:
-            s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None))
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_copy(ty, v, w, indent = "    ", vparent = None, wparent = None):
-    s = ""
-
-    if vparent is None:
-        s += "GC_INIT(ctx);\n";
-
-    if isinstance(ty, idl.KeyedUnion):
-        if vparent is None or wparent is None:
-            raise Exception("KeyedUnion type must have a parent")
-        s += "%s = %s;\n" % ((vparent + ty.keyvar.name), (wparent + ty.keyvar.name))
-        s += "switch (%s) {\n" % (wparent + ty.keyvar.name)
-        for f in ty.fields:
-            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
-            (wnparent,wfexpr) = ty.member(w, f, wparent is None)
-            s += "case %s:\n" % f.enumname
-            if f.type is not None:
-                s += libxl_C_type_copy(f.type, vfexpr, wfexpr, indent + "    ",
-                                       vnparent, wnparent)
-            s += "    break;\n"
-        s += "}\n"
-    elif isinstance(ty, idl.Array):
-        if vparent is None or wparent is None:
-            raise Exception("Array type must have a parent")
-        s += "%s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (ty.pass_arg(v, vparent is None),
-                                                               (wparent + ty.lenvar.name),
-                                                               ty.pass_arg(w, wparent is None))
-        s += "%s = %s;\n" % ((vparent + ty.lenvar.name), (wparent + ty.lenvar.name))
-        s += "{\n"
-        s += "    int i;\n"
-        s += "    for (i=0; i<%s; i++)\n" % (wparent + ty.lenvar.name)
-        s += libxl_C_type_copy(ty.elem_type, v+"[i]", w+"[i]",
-                               indent + "        ", vparent, wparent)
-        s += "}\n"
-    elif isinstance(ty, idl.Struct) and ((vparent is None and wparent is None) or ty.copy_fn is None):
-        for f in [f for f in ty.fields if not f.const and not f.type.private]:
-            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
-            (wnparent,wfexpr) = ty.member(w, f, wparent is None)
-            s += libxl_C_type_copy(f.type, vfexpr, wfexpr, "", vnparent, wnparent)
-    else:
-        if ty.copy_fn is not None:
-            s += "%s(ctx, %s, %s);\n" % (ty.copy_fn,
-                                         ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_REFERENCE),
-                                         ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_REFERENCE))
-
-        else:
-            s += "%s = %s;\n" % (ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_VALUE),
-                                 ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_VALUE))
-
-    if vparent is None:
-        s += "GC_FREE;\n"
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_init_members(ty, nesting = 0):
-    """Returns a list of members of ty which require a separate init"""
-
-    if isinstance(ty, idl.Aggregate):
-        return [f for f in ty.fields if not f.const and isinstance(f.type,idl.KeyedUnion)]
-    else:
-        return []
-    
-def libxl_C_type_do_init(ty, pass_arg, need_zero=True, indent="    "):
-    s=indent
-    if ty.init_val is not None:
-        s+= "%s = %s;\n" % (pass_arg(idl.PASS_BY_VALUE), ty.init_val)
-    elif ty.init_fn is not None:
-        s+= "%s(%s);\n" % (ty.init_fn, pass_arg(idl.PASS_BY_REFERENCE))
-    elif need_zero:
-        ptr = pass_arg(idl.PASS_BY_REFERENCE)
-        s+= "memset(%s, 0, sizeof(*%s));\n" % (ptr, ptr)
-    else:
-        s=""
-    return s
-
-def _libxl_C_type_init(ty, v, indent = "    ", parent = None, subinit=False):
-    s = ""
-    if isinstance(ty, idl.KeyedUnion):
-        if parent is None:
-            raise Exception("KeyedUnion type must have a parent")
-        if subinit:
-            s += "switch (%s) {\n" % (parent + ty.keyvar.name)
-            for f in ty.fields:
-                (nparent,fexpr) = ty.member(v, f, parent is None)
-                s += "case %s:\n" % f.enumname
-                if f.type is not None:
-                    s += _libxl_C_type_init(f.type, fexpr, "    ", nparent)
-                s += "    break;\n"
-            s += "}\n"
-        else:
-            if ty.keyvar.init_val:
-                s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.init_val)
-            elif ty.keyvar.type.init_val:
-                s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.type.init_val)
-    elif isinstance(ty, idl.Struct) and (parent is None or ty.init_fn is None):
-        for f in [f for f in ty.fields if not f.const]:
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            if f.init_val is not None:
-                s += "%s = %s;\n" % (fexpr, f.init_val)
-            else:
-                s += _libxl_C_type_init(f.type, fexpr, "", nparent)
-    else:
-        if ty.init_val is not None:
-            s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), ty.init_val)
-        elif ty.init_fn is not None:
-            s += "%s(%s);\n" % (ty.init_fn, ty.pass_arg(v, parent is None))
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_init(ty):
-    s = ""
-    s += "void %s(%s)\n" % (ty.init_fn, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))
-    s += "{\n"
-    s += "    memset(p, '\\0', sizeof(*p));\n"
-    s += _libxl_C_type_init(ty, "p")
-    s += "}\n"
-    s += "\n"
-    return s
-
-def libxl_C_type_member_init(ty, field):
-    if not isinstance(field.type, idl.KeyedUnion):
-        raise Exception("Only KeyedUnion is supported for member init")
-
-    ku = field.type
-    
-    s = ""
-    s += "void %s(%s, %s)\n" % (ty.init_fn + "_" + ku.keyvar.name,
-                                ty.make_arg("p", passby=idl.PASS_BY_REFERENCE),
-                                ku.keyvar.type.make_arg(ku.keyvar.name))
-    s += "{\n"
-    
-    if ku.keyvar.init_val is not None:
-        init_val = ku.keyvar.init_val
-    elif ku.keyvar.type.init_val is not None:
-        init_val = ku.keyvar.type.init_val
-    else:
-        init_val = None
-        
-    (nparent,fexpr) = ty.member(ty.pass_arg("p"), ku.keyvar, isref=True)
-    if init_val is not None:
-        s += "    assert(%s == %s);\n" % (fexpr, init_val)
-    else:
-        s += "    assert(!%s);\n" % (fexpr)
-    s += "    %s = %s;\n" % (fexpr, ku.keyvar.name)
-
-    (nparent,fexpr) = ty.member(ty.pass_arg("p"), field, isref=True)
-    s += _libxl_C_type_init(ku, fexpr, parent=nparent, subinit=True)
-    s += "}\n"
-    s += "\n"
-    return s
-
-def libxl_C_type_gen_map_key(f, parent, indent = ""):
-    s = ""
-    if isinstance(f.type, idl.KeyedUnion):
-        s += "switch (%s) {\n" % (parent + f.type.keyvar.name)
-        for x in f.type.fields:
-            v = f.type.keyvar.name + "." + x.name
-            s += "case %s:\n" % x.enumname
-            s += "    s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (v, v)
-            s += "    if (s != yajl_gen_status_ok)\n"
-            s += "        goto out;\n"
-            s += "    break;\n"
-        s += "}\n"
-    else:
-        s += "s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (f.name, f.name)
-        s += "if (s != yajl_gen_status_ok)\n"
-        s += "    goto out;\n"
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_copy_deprecated(field, v, indent = "    ", vparent = None):
-    s = ""
-
-    if isinstance(field.type, idl.KeyedUnion):
-        if vparent is None:
-            raise Exception("KeyedUnion type must have a parent")
-        s += "switch (%s) {\n" % (vparent + field.type.keyvar.name)
-        for f in [f for f in field.type.fields if not f.const]:
-            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
-            s += "case %s:\n" % f.enumname
-            if f.type is not None:
-                s += libxl_C_type_copy_deprecated(f, vfexpr, indent, vnparent)
-            s+= "    break;\n"
-        s+="}\n";
-    elif isinstance(field.type, idl.Array) and field.deprecated_by:
-        raise Exception("Array type is not supported for deprecation")
-    elif isinstance(field.type, idl.Struct) and field.type.copy_fn is None:
-        for f in [f for f in field.type.fields if not f.const]:
-            (vnparent,vfexpr) = ty.member(v, f, vparent is None)
-            s += libxl_C_type_copy_deprecated(f, vfexpr, "", vnparent)
-    elif field.deprecated_by is not None:
-        if field.type.check_default_fn is None:
-            raise Exception(
-"Deprecated field %s type doesn't have a default value checker" % field.name)
-        field_pass = lambda by: field.type.pass_arg(v, vparent is None,
-                                                    passby=by)
-        field_val = field_pass(idl.PASS_BY_VALUE)
-        field_ptr = field_pass(idl.PASS_BY_REFERENCE)
-        s+= "if (!%s(&p->%s) && !%s(%s))\n" % (field.type.check_default_fn,
-                                               field.deprecated_by,
-                                               field.type.check_default_fn,
-                                               field_ptr)
-        s+= "    return -EINVAL;\n"
-        s+="(void) (&p->%s == %s);\n" % (field.deprecated_by, field_ptr)
-        s+= "if (%s(&p->%s)) {\n" % (field.type.check_default_fn,
-                                     field.deprecated_by)
-        s+= "    "
-        if field.type.copy_fn is not None:
-            s+= "%s(ctx, &p->%s, %s);\n" % (field.type.copy_fn,
-                                            field.deprecated_by, field_ptr)
-        else:
-            s+= "p->%s = %s;\n" % (field.deprecated_by, field_val)
-
-        if field.type.dispose_fn is not None:
-            s+= "    %s(%s);\n" % (field.type.dispose_fn,
-                                   field.type.pass_arg(v, vparent is None))
-        s+=libxl_C_type_do_init(field.type, field_pass)
-        s+= "}\n"
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def get_init_val(f):
-    if f.init_val is not None:
-        return f.init_val
-    elif f.type.init_val is not None:
-        return f.type.init_val
-    return None
-
-def get_default_expr(f, nparent, fexpr):
-    if isinstance(f.type, idl.Aggregate):
-        return "1 /* always generate JSON output for aggregate type */"
-
-    if isinstance(f.type, idl.Array):
-        return "%s && %s" % (fexpr, nparent + f.type.lenvar.name)
-
-    init_val = get_init_val(f)
-    if init_val is not None:
-        return "%s != %s" % (fexpr, init_val)
-
-    if f.type.check_default_fn:
-        return "!%s(&%s)" % (f.type.check_default_fn, fexpr)
-
-    return "%s" % fexpr
-
-def libxl_C_type_gen_json(ty, v, indent = "    ", parent = None):
-    s = ""
-    if parent is None:
-        s += "yajl_gen_status s;\n"
-
-    if isinstance(ty, idl.Array):
-        if parent is None:
-            raise Exception("Array type must have a parent")
-        s += "{\n"
-        s += "    int i;\n"
-        s += "    s = yajl_gen_array_open(hand);\n"
-        s += "    if (s != yajl_gen_status_ok)\n"
-        s += "        goto out;\n"
-        s += "    for (i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name)
-        s += libxl_C_type_gen_json(ty.elem_type, v+"[i]",
-                                   indent + "        ", parent)
-        s += "    }\n"
-        s += "    s = yajl_gen_array_close(hand);\n"
-        s += "    if (s != yajl_gen_status_ok)\n"
-        s += "        goto out;\n"
-        s += "}\n"
-    elif isinstance(ty, idl.Enumeration):
-        s += "s = libxl__yajl_gen_enum(hand, %s_to_string(%s));\n" % (ty.typename, ty.pass_arg(v, parent is None))
-        s += "if (s != yajl_gen_status_ok)\n"
-        s += "    goto out;\n"
-    elif isinstance(ty, idl.KeyedUnion):
-        if parent is None:
-            raise Exception("KeyedUnion type must have a parent")
-        s += "switch (%s) {\n" % (parent + ty.keyvar.name)
-        for f in ty.fields:
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            s += "case %s:\n" % f.enumname
-            if f.type is not None:
-                s += libxl_C_type_gen_json(f.type, fexpr, indent + "    ", nparent)
-            else:
-                s += "    s = yajl_gen_map_open(hand);\n"
-                s += "    if (s != yajl_gen_status_ok)\n"
-                s += "        goto out;\n"
-                s += "    s = yajl_gen_map_close(hand);\n"
-                s += "    if (s != yajl_gen_status_ok)\n"
-                s += "        goto out;\n"
-            s += "    break;\n"
-        s += "}\n"
-    elif isinstance(ty, idl.Struct) and (parent is None or ty.json_gen_fn is None):
-        s += "s = yajl_gen_map_open(hand);\n"
-        s += "if (s != yajl_gen_status_ok)\n"
-        s += "    goto out;\n"
-        for f in [f for f in ty.fields if not f.const and not f.type.private]:
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            default_expr = get_default_expr(f, nparent, fexpr)
-            s += "if (%s) {\n" % default_expr
-
-            s += libxl_C_type_gen_map_key(f, nparent, "    ")
-            s += libxl_C_type_gen_json(f.type, fexpr, "    ", nparent)
-
-            s += "}\n"
-
-        s += "s = yajl_gen_map_close(hand);\n"
-        s += "if (s != yajl_gen_status_ok)\n"
-        s += "    goto out;\n"
-    else:
-        if ty.json_gen_fn is not None:
-            s += "s = %s(hand, %s);\n" % (ty.json_gen_fn, ty.pass_arg(v, parent is None))
-            s += "if (s != yajl_gen_status_ok)\n"
-            s += "    goto out;\n"
-
-    if parent is None:
-        s += "out:\n"
-        s += "return s;\n"
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_to_json(ty, v, indent = "    "):
-    s = ""
-    gen = "(libxl__gen_json_callback)&%s_gen_json" % ty.typename
-    s += "return libxl__object_to_json(ctx, \"%s\", %s, (void *)%s);\n" % (ty.typename, gen, ty.pass_arg(v, passby=idl.PASS_BY_REFERENCE))
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_parse_json(ty, w, v, indent = "    ", parent = None, discriminator = None):
-    s = ""
-    if parent is None:
-        s += "int rc = 0;\n"
-        s += "const libxl__json_object *x __attribute__((__unused__)) = o;\n"
-
-    if isinstance(ty, idl.Array):
-        if parent is None:
-            raise Exception("Array type must have a parent")
-        if discriminator is not None:
-            raise Exception("Only KeyedUnion can have discriminator")
-        lenvar = parent + ty.lenvar.name
-        s += "{\n"
-        s += "    libxl__json_object *t;\n"
-        s += "    int i;\n"
-        s += "    if (!libxl__json_object_is_array(x)) {\n"
-        s += "        rc = -1;\n"
-        s += "        goto out;\n"
-        s += "    }\n"
-        s += "    %s = x->u.array->count;\n" % lenvar
-        s += "    %s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (v, lenvar, v)
-        s += "    if (!%s && %s != 0) {\n" % (v, lenvar)
-        s += "        rc = -1;\n"
-        s += "        goto out;\n"
-        s += "    }\n"
-        s += "    for (i=0; (t=libxl__json_array_get(x,i)); i++) {\n"
-        s += libxl_C_type_do_init(ty.elem_type,
-                    lambda by: ("&" if by == idl.PASS_BY_REFERENCE else "")+
-                               ("%s[i]" % v),
-                                  need_zero=False, indent=indent+"    ")
-        s += libxl_C_type_parse_json(ty.elem_type, "t", v+"[i]",
-                                     indent + "    ", parent)
-        s += "    }\n"
-        s += "    if (i != %s) {\n" % lenvar
-        s += "        rc = -1;\n"
-        s += "        goto out;\n"
-        s += "    }\n"
-        s += "}\n"
-    elif isinstance(ty, idl.Enumeration):
-        if discriminator is not None:
-            raise Exception("Only KeyedUnion can have discriminator")
-        s += "{\n"
-        s += "    const char *enum_str;\n"
-        s += "    if (!libxl__json_object_is_string(%s)) {\n" % w
-        s += "        rc = -1;\n"
-        s += "        goto out;\n"
-        s += "    }\n"
-        s += "    enum_str = libxl__json_object_get_string(%s);\n" % w
-        s += "    rc = %s_from_string(enum_str, %s);\n" % (ty.typename, ty.pass_arg(v, parent is None, idl.PASS_BY_REFERENCE))
-        s += "    if (rc)\n"
-        s += "        goto out;\n"
-        s += "}\n"
-    elif isinstance(ty, idl.KeyedUnion):
-        if parent is None:
-            raise Exception("KeyedUnion type must have a parent")
-        if discriminator is None:
-            raise Excpetion("KeyedUnion type must have a discriminator")
-        for f in ty.fields:
-            if f.enumname != discriminator:
-                continue
-            (nparent,fexpr) = ty.member(v, f, parent is None)
-            if f.type is not None:
-                s += libxl_C_type_parse_json(f.type, w, fexpr, indent + "    ", nparent)
-    elif isinstance(ty, idl.Struct) and (parent is None or ty.json_parse_fn is None):
-        if discriminator is not None:
-            raise Exception("Only KeyedUnion can have discriminator")
-        for f in [f for f in ty.fields if not f.const and not f.type.private]:
-            saved_var_name = "saved_%s" % f.name
-            s += "{\n"
-            s += "    const libxl__json_object *%s = x;\n" % saved_var_name
-            if isinstance(f.type, idl.KeyedUnion):
-                for x in f.type.fields:
-                    s += "    x = libxl__json_map_get(\"%s\", %s, JSON_MAP);\n" % \
-                         (f.type.keyvar.name + "." + x.name, w)
-                    s += "    if (x) {\n"
-                    (nparent, fexpr) = ty.member(v, f.type.keyvar, parent is None)
-                    s += "        %s_init_%s(%s, %s);\n" % (ty.typename, f.type.keyvar.name, v, x.enumname)
-                    (nparent,fexpr) = ty.member(v, f, parent is None)
-                    s += libxl_C_type_parse_json(f.type, "x", fexpr, "  ", nparent, x.enumname)
-                    s += "    }\n"
-            else:
-                s += "    x = libxl__json_map_get(\"%s\", %s, %s);\n" % (f.name, w, f.type.json_parse_type)
-                s += "    if (x) {\n"
-                (nparent,fexpr) = ty.member(v, f, parent is None)
-                s += libxl_C_type_parse_json(f.type, "x", fexpr, "        ", nparent)
-                s += "    }\n"
-            s += "    x = %s;\n" % saved_var_name
-            s += "}\n"
-    else:
-        if discriminator is not None:
-            raise Exception("Only KeyedUnion can have discriminator")
-        if ty.json_parse_fn is not None:
-            s += "rc = %s(gc, %s, &%s);\n" % (ty.json_parse_fn, w, v)
-            s += "if (rc)\n"
-            s += "    goto out;\n"
-
-    if parent is None:
-        s += "out:\n"
-        s += "return rc;\n"
-
-    if s != "":
-        s = indent +s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_type_from_json(ty, v, w, indent = "    "):
-    s = ""
-    parse = "(libxl__json_parse_callback)&%s_parse_json" % (ty.namespace + "_" + ty.rawname)
-    s += "return libxl__object_from_json(ctx, \"%s\", %s, %s, %s);\n" % (ty.typename, parse, v, w)
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_enum_to_string(ty, e, indent = "    "):
-    s = ""
-    s += "switch(%s) {\n" % e
-    for v in ty.values:
-        s += "    case %s:\n" % (v.name)
-        s += "        return \"%s\";\n" % (v.valuename.lower())
-    s += "    default:\n "
-    s += "        return NULL;\n"
-    s += "}\n"
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_enum_strings(ty, indent=""):
-    s = ""
-    s += "libxl_enum_string_table %s_string_table[] = {\n" % (ty.typename)
-    for v in ty.values:
-        s += "    { .s = \"%s\", .v = %s },\n" % (v.valuename.lower(), v.name)
-    s += "    { NULL, -1 },\n"
-    s += "};\n"
-    s += "\n"
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-def libxl_C_enum_from_string(ty, str, e, indent = "    "):
-    s = ""
-    s += "return libxl__enum_from_string(%s_string_table,\n" % ty.typename
-    s += "                               %s, (int *)%s);\n" % (str, e)
-
-    if s != "":
-        s = indent + s
-    return s.replace("\n", "\n%s" % indent).rstrip(indent)
-
-
-if __name__ == '__main__':
-    if len(sys.argv) != 6:
-        print("Usage: gentypes.py <idl> <header> <header-private> <header-json> <implementation>", file=sys.stderr)
-        sys.exit(1)
-
-    (_, idlname, header, header_private, header_json, impl) = sys.argv
-
-    (builtins,types) = idl.parse(idlname)
-
-    print("outputting libxl type definitions to %s" % header)
-
-    f = open(header, "w")
-
-    header_define = header.upper().replace('.','_')
-    f.write("""#ifndef %s
-#define %s
-
-/*
- * DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-""" % (header_define, header_define, " ".join(sys.argv)))
-
-    for ty in types:
-        f.write(libxl_C_type_define(ty) + ";\n")
-        if ty.dispose_fn is not None:
-            f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.dispose_fn, ty.make_arg("p")))
-        if ty.copy_deprecated_fn is not None:
-            f.write("%sint %s(libxl_ctx *ctx, %s);\n" % (ty.hidden(),
-                                                         ty.copy_deprecated_fn,
-                                                         ty.make_arg("p")))
-        if ty.copy_fn is not None:
-            f.write("%svoid %s(libxl_ctx *ctx, %s, const %s);\n" % (ty.hidden(), ty.copy_fn,
-                                              ty.make_arg("dst"), ty.make_arg("src")))
-        if ty.init_fn is not None:
-            f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.init_fn, ty.make_arg("p")))
-            for field in libxl_init_members(ty):
-                if not isinstance(field.type, idl.KeyedUnion):
-                    raise Exception("Only KeyedUnion is supported for member init")
-                ku = field.type
-                f.write("%svoid %s(%s, %s);\n" % (ty.hidden(), ty.init_fn + "_" + ku.keyvar.name,
-                                               ty.make_arg("p"),
-                                               ku.keyvar.type.make_arg(ku.keyvar.name)))
-        if ty.json_gen_fn is not None:
-            f.write("%schar *%s_to_json(libxl_ctx *ctx, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
-        if ty.json_parse_fn is not None:
-            f.write("%sint %s_from_json(libxl_ctx *ctx, %s, const char *s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-        if isinstance(ty, idl.Enumeration):
-            f.write("%sconst char *%s_to_string(%s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p")))
-            f.write("%sint %s_from_string(const char *s, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("e", passby=idl.PASS_BY_REFERENCE)))
-            f.write("%sextern libxl_enum_string_table %s_string_table[];\n" % (ty.hidden(), ty.typename))
-        f.write("\n")
-
-    f.write("""#endif /* %s */\n""" % (header_define))
-    f.close()
-
-    print("outputting libxl JSON definitions to %s" % header_json)
-
-    f = open(header_json, "w")
-
-    header_json_define = header_json.upper().replace('.','_')
-    f.write("""#ifndef %s
-#define %s
-
-/*
- * DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-""" % (header_json_define, header_json_define, " ".join(sys.argv)))
-
-    for ty in [ty for ty in types if ty.json_gen_fn is not None]:
-        f.write("%syajl_gen_status %s_gen_json(yajl_gen hand, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-
-    f.write("\n")
-    f.write("""#endif /* %s */\n""" % header_json_define)
-    f.close()
-
-    print("outputting libxl type internal definitions to %s" % header_private)
-
-    f = open(header_private, "w")
-
-    header_private_define = header_private.upper().replace('.','_')
-    f.write("""#ifndef %s
-#define %s
-
-/*
- * DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-""" % (header_private_define, header_private_define, " ".join(sys.argv)))
-
-    for ty in [ty for ty in types if ty.json_parse_fn is not None]:
-        f.write("%sint %s_parse_json(libxl__gc *gc, const libxl__json_object *o, %s);\n" % \
-                (ty.hidden(), ty.namespace + "_" + ty.rawname,
-                 ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-
-    f.write("\n")
-    f.write("""#endif /* %s */\n""" % header_json_define)
-    f.close()
-
-    print("outputting libxl type implementations to %s" % impl)
-
-    f = open(impl, "w")
-    f.write("""
-/* DO NOT EDIT.
- *
- * This file is autogenerated by
- * "%s"
- */
-
-#include "libxl_osdeps.h"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "libxl_internal.h"
-
-
-""" % " ".join(sys.argv))
-
-    for ty in [t for t in types if t.dispose_fn is not None and t.autogenerate_dispose_fn]:
-        f.write("void %s(%s)\n" % (ty.dispose_fn, ty.make_arg("p")))
-        f.write("{\n")
-        f.write("    if (!p) return;\n")
-        f.write(libxl_C_type_dispose(ty, "p"))
-        f.write("    memset(p, 0, sizeof(*p));\n")
-        f.write("}\n")
-        f.write("\n")
-
-    for ty in [t for t in types if t.copy_fn and t.autogenerate_copy_fn]:
-        f.write("void %s(libxl_ctx *ctx, %s, const %s)\n" % (ty.copy_fn,
-                                       ty.make_arg("dst", passby=idl.PASS_BY_REFERENCE),
-                                       ty.make_arg("src", passby=idl.PASS_BY_REFERENCE)))
-        f.write("{\n")
-        f.write(libxl_C_type_copy(ty, "dst", "src"))
-        f.write("}\n")
-        f.write("\n")
-        
-    for ty in [t for t in types if t.copy_deprecated_fn]:
-        f.write("int %s(libxl_ctx *ctx, %s)\n" % (ty.copy_deprecated_fn,
-            ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-        f.write("{\n")
-        for field in [field for field in ty.fields if not field.const]:
-            (vnparent,vfexpr) = ty.member("p", field, True)
-            f.write(libxl_C_type_copy_deprecated(field, vfexpr,
-                                                 vparent = vnparent))
-        f.write("    return 0;\n")
-        f.write("}\n")
-        f.write("\n")
-
-    for ty in [t for t in types if t.init_fn is not None and t.autogenerate_init_fn]:
-        f.write(libxl_C_type_init(ty))
-        for field in libxl_init_members(ty):
-            f.write(libxl_C_type_member_init(ty, field))
-
-    for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
-        f.write("const char *%s_to_string(%s e)\n" % (ty.typename, ty.typename))
-        f.write("{\n")
-        f.write(libxl_C_enum_to_string(ty, "e"))
-        f.write("}\n")
-        f.write("\n")
-
-        f.write(libxl_C_enum_strings(ty))
-
-        f.write("int %s_from_string(const char *s, %s *e)\n" % (ty.typename, ty.typename))
-        f.write("{\n")
-        f.write(libxl_C_enum_from_string(ty, "s", "e"))
-        f.write("}\n")
-        f.write("\n")
-
-    for ty in [t for t in types if t.json_gen_fn is not None]:
-        f.write("yajl_gen_status %s_gen_json(yajl_gen hand, %s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-        f.write("{\n")
-        f.write(libxl_C_type_gen_json(ty, "p"))
-        f.write("}\n")
-        f.write("\n")
-
-        f.write("char *%s_to_json(libxl_ctx *ctx, %s)\n" % (ty.typename, ty.make_arg("p")))
-        f.write("{\n")
-        f.write(libxl_C_type_to_json(ty, "p"))
-        f.write("}\n")
-        f.write("\n")
-
-    for ty in [t for t in types if t.json_parse_fn is not None]:
-        f.write("int %s_parse_json(libxl__gc *gc, const libxl__json_object *%s, %s)\n" % \
-                (ty.namespace + "_" + ty.rawname,"o",ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-        f.write("{\n")
-        f.write(libxl_C_type_parse_json(ty, "o", "p"))
-        f.write("}\n")
-        f.write("\n")
-
-        f.write("int %s_from_json(libxl_ctx *ctx, %s, const char *s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
-        f.write("{\n")
-        if not isinstance(ty, idl.Enumeration):
-            f.write("    %s_init(p);\n" % ty.typename)
-        f.write(libxl_C_type_from_json(ty, "p", "s"))
-        f.write("}\n")
-        f.write("\n")
-
-    f.close()
diff --git a/tools/libxl/idl.py b/tools/libxl/idl.py
deleted file mode 100644 (file)
index d736750..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-from __future__ import print_function
-
-import sys
-
-PASS_BY_VALUE = 1
-PASS_BY_REFERENCE = 2
-
-DIR_NONE = 0
-DIR_IN   = 1
-DIR_OUT  = 2
-DIR_BOTH = 3
-
-_default_namespace = ""
-def namespace(s):
-    if type(s) != str:
-        raise TypeError("Require a string for the default namespace.")
-    global _default_namespace
-    _default_namespace = s
-
-def _get_default_namespace():
-    global _default_namespace
-    return _default_namespace
-
-_default_hidden = False
-def hidden(b):
-    global _default_hidden
-    _default_hidden = b
-
-def _get_default_hidden():
-    global _default_hidden
-    return _default_hidden
-
-class Type(object):
-    def __init__(self, typename, **kwargs):
-        self.namespace = kwargs.setdefault('namespace',
-                _get_default_namespace())
-        self._hidden = kwargs.setdefault('hidden', _get_default_hidden())
-        self.dir = kwargs.setdefault('dir', DIR_BOTH)
-        if self.dir not in [DIR_NONE, DIR_IN, DIR_OUT, DIR_BOTH]:
-            raise ValueError
-
-        self.passby = kwargs.setdefault('passby', PASS_BY_VALUE)
-        if self.passby not in [PASS_BY_VALUE, PASS_BY_REFERENCE]:
-            raise ValueError
-
-        self.private = kwargs.setdefault('private', False)
-
-        if typename is None: # Anonymous type
-            self.typename = None
-            self.rawname = None
-        elif self.namespace is None: # e.g. system provided types
-            self.typename = typename
-            self.rawname = typename
-        else:
-            self.typename = self.namespace + typename
-            self.rawname = typename
-
-        if self.typename is not None:
-            self.dispose_fn = kwargs.setdefault('dispose_fn', self.typename + "_dispose")
-        else:
-            self.dispose_fn = kwargs.setdefault('dispose_fn', None)
-
-        self.autogenerate_dispose_fn = kwargs.setdefault('autogenerate_dispose_fn', True)
-
-        if self.typename is not None:
-            self.copy_fn = kwargs.setdefault('copy_fn', self.typename + "_copy")
-        else:
-            self.copy_fn = kwargs.setdefault('copy_fn', None)
-
-        self.autogenerate_copy_fn = kwargs.setdefault('autogenerate_copy_fn', True)
-
-        self.init_fn = kwargs.setdefault('init_fn', None)
-        self.init_val = kwargs.setdefault('init_val', None)
-        self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', False)
-
-        self.check_default_fn = kwargs.setdefault('check_default_fn', None)
-        self.copy_deprecated_fn = kwargs.setdefault('copy_deprecated_fn',
-                                                    None)
-
-        if self.typename is not None and not self.private:
-            self.json_gen_fn = kwargs.setdefault('json_gen_fn', self.typename + "_gen_json")
-            self.json_parse_type = kwargs.setdefault('json_parse_type', "JSON_ANY")
-            if self.namespace is not None:
-                self.json_parse_fn = kwargs.setdefault('json_parse_fn',
-                                                       self.namespace + "_" + self.rawname  + "_parse_json")
-            else:
-                self.json_parse_fn = kwargs.setdefault('json_parse_fn',
-                                                       self.typename + "_parse_json")
-        else:
-            self.json_gen_fn = kwargs.setdefault('json_gen_fn', None)
-            self.json_parse_type = kwargs.setdefault('json_parse_type', None)
-            self.json_parse_fn = kwargs.setdefault('json_parse_fn', None)
-
-        self.autogenerate_json = kwargs.setdefault('autogenerate_json', True)
-
-    def marshal_in(self):
-        return self.dir in [DIR_IN, DIR_BOTH]
-    def marshal_out(self):
-        return self.dir in [DIR_OUT, DIR_BOTH]
-
-    def hidden(self):
-        if self._hidden:
-            return "_hidden "
-        else:
-            return ""
-
-    def make_arg(self, n, passby=None):
-        if passby is None: passby = self.passby
-
-        if passby == PASS_BY_REFERENCE:
-            return "%s *%s" % (self.typename, n)
-        else:
-            return "%s %s" % (self.typename, n)
-
-    def pass_arg(self, n, isref=None, passby=None):
-        if passby is None: passby = self.passby
-        if isref is None: isref = self.passby == PASS_BY_REFERENCE
-
-        if passby == PASS_BY_REFERENCE:
-            if isref:
-                return "%s" % (n)
-            else:
-                return "&%s" % (n)
-        else:
-            if isref:
-                return "*%s" % (n)
-            else:
-                return "%s" % (n)
-
-class Builtin(Type):
-    """Builtin type"""
-    def __init__(self, typename, **kwargs):
-        kwargs.setdefault('dispose_fn', None)
-        kwargs.setdefault('autogenerate_dispose_fn', False)
-        kwargs.setdefault('autogenerate_json', False)
-        Type.__init__(self, typename, **kwargs)
-
-class Number(Builtin):
-    def __init__(self, ctype, **kwargs):
-        kwargs.setdefault('namespace', None)
-        kwargs.setdefault('dispose_fn', None)
-        kwargs.setdefault('copy_fn', None)
-        kwargs.setdefault('signed', False)
-        kwargs.setdefault('json_gen_fn', "yajl_gen_integer")
-        kwargs.setdefault('json_parse_type', "JSON_INTEGER")
-        # json_parse_fn might be overriden on specific type
-        kwargs.setdefault('json_parse_fn', "libxl__int_parse_json")
-        self.signed = kwargs['signed']
-        Builtin.__init__(self, ctype, **kwargs)
-
-class UInt(Number):
-    def __init__(self, w, **kwargs):
-        kwargs.setdefault('namespace', None)
-        kwargs.setdefault('dispose_fn', None)
-        kwargs.setdefault('json_parse_fn', "libxl__uint%d_parse_json" % w)
-        kwargs.setdefault('copy_fn', None)
-        Number.__init__(self, "uint%d_t" % w, **kwargs)
-
-        self.width = w
-
-class EnumerationValue(object):
-    def __init__(self, enum, value, name, **kwargs):
-        self.enum = enum
-
-        self.valuename = str.upper(name)
-        self.rawname = str.upper(enum.rawname) + "_" + self.valuename
-        self.name = str.upper(enum.value_namespace) + self.rawname
-        self.value = value
-
-class Enumeration(Type):
-    def __init__(self, typename, values, **kwargs):
-        kwargs.setdefault('dispose_fn', None)
-        kwargs.setdefault('copy_fn', None)
-        kwargs.setdefault('json_parse_type', "JSON_STRING")
-        Type.__init__(self, typename, **kwargs)
-
-        self.value_namespace = kwargs.setdefault('value_namespace',
-            self.namespace)
-
-        self.values = []
-        for v in values:
-            # (value, name)
-            (num,name) = v
-            self.values.append(EnumerationValue(self, num, name,
-                                                typename=self.rawname))
-    def lookup(self, name):
-        for v in self.values:
-            if v.valuename == str.upper(name):
-                return v
-        return ValueError
-
-class Field(object):
-    """An element of an Aggregate type"""
-    def __init__(self, type, name, **kwargs):
-        self.type = type
-        self.name = name
-        self.const = kwargs.setdefault('const', False)
-        self.enumname = kwargs.setdefault('enumname', None)
-        self.init_val = kwargs.setdefault('init_val', None)
-        self.deprecated_by = kwargs.setdefault('deprecated_by', None)
-
-class Aggregate(Type):
-    """A type containing a collection of other types"""
-    def __init__(self, kind, typename, fields, **kwargs):
-        kwargs.setdefault('json_parse_type', "JSON_MAP")
-        Type.__init__(self, typename, **kwargs)
-
-        if self.typename is not None:
-            self.init_fn = kwargs.setdefault('init_fn', self.typename + "_init")
-        else:
-            self.init_fn = kwargs.setdefault('init_fn', None)
-
-        self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', True)
-
-        self.kind = kind
-
-        self.fields = []
-        for f in fields:
-            # (name, type[, {kw args}])
-            if len(f) == 2:
-                n,t = f
-                kw = {}
-            elif len(f) == 3:
-                n,t,kw = f
-            else:
-                raise ValueError
-            if n is None:
-                raise ValueError
-            self.fields.append(Field(t,n,**kw))
-
-    # Returns a tuple (stem, field-expr)
-    #
-    # field-expr is a C expression for a field "f" within the struct
-    # "v".
-    #
-    # stem is the stem common to both "f" and any other sibbling field
-    # within the "v".
-    def member(self, v, f, isref):
-        if isref:
-            deref = v + "->"
-        else:
-            deref = v + "."
-
-        if f.name is None: # Anonymous
-            return (deref, deref)
-        else:
-            return (deref, deref + f.name)
-
-class Struct(Aggregate):
-    def __init__(self, name, fields, **kwargs):
-        kwargs.setdefault('passby', PASS_BY_REFERENCE)
-        Aggregate.__init__(self, "struct", name, fields, **kwargs)
-
-    def has_fields(self):
-        return len(self.fields) != 0
-
-class Union(Aggregate):
-    def __init__(self, name, fields, **kwargs):
-        # Generally speaking some intelligence is required to free a
-        # union therefore any specific instance of this class will
-        # need to provide an explicit destructor function.
-        kwargs.setdefault('passby', PASS_BY_REFERENCE)
-        kwargs.setdefault('dispose_fn', None)
-        Aggregate.__init__(self, "union", name, fields, **kwargs)
-
-class KeyedUnion(Aggregate):
-    """A union which is keyed of another variable in the parent structure"""
-    def __init__(self, name, keyvar_type, keyvar_name, fields, **kwargs):
-        Aggregate.__init__(self, "union", name, [], **kwargs)
-
-        if not isinstance(keyvar_type, Enumeration):
-            raise ValueError
-
-        kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in kwargs.items() if x.startswith('keyvar_')])
-        
-        self.keyvar = Field(keyvar_type, keyvar_name, **kv_kwargs)
-
-        for f in fields:
-            # (name, enum, type)
-            e, ty = f
-            ev = keyvar_type.lookup(e)
-            en = ev.name
-            self.fields.append(Field(ty, e, enumname=en))
-
-#
-# Standard Types
-#
-
-void = Builtin("void *", namespace = None)
-bool = Builtin("bool", namespace = None,
-               copy_fn=None,
-               json_gen_fn = "yajl_gen_bool",
-               json_parse_type = "JSON_BOOL",
-               json_parse_fn = "libxl__bool_parse_json",
-               autogenerate_json = False)
-
-size_t = Number("size_t", namespace = None)
-
-integer = Number("int", namespace = None, signed = True)
-
-uint8 = UInt(8)
-uint16 = UInt(16)
-uint32 = UInt(32)
-uint64 = UInt(64, json_gen_fn = "libxl__uint64_gen_json")
-
-string = Builtin("char *", namespace = None, copy_fn = "libxl_string_copy", dispose_fn = "free",
-                 json_gen_fn = "libxl__string_gen_json",
-                 json_parse_type = "JSON_STRING | JSON_NULL",
-                 json_parse_fn = "libxl__string_parse_json",
-                 autogenerate_json = False,
-                 check_default_fn="libxl__string_is_default")
-
-class Array(Type):
-    """An array of the same type"""
-    def __init__(self, elem_type, lenvar_name, **kwargs):
-        kwargs.setdefault('dispose_fn', 'free')
-        kwargs.setdefault('json_parse_type', 'JSON_ARRAY')
-        Type.__init__(self, namespace=elem_type.namespace, typename=elem_type.rawname + " *", **kwargs)
-
-        lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in kwargs.items() if x.startswith('lenvar_')])
-
-        self.lenvar = Field(integer, lenvar_name, **lv_kwargs)
-        self.elem_type = elem_type
-
-class OrderedDict(dict):
-    """A dictionary which remembers insertion order.
-
-       push to back on duplicate insertion"""
-
-    def __init__(self):
-        dict.__init__(self)
-        self.__ordered = []
-
-    def __setitem__(self, key, value):
-        try:
-            self.__ordered.remove(key)
-        except ValueError:
-            pass
-
-        self.__ordered.append(key)
-        dict.__setitem__(self, key, value)
-
-    def ordered_keys(self):
-        return self.__ordered
-    def ordered_values(self):
-        return [self[x] for x in self.__ordered]
-    def ordered_items(self):
-        return [(x,self[x]) for x in self.__ordered]
-
-def parse(f):
-    print("Parsing %s" % f, file=sys.stderr)
-
-    globs = {}
-    locs = OrderedDict()
-
-    for n,t in globals().items():
-        if isinstance(t, Type):
-            globs[n] = t
-        elif isinstance(t,type(object)) and issubclass(t, Type):
-            globs[n] = t
-        elif n in ['PASS_BY_REFERENCE', 'PASS_BY_VALUE',
-                   'DIR_NONE', 'DIR_IN', 'DIR_OUT', 'DIR_BOTH',
-                   'namespace', 'hidden']:
-            globs[n] = t
-
-    try:
-        exec(compile(open(f).read(), f, 'exec'), globs, locs)
-    except SyntaxError as e:
-        raise SyntaxError("Errors were found at line %d while processing %s:\n\t%s"
-                          % (e.lineno, f, e.text))
-
-    types = [t for t in locs.ordered_values() if isinstance(t,Type)]
-
-    builtins = [t for t in types if isinstance(t,Builtin)]
-    types = [t for t in types if not isinstance(t,Builtin)]
-
-    return (builtins,types)
diff --git a/tools/libxl/idl.txt b/tools/libxl/idl.txt
deleted file mode 100644 (file)
index 7440fb3..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-libxl IDL
----------
-
-Each type in the libxl interface is represented by an object of type
-idl.Type (or a subclass thereof). Every local variable defined by the
-.idl file must be an instance of idl.Type (e.g. you may not define
-Python functions or any other construct other than defining variables)
-
-The name of the type must be passed as the first argument to the
-constructor when defining a new type. The name given should not
-contain the initial namespace element (e.g. "libxl_"). See below for
-how to specify a namespace.
-
-The Type.typename contains the C name of the type _including_ the
-namespace element while Type.rawname is always set to the 'base' name
-of the type.
-
-The idl.Type base class has several other properties which apply to
-all types. The properties are set by passing a named parameter to the
-constructor.
-
-Type.namespace: (default: "libxl_")
-
- The namespace in which the type resides. Usually this is "libxl_" but
- system defined and builtin types may differ.
-
- If the typename is not None then the namespace is prepended to the
- type.
-Type.passby: (default: idl.PASS_BY_VALUE)
-
- Defines the manner in which a type should be passed to C
- functions. Valid values for this fields are:
-   idl.PASS_BY_VALUE
-   idl.PASS_BY_REFERENCE
-
-Type.dispose_fn: (default: typename + "_dispose" or None if type == None)
-
- The name of the C function which will free all dynamically allocated
- memory contained within this type (but not the type itself).
-
-Type.autogenerate_dispose_fn: (default: True)
-
- Indicates if the above named Type.dispose_fn should be
- autogenerated.
-
-Type.copy_fn: (default: typename + "_copy" or None if type == None)
-
- The name of the C function which will deep copy all fields within
- this type.
-
-Type.autogenerate_copy_fn: (default: True)
-
- Indicates if the above named Type.copy_fn should be
- autogenerated.
-
-Type.autogenerate_copy_fn
-
-Type.init_val: (default: None)
-
- C expression for the value to initialise instances of this type to.
-
- If present takes precendence over init_fn (see below).
-
-Type.init_fn: (default: typename + "_init" if dir in [IN, BOTH] and
-                        type != None)
-
- The name of the C function which will initialist Type.
-
-Type.autogenerate_init_fn: (default: True if dir in [IN, BOTH])
-
- Indicates if the above named Type.init_fn should be
- autogenerated.
-
-Type.json_gen_fn: (default: typename + "_gen_json" or None if type == None)
-
- The name of the C function which will generate a YAJL data structure
- representing this type.
-
-Type.json_parse_fn: (default: typename + "_parse_json" or None if type == None)
-
- The name of the C function which will parse a libxl JSON structure
- representing this type to C type.
-
-Type.autogenerate_json: (default: True)
-
- Indicates if the above named Type.json_*_fn should be autogenerated.
-
-Type.check_default_fn:
-
- If it's set then calling this function shall return true if this type
- has been set to default value (internal libxl implementation).
-
- If this is not set, that means we can check the type against init_val
- (if it has one) or zero to determine whether the value is default
- value.
-
-Other simple type-Classes
--------------------------
-
-idl.Builtin
-
- Instances of this class represent types which are predefined within
- the system.
-
-idl.UInt
-
- Instances of this class represent the standard uint<N>_t types.
-
- The <N> for a given instance must be passed to the constructor and is
- then available in UInt.width
-
-Complex type-Classes
---------------------
-
-idl.Enumeration
-
-  A class representing an enumeration (named integer values).
-  This class has one property besides the ones defined for the Type
-  class:
-
-  Enumeration.value_namespace: (default: namespace)
-
-    The namespace in which the values of the Enumeration (see below) reside.
-    This prefix is prepended to the name of the value.
-
-  The values are available in the list Enumeration.values. Each
-  element in the list is of type idl.EnumerationValue.
-
-  Each EnumerationValue has the following properties:
-
-    EnumerationValue.enum       Reference to containing Enumeration
-    EnumerationValue.name       The C name of this value, including
-                                    the namespace and typename of the
-                                    containing Enumeration (e.g.
-                                    "LIBXL_FOOENUM_VALUE")
-    EnumerationValue.rawname    The C name of this value, excluding
-                                    the namespace but including the
-                                    typename of the containing
-                                    Enumeration (e.g. "FOOENUM_VALUE")
-    EnumerationValue.valuename  The name of this value, excluding the
-                                    name of the containing Enumeration
-                                    and any namespace (e.g. "VALUE")
-    EnumerationValue.value      The integer value associated with this name.
-
-idl.Aggregate
-
- Base class for type-Classes which contain a number of other types
- (e.g. structs and unions).
-
- The contained types are available in the list Aggregate.fields. Each
- element in the list is of type idl.Field representing a member of the
- aggregate.
-
- Each field has the following properties:
-
-  Field.type     The type of the member (a idl.Type).
-  Field.name     The name of the member (can be None for anonymous
-                 fields).
-  Field.const    Boolean, true if the member is const.
-  Field.init_val The initialisation value for this field. Takes
-                 precendence over both Field.type.init_val and
-                 Field.type.init_fn.
-
-idl.Struct
-
- A subclass of idl.Aggregate representing the C struct type.
-
- Struct.kind == "struct"
-
-idl.Union
-
- A subclass of idl.Aggregate representing the C union type.
-
- Union.kind == "union"
-
-idl.KeyedUnion
-
- A subclass of idl.Aggregate which represents the C union type
- where the currently valid member of the union can be determined based
- upon another member in the containing type. An idl.KeyedUnion must
- always be a member of a containing idl.Aggregate type.
-
- The KeyedUnion.keyvar contains an idl.Field, this is the member of
- the containing type which determines the valid member of the
- union. The idl.Field.type of the keyvar must be an Enumeration type.
-
-idl.Array
-
-  A class representing an array of similar elements. An idl.Array must
-  always be an idl.Field of a containing idl.Aggregate.
-
-  idl.Array.elem_type contains an idl.Type which is the type of each
-  element of the array.
-
-  idl.Array.len_var contains an idl.Field which is added to the parent
-  idl.Aggregate and will contain the length of the array. The field
-  MUST be named num_ARRAYNAME.
-
-Standard Types
---------------
-
-Several standard types a predefined. They are
-
-void                    (void pointer type)
-bool
-size_t
-integer                 24 bit signed integer.
-
-uint{8,16,32,64}        uint{8,16,32,64}_t
-
-domid                   Domain ID
-
-string                  NULL terminated string
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
deleted file mode 100644 (file)
index 621acc8..0000000
+++ /dev/null
@@ -1,831 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-int libxl_ctx_alloc(libxl_ctx **pctx, int version,
-                    unsigned flags, xentoollog_logger * lg)
-{
-    libxl_ctx *ctx = NULL;
-    libxl__gc gc_buf, *gc = NULL;
-    int rc;
-
-    if (version != LIBXL_VERSION) { rc = ERROR_VERSION; goto out; }
-
-    ctx = malloc(sizeof(*ctx));
-    if (!ctx) {
-        xtl_log(lg, XTL_ERROR, errno, "libxl",
-                "%s:%d:%s: Failed to allocate context\n",
-                __FILE__, __LINE__, __func__);
-        rc = ERROR_NOMEM; goto out;
-    }
-
-    memset(ctx, 0, sizeof(libxl_ctx));
-    ctx->lg = lg;
-
-    /* First initialise pointers etc. (cannot fail) */
-
-    ctx->nogc_gc.alloc_maxsize = -1;
-    ctx->nogc_gc.owner = ctx;
-
-    LIBXL_TAILQ_INIT(&ctx->occurred);
-
-    ctx->osevent_hooks = 0;
-
-    ctx->poller_app = 0;
-    LIBXL_LIST_INIT(&ctx->pollers_event);
-    LIBXL_LIST_INIT(&ctx->pollers_idle);
-    LIBXL_LIST_INIT(&ctx->pollers_active);
-
-    LIBXL_LIST_INIT(&ctx->efds);
-    LIBXL_TAILQ_INIT(&ctx->etimes);
-
-    ctx->watch_slots = 0;
-    LIBXL_SLIST_INIT(&ctx->watch_freeslots);
-    libxl__ev_fd_init(&ctx->watch_efd);
-
-    ctx->xce = 0;
-    LIBXL_LIST_INIT(&ctx->evtchns_waiting);
-    libxl__ev_fd_init(&ctx->evtchn_efd);
-
-    LIBXL_LIST_INIT(&ctx->aos_inprogress);
-
-    LIBXL_TAILQ_INIT(&ctx->death_list);
-    libxl__ev_xswatch_init(&ctx->death_watch);
-
-    ctx->childproc_hooks = &libxl__childproc_default_hooks;
-    ctx->childproc_user = 0;
-
-    ctx->sigchld_selfpipe[0] = -1;
-    ctx->sigchld_selfpipe[1] = -1;
-    libxl__ev_fd_init(&ctx->sigchld_selfpipe_efd);
-
-    /* The mutex is special because we can't idempotently destroy it */
-
-    if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
-        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Failed to initialize mutex");
-        free(ctx);
-        ctx = 0;
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /* Now ctx is safe for ctx_free; failures simply set rc and "goto out" */
-    LIBXL_INIT_GC(gc_buf,ctx);
-    gc = &gc_buf;
-    /* Now gc is useable */
-
-    rc = libxl__atfork_init(ctx);
-    if (rc) goto out;
-
-    ctx->poller_app = libxl__poller_get(gc);
-    if (!ctx->poller_app) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    ctx->xch = xc_interface_open(lg,lg,0);
-    if (!ctx->xch) {
-        LOGEV(ERROR, errno, "cannot open libxc handle");
-        rc = ERROR_FAIL; goto out;
-    }
-
-    ctx->xsh = xs_daemon_open();
-    if (!ctx->xsh)
-        ctx->xsh = xs_domain_open();
-    if (!ctx->xsh) {
-        LOGEV(ERROR, errno, "cannot connect to xenstore");
-        rc = ERROR_FAIL; goto out;
-    }
-
-    *pctx = ctx;
-    return 0;
-
- out:
-    if (gc) libxl__free_all(gc);
-    libxl_ctx_free(ctx);
-    *pctx = NULL;
-    return rc;
-}
-
-static void free_disable_deaths(libxl__gc *gc,
-                                struct libxl__evgen_domain_death_list *l) {
-    libxl_evgen_domain_death *death;
-    while ((death = LIBXL_TAILQ_FIRST(l)))
-        libxl__evdisable_domain_death(gc, death);
-}
-
-static void discard_events(struct libxl__event_list *l) {
-    /* doesn't bother unlinking from the list, so l is corrupt on return */
-    libxl_event *ev, *next;
-    LIBXL_TAILQ_FOREACH_SAFE(ev, l, link, next)
-        libxl_event_free(0, ev);
-}
-
-int libxl_ctx_free(libxl_ctx *ctx)
-{
-    if (!ctx) return 0;
-
-    int i;
-    GC_INIT(ctx);
-
-    CTX_LOCK;
-    assert(!ctx->osevent_in_hook);
-    CTX->osevent_in_hook += 1000; /* make violations easier to debug */
-
-    /* Deregister all libxl__ev_KINDs: */
-
-    free_disable_deaths(gc, &CTX->death_list);
-    free_disable_deaths(gc, &CTX->death_reported);
-
-    libxl_evgen_disk_eject *eject;
-    while ((eject = LIBXL_LIST_FIRST(&CTX->disk_eject_evgens)))
-        libxl__evdisable_disk_eject(gc, eject);
-
-    libxl_childproc_setmode(CTX,0,0);
-    for (i = 0; i < ctx->watch_nslots; i++)
-        assert(!libxl__watch_slot_contents(gc, i));
-    assert(!libxl__ev_fd_isregistered(&ctx->watch_efd));
-    assert(!libxl__ev_fd_isregistered(&ctx->evtchn_efd));
-    assert(!libxl__ev_fd_isregistered(&ctx->sigchld_selfpipe_efd));
-
-    /* Now there should be no more events requested from the application: */
-
-    assert(LIBXL_LIST_EMPTY(&ctx->efds));
-    assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
-    assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting));
-    assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress));
-
-    if (ctx->xch) xc_interface_close(ctx->xch);
-    libxl_version_info_dispose(&ctx->version_info);
-    if (ctx->xsh) xs_daemon_close(ctx->xsh);
-    if (ctx->xce) xenevtchn_close(ctx->xce);
-
-    libxl__poller_put(ctx, ctx->poller_app);
-    ctx->poller_app = NULL;
-    assert(LIBXL_LIST_EMPTY(&ctx->pollers_event));
-    assert(LIBXL_LIST_EMPTY(&ctx->pollers_active));
-    libxl__poller *poller, *poller_tmp;
-    LIBXL_LIST_FOREACH_SAFE(poller, &ctx->pollers_idle, entry, poller_tmp) {
-        libxl__poller_dispose(poller);
-        free(poller);
-    }
-
-    free(ctx->watch_slots);
-
-    discard_events(&ctx->occurred);
-
-    /* If we have outstanding children, then the application inherits
-     * them; we wish the application good luck with understanding
-     * this if and when it reaps them. */
-    libxl__sigchld_notneeded(gc);
-    libxl__pipe_close(ctx->sigchld_selfpipe);
-
-    CTX_UNLOCK;
-    pthread_mutex_destroy(&ctx->lock);
-
-    GC_FREE;
-    free(ctx);
-    return 0;
-}
-
-void libxl_string_list_dispose(libxl_string_list *psl)
-{
-    int i;
-    libxl_string_list sl = *psl;
-
-    if (!sl)
-        return;
-
-    for (i = 0; sl[i] != NULL; i++) {
-        free(sl[i]);
-        sl[i] = NULL;
-    }
-    free(sl);
-    *psl = NULL;
-}
-
-void libxl_string_list_copy(libxl_ctx *ctx,
-                            libxl_string_list *dst,
-                            const libxl_string_list *src)
-{
-    GC_INIT(ctx);
-    int i, len;
-
-    if (!*src) {
-        *dst = NULL;
-        goto out;
-    }
-
-    len = libxl_string_list_length(src);
-    /* one extra slot for sentinel */
-    *dst = libxl__calloc(NOGC, len + 1, sizeof(char *));
-
-    for (i = 0; i < len; i++)
-        (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
-
-out:
-    GC_FREE;
-}
-
-int libxl_string_list_length(const libxl_string_list *psl)
-{
-    int i = 0;
-
-    if (*psl)
-        while ((*psl)[i])
-            i++;
-
-    return i;
-}
-
-int libxl_key_value_list_length(const libxl_key_value_list *pkvl)
-{
-    int i = 0;
-    libxl_key_value_list kvl = *pkvl;
-
-    if (kvl) {
-        while (kvl[2 * i]) /* Only checks keys */
-            i++;
-    }
-
-    return i;
-}
-
-void libxl_key_value_list_dispose(libxl_key_value_list *pkvl)
-{
-    int i;
-    libxl_key_value_list kvl = *pkvl;
-
-    if (!kvl)
-        return;
-
-    for (i = 0; kvl[i] != NULL; i += 2) {
-        free(kvl[i]);
-        kvl[i] = NULL;
-        if (kvl[i + 1]) {
-            free(kvl[i + 1]);
-            kvl[i+1] = NULL;
-        }
-    }
-    free(kvl);
-    *pkvl = NULL;
-}
-
-void libxl_key_value_list_copy(libxl_ctx *ctx,
-                               libxl_key_value_list *dst,
-                               const libxl_key_value_list *src)
-{
-    GC_INIT(ctx);
-    int i, len;
-
-    if (*src == NULL) {
-        *dst = NULL;
-        goto out;
-    }
-
-    len = libxl_key_value_list_length(src);
-    /* one extra slot for sentinel */
-    *dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *));
-
-    for (i = 0; i < len * 2; i += 2) {
-        (*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
-        if ((*src)[i+1])
-            (*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]);
-        else
-            (*dst)[i+1] = NULL;
-    }
-
-out:
-    GC_FREE;
-}
-
-void libxl_defbool_set(libxl_defbool *db, bool b)
-{
-    db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE;
-}
-
-void libxl_defbool_unset(libxl_defbool *db)
-{
-    db->val = LIBXL__DEFBOOL_DEFAULT;
-}
-
-bool libxl_defbool_is_default(libxl_defbool db)
-{
-    return !db.val;
-}
-
-void libxl_defbool_setdefault(libxl_defbool *db, bool b)
-{
-    if (libxl_defbool_is_default(*db))
-        libxl_defbool_set(db, b);
-}
-
-bool libxl_defbool_val(libxl_defbool db)
-{
-    assert(!libxl_defbool_is_default(db));
-    return db.val > 0;
-}
-
-const char *libxl_defbool_to_string(libxl_defbool b)
-{
-    if (b.val < 0)
-        return LIBXL__DEFBOOL_STR_FALSE;
-    else if (b.val > 0)
-        return LIBXL__DEFBOOL_STR_TRUE;
-    else
-        return LIBXL__DEFBOOL_STR_DEFAULT;
-}
-
-/******************************************************************************/
-int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo)
-{
-    xc_physinfo_t xcphysinfo = { 0 };
-    int rc;
-    long l;
-    GC_INIT(ctx);
-
-    rc = xc_physinfo(ctx->xch, &xcphysinfo);
-    if (rc != 0) {
-        LOGE(ERROR, "getting physinfo");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    physinfo->threads_per_core = xcphysinfo.threads_per_core;
-    physinfo->cores_per_socket = xcphysinfo.cores_per_socket;
-    physinfo->max_cpu_id = xcphysinfo.max_cpu_id;
-    physinfo->nr_cpus = xcphysinfo.nr_cpus;
-    physinfo->cpu_khz = xcphysinfo.cpu_khz;
-    physinfo->total_pages = xcphysinfo.total_pages;
-    physinfo->free_pages = xcphysinfo.free_pages;
-    physinfo->scrub_pages = xcphysinfo.scrub_pages;
-    physinfo->outstanding_pages = xcphysinfo.outstanding_pages;
-    physinfo->max_possible_mfn = xcphysinfo.max_mfn;
-    l = xc_sharing_freed_pages(ctx->xch);
-    if (l < 0 && errno == ENOSYS) {
-        l = 0;
-    } else if (l < 0) {
-        LOGEV(ERROR, l, "getting sharing freed pages");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    physinfo->sharing_freed_pages = l;
-    l = xc_sharing_used_frames(ctx->xch);
-    if (l < 0 && errno == ENOSYS) {
-        l = 0;
-    } else if (l < 0) {
-        LOGEV(ERROR, l, "getting sharing used frames");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    physinfo->sharing_used_frames = l;
-    physinfo->nr_nodes = xcphysinfo.nr_nodes;
-    memcpy(physinfo->hw_cap,xcphysinfo.hw_cap, sizeof(physinfo->hw_cap));
-
-    physinfo->cap_hvm = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm);
-    physinfo->cap_pv = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_pv);
-    physinfo->cap_hvm_directio =
-        !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_directio);
-    physinfo->cap_hap = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hap);
-    physinfo->cap_shadow =
-        !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_shadow);
-    physinfo->cap_iommu_hap_pt_share =
-        !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_iommu_hap_pt_share);
-
-    GC_FREE;
-    return 0;
-}
-
-libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out)
-{
-    GC_INIT(ctx);
-    xc_cputopo_t *cputopo;
-    libxl_cputopology *ret = NULL;
-    int i;
-    unsigned num_cpus = 0;
-
-    /* Setting buffer to NULL makes the call return number of CPUs */
-    if (xc_cputopoinfo(ctx->xch, &num_cpus, NULL))
-    {
-        LOGE(ERROR, "Unable to determine number of CPUS");
-        goto out;
-    }
-
-    cputopo = libxl__zalloc(gc, sizeof(*cputopo) * num_cpus);
-
-    if (xc_cputopoinfo(ctx->xch, &num_cpus, cputopo)) {
-        LOGE(ERROR, "CPU topology info hypercall failed");
-        goto out;
-    }
-
-    ret = libxl__zalloc(NOGC, sizeof(libxl_cputopology) * num_cpus);
-
-    for (i = 0; i < num_cpus; i++) {
-#define V(map, i, invalid) ( cputopo[i].map == invalid) ? \
-   LIBXL_CPUTOPOLOGY_INVALID_ENTRY : cputopo[i].map
-        ret[i].core = V(core, i, XEN_INVALID_CORE_ID);
-        ret[i].socket = V(socket, i, XEN_INVALID_SOCKET_ID);
-        ret[i].node = V(node, i, XEN_INVALID_NODE_ID);
-#undef V
-    }
-
-    *nb_cpu_out = num_cpus;
-
- out:
-    GC_FREE;
-    return ret;
-}
-
-libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs)
-{
-    GC_INIT(ctx);
-    physdev_pci_device_t *devs;
-    uint32_t *nodes;
-    libxl_pcitopology *ret = NULL;
-    int i, rc;
-
-    *num_devs = libxl__pci_numdevs(gc);
-    if (*num_devs < 0) {
-        LOG(ERROR, "Unable to determine number of PCI devices, rc %d",
-            *num_devs);
-        goto out;
-    }
-
-    devs = libxl__zalloc(gc, sizeof(*devs) * *num_devs);
-    nodes = libxl__zalloc(gc, sizeof(*nodes) * *num_devs);
-
-    rc = libxl__pci_topology_init(gc, devs, *num_devs);
-    if (rc) {
-        LOG(ERROR, "Cannot initialize PCI hypercall structure, rc %d", rc);
-        goto out;
-    }
-
-    if (xc_pcitopoinfo(ctx->xch, *num_devs, devs, nodes) != 0) {
-        LOGE(ERROR, "PCI topology info hypercall failed");
-        goto out;
-    }
-
-    ret = libxl__zalloc(NOGC, sizeof(libxl_pcitopology) * *num_devs);
-
-    for (i = 0; i < *num_devs; i++) {
-        ret[i].seg = devs[i].seg;
-        ret[i].bus = devs[i].bus;
-        ret[i].devfn = devs[i].devfn;
-        ret[i].node = ((nodes[i] == XEN_INVALID_NODE_ID) ||
-                       (nodes[i] == XEN_INVALID_DEV)) ?
-            LIBXL_PCITOPOLOGY_INVALID_ENTRY : nodes[i];
-    }
-
- out:
-    GC_FREE;
-    return ret;
-}
-
-libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr)
-{
-    GC_INIT(ctx);
-    xc_meminfo_t *meminfo;
-    uint32_t *distance;
-    libxl_numainfo *ret = NULL;
-    int i, j;
-    unsigned num_nodes = 0;
-
-    if (xc_numainfo(ctx->xch, &num_nodes, NULL, NULL)) {
-        LOGE(ERROR, "Unable to determine number of nodes");
-        goto out;
-    }
-
-    meminfo = libxl__zalloc(gc, sizeof(*meminfo) * num_nodes);
-    distance = libxl__zalloc(gc, sizeof(*distance) * num_nodes * num_nodes);
-
-    if (xc_numainfo(ctx->xch, &num_nodes, meminfo, distance)) {
-        LOGE(ERROR, "getting numainfo");
-        goto out;
-    }
-
-    *nr = num_nodes;
-
-    ret = libxl__zalloc(NOGC, sizeof(libxl_numainfo) * num_nodes);
-    for (i = 0; i < num_nodes; i++)
-        ret[i].dists = libxl__calloc(NOGC, num_nodes, sizeof(*distance));
-
-    for (i = 0; i < num_nodes; i++) {
-#define V(val, invalid) (val == invalid) ? \
-       LIBXL_NUMAINFO_INVALID_ENTRY : val
-        ret[i].size = V(meminfo[i].memsize, XEN_INVALID_MEM_SZ);
-        ret[i].free = V(meminfo[i].memfree, XEN_INVALID_MEM_SZ);
-        ret[i].num_dists = num_nodes;
-        for (j = 0; j < ret[i].num_dists; j++) {
-            unsigned idx = i * num_nodes + j;
-            ret[i].dists[j] = V(distance[idx], XEN_INVALID_NODE_DIST);
-        }
-#undef V
-    }
-
- out:
-    GC_FREE;
-    return ret;
-}
-
-static int libxl__xc_version_wrap(libxl__gc *gc, libxl_version_info *info,
-                                  xen_build_id_t *build)
-{
-    int r;
-
-    r = xc_version(CTX->xch, XENVER_build_id, build);
-    switch (r) {
-    case -EPERM:
-    case -ENODATA:
-    case 0:
-        info->build_id = libxl__strdup(NOGC, "");
-        break;
-
-    case -ENOBUFS:
-        break;
-
-    default:
-        if (r > 0) {
-            unsigned int i;
-
-            info->build_id = libxl__zalloc(NOGC, (r * 2) + 1);
-
-            for (i = 0; i < r ; i++)
-                snprintf(&info->build_id[i * 2], 3, "%02hhx", build->buf[i]);
-
-            r = 0;
-        }
-        break;
-    }
-    return r;
-}
-
-const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx)
-{
-    GC_INIT(ctx);
-    union {
-        xen_extraversion_t xen_extra;
-        xen_compile_info_t xen_cc;
-        xen_changeset_info_t xen_chgset;
-        xen_capabilities_info_t xen_caps;
-        xen_platform_parameters_t p_parms;
-        xen_commandline_t xen_commandline;
-        xen_build_id_t build_id;
-    } u;
-    long xen_version;
-    int r;
-    libxl_version_info *info = &ctx->version_info;
-
-    if (info->xen_version_extra != NULL)
-        goto out;
-
-    xen_version = xc_version(ctx->xch, XENVER_version, NULL);
-    info->xen_version_major = xen_version >> 16;
-    info->xen_version_minor = xen_version & 0xFF;
-
-    xc_version(ctx->xch, XENVER_extraversion, &u.xen_extra);
-    info->xen_version_extra = libxl__strdup(NOGC, u.xen_extra);
-
-    xc_version(ctx->xch, XENVER_compile_info, &u.xen_cc);
-    info->compiler = libxl__strdup(NOGC, u.xen_cc.compiler);
-    info->compile_by = libxl__strdup(NOGC, u.xen_cc.compile_by);
-    info->compile_domain = libxl__strdup(NOGC, u.xen_cc.compile_domain);
-    info->compile_date = libxl__strdup(NOGC, u.xen_cc.compile_date);
-
-    xc_version(ctx->xch, XENVER_capabilities, &u.xen_caps);
-    info->capabilities = libxl__strdup(NOGC, u.xen_caps);
-
-    xc_version(ctx->xch, XENVER_changeset, &u.xen_chgset);
-    info->changeset = libxl__strdup(NOGC, u.xen_chgset);
-
-    xc_version(ctx->xch, XENVER_platform_parameters, &u.p_parms);
-    info->virt_start = u.p_parms.virt_start;
-
-    info->pagesize = xc_version(ctx->xch, XENVER_pagesize, NULL);
-
-    xc_version(ctx->xch, XENVER_commandline, &u.xen_commandline);
-    info->commandline = libxl__strdup(NOGC, u.xen_commandline);
-
-    u.build_id.len = sizeof(u) - sizeof(u.build_id);
-    r = libxl__xc_version_wrap(gc, info, &u.build_id);
-    if (r == -ENOBUFS) {
-            xen_build_id_t *build_id;
-
-            build_id = libxl__zalloc(gc, info->pagesize);
-            build_id->len = info->pagesize - sizeof(*build_id);
-            r = libxl__xc_version_wrap(gc, info, build_id);
-            if (r) LOGEV(ERROR, r, "getting build_id");
-    }
- out:
-    GC_FREE;
-    return info;
-}
-
-int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq)
-{
-    GC_INIT(ctx);
-    char *dompath = libxl__xs_get_dompath(gc, domid);
-
-    libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/control/sysrq", dompath),
-                     "%c", sysrq);
-
-    GC_FREE;
-    return 0;
-}
-
-int libxl_send_debug_keys(libxl_ctx *ctx, char *keys)
-{
-    int ret;
-    GC_INIT(ctx);
-    ret = xc_send_debug_keys(ctx->xch, keys);
-    if ( ret < 0 ) {
-        LOGE(ERROR, "sending debug keys");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    GC_FREE;
-    return 0;
-}
-
-int libxl_set_parameters(libxl_ctx *ctx, char *params)
-{
-    int ret;
-    GC_INIT(ctx);
-    char *par, *val, *end, *path;
-    xenhypfs_handle *hypfs;
-
-    hypfs = xenhypfs_open(ctx->lg, 0);
-    if (!hypfs) {
-        LOGE(ERROR, "opening Xen hypfs");
-        ret = ERROR_FAIL;
-        goto out;
-    }
-
-    while (isblank(*params))
-        params++;
-
-    for (par = params; *par; par = end) {
-        end = strchr(par, ' ');
-        if (!end)
-            end = par + strlen(par);
-
-        val = strchr(par, '=');
-        if (val > end)
-            val = NULL;
-        if (!val && !strncmp(par, "no", 2)) {
-            path = libxl__sprintf(gc, "/params/%s", par + 2);
-            path[end - par - 2 + 8] = 0;
-            val = "no";
-            par += 2;
-        } else {
-            path = libxl__sprintf(gc, "/params/%s", par);
-            path[val - par + 8] = 0;
-            val = libxl__strndup(gc, val + 1, end - val - 1);
-        }
-
-       LOG(DEBUG, "setting node \"%s\" to value \"%s\"", path, val);
-        ret = xenhypfs_write(hypfs, path, val);
-        if (ret < 0) {
-            LOGE(ERROR, "setting parameters");
-            ret = ERROR_FAIL;
-            goto out;
-        }
-
-        while (isblank(*end))
-            end++;
-    }
-
-    ret = 0;
-
-out:
-    xenhypfs_close(hypfs);
-    GC_FREE;
-    return ret;
-}
-
-static int fd_set_flags(libxl_ctx *ctx, int fd,
-                        int fcntlgetop, int fcntlsetop, const char *fl,
-                        int flagmask, int set_p)
-{
-    int flags, r;
-    GC_INIT(ctx);
-
-    flags = fcntl(fd, fcntlgetop);
-    if (flags == -1) {
-        LOGE(ERROR, "fcntl(,F_GET%s) failed", fl);
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-
-    if (set_p)
-        flags |= flagmask;
-    else
-        flags &= ~flagmask;
-
-    r = fcntl(fd, fcntlsetop, flags);
-    if (r == -1) {
-        LOGE(ERROR, "fcntl(,F_SET%s) failed", fl);
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return 0;
-}
-
-int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec)
-  { return fd_set_flags(ctx,fd, F_GETFD,F_SETFD,"FD", FD_CLOEXEC, cloexec); }
-
-int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock)
-  { return fd_set_flags(ctx,fd, F_GETFL,F_SETFL,"FL", O_NONBLOCK, nonblock); }
-
-int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
-                                int mask, int val, int *r_oldflags)
-{
-    int rc, ret, fdfl;
-
-    fdfl = fcntl(fd, F_GETFL);
-    if (fdfl < 0) {
-        LOGE(ERROR, "failed to fcntl.F_GETFL for fd %d", fd);
-        rc = ERROR_FAIL;
-        goto out_err;
-    }
-
-    LOG(DEBUG, "fnctl F_GETFL flags for fd %d are 0x%x", fd, fdfl);
-
-    if (r_oldflags)
-        *r_oldflags = fdfl;
-
-    fdfl &= mask;
-    fdfl |= val;
-
-    LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
-
-    ret = fcntl(fd, F_SETFL, fdfl);
-    if (ret < 0) {
-        LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
-        rc = ERROR_FAIL;
-        goto out_err;
-    }
-
-    rc = 0;
-
-out_err:
-    return rc;
-}
-
-int libxl__fd_flags_restore(libxl__gc *gc, int fd, int fdfl)
-{
-    int ret, rc;
-
-    LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
-
-    ret = fcntl(fd, F_SETFL, fdfl);
-    if (ret < 0) {
-        LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
-        rc = ERROR_FAIL;
-        goto out_err;
-    }
-
-    rc = 0;
-
-out_err:
-    return rc;
-
-}
-
-void libxl_hwcap_copy(libxl_ctx *ctx,libxl_hwcap *dst, const libxl_hwcap *src)
-{
-    int i;
-
-    for (i = 0; i < 8; i++)
-        (*dst)[i] = (*src)[i];
-}
-
-void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src)
-{
-    int i;
-
-    for (i = 0; i < 6; i++)
-        (*dst)[i] = (*src)[i];
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
deleted file mode 100644 (file)
index 1ea5b4f..0000000
+++ /dev/null
@@ -1,2732 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-/*
- * libxl API compatibility
- *
- * From Xen 4.2 onwards the API of libxl will be maintained in a
- * stable manner. This means that it should be possible to write an
- * application against the API provided by libxl in Xen 4.2 and expect
- * that it will continue to compile against future versions of Xen
- * without source modification.
- *
- * In order to make such compatibility possible it is required that
- * application which want to be exposed to a particular API #define
- * LIBXL_API_VERSION before including libxl.h or any other libxl
- * header. The syntax of the LIBXL_API_VERSION is:
- *    0xVVSSEE
- * where ($(XEN_xxx) from xen/Makefile):
- *   VV is the Xen major release number, $(XEN_VERSION)
- *   SS is the Xen sub version number, $(XEN_SUBVERSION)
- *   EE is the Xen extra version digit, first numeric part of
- *     $(XEN_EXTRAVERSION) not including the leading "."
- * For example the first stable API version, supported by Xen 4.2.0,
- * is 0x040200.
- *
- * Lack of LIBXL_API_VERSION means "the latest" which will
- * change. Specifying an unknown LIBXL_API_VERSION will result in a
- * compile time error.
- *
- * Identical versions of the libxl API will represented by the version
- * containing the earliest instance of that API. e.g. if 4.2.0 and
- * 4.3.0 contain an identical libxl API then only LIBXL_API_VERSION
- * 0x040200 will be valid.
- *
- * We will try especially hard to avoid changing the API during a
- * stable series, i.e. it should be unusual for the last byte of
- * LIBXL_API_VERSION to be non-zero.
- *
- * In the event that a change is required which cannot be made
- * backwards compatible in this manner a #define of the form
- * LIBXL_HAVE_<interface> will always be added in order to make it
- * possible to write applications which build against any version of
- * libxl. Such changes are expected to be exceptional and used as a
- * last resort. The barrier for backporting such a change to a stable
- * branch will be very high.
- *
- * These guarantees apply only to stable releases of Xen. When an
- * incompatible change is made in the unstable tree then
- * LIBXL_API_VERSION will be bumped to the next expected stable
- * release number on the first such change only. Applications which
- * want to support building against Xen unstable are expected to track
- * API changes in that tree until it is released as a stable release.
- *
- * API compatibility will be maintained for all versions of Xen using
- * the same $(XEN_VERSION) (e.g. throughout a major release).
- */
-
-/* LIBXL_HAVE_PHYSINFO_CAP_PV
- *
- * If this is defined, libxl_physinfo has a "cap_pv" field.
- */
-#define LIBXL_HAVE_PHYSINFO_CAP_PV 1
-
-/* LIBXL_HAVE_CONSOLE_NOTIFY_FD
- *
- * If this is defined, libxl_console_exec and
- * libxl_primary_console_exe take a notify_fd parameter. That
- * parameter will be used to notify the caller that the console is connected.
- */
-#define LIBXL_HAVE_CONSOLE_NOTIFY_FD 1
-
-/* LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS
- *
- * If this is defined, the copy functions have constified src parameter and the
- * length functions accept constified parameter.
- */
-#define LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS 1
-
-/* LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO
- *
- * If this is defined, libxl_domain_need_memory no longer modifies
- * the b_info paseed in.
- */
-#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO 1
-
-/* LIBXL_HAVE_VNUMA
- *
- * If this is defined the type libxl_vnode_info exists, and a
- * field 'vnuma_nodes' is present in libxl_domain_build_info.
- */
-#define LIBXL_HAVE_VNUMA 1
-
-/* LIBXL_HAVE_USERDATA_UNLINK
- *
- * If it is defined, libxl has a library function called
- * libxl_userdata_unlink.
- */
-#define LIBXL_HAVE_USERDATA_UNLINK 1
-
-/* LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID
- *
- * If this is defined, libxl has a library function called
- * libxl_cpupool_qualifier_to_cpupoolid, which takes in a CPU pool
- * qualifier in the form of number or string, then returns the ID of
- * that CPU pool.
- */
-#define LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID 1
-
-/* LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP
- *
- * If this is defined, libxl has two library functions called
- * libxl_cpupool_cpuadd_cpumap and libxl_cpupool_cpuremove_cpumap,
- * which allow to add to or remove from a cpupool all the cpus
- * specified in a bitmap.
- */
-#define LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP 1
-
-/*
- *
- * LIBXL_HAVE_BITMAP_AND_OR
- *
- * If this is defined, libxl has two library functions, libxl_bitmap_and
- * and libxl_bitmap_or to compute the logical and and or of two bitmaps
- */
-#define LIBXL_HAVE_BITMAP_AND_OR 1
-
-/*
- * LIBXL_HAVE_FIRMWARE_PASSTHROUGH indicates the feature for
- * passing in SMBIOS and ACPI firmware to HVM guests is present
- * in the library.
- */
-#define LIBXL_HAVE_FIRMWARE_PASSTHROUGH 1
-
-/*
- * LIBXL_HAVE_DOMAIN_NODEAFFINITY indicates that a 'nodemap' field
- * (of libxl_bitmap type) is present in libxl_domain_build_info,
- * containing the node-affinity for the domain.
- */
-#define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1
-
-/*
- * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices
- * through pvusb -- both hotplug and at domain creation time..
- */
-#define LIBXL_HAVE_PVUSB 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the
- * libxl_vendor_device field is present in the hvm sections of
- * libxl_domain_build_info. This field tells libxl which
- * flavour of xen-pvdevice to enable in QEMU.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE 1
-
-/*
- * The libxl_domain_build_info has the event_channels field.
- */
-#define LIBXL_HAVE_BUILDINFO_EVENT_CHANNELS 1
-
-/*
- * libxl_domain_build_info has the u.hvm.ms_vm_genid field.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_MS_VM_GENID 1
-
-/*
- * LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY indicates that a 'cpumap_soft'
- * field (of libxl_bitmap type) is present in libxl_vcpuinfo,
- * containing the soft affinity of a vcpu.
- */
-#define LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY 1
-
-/*
- * LIBXL_HAVE_SET_VCPUAFFINITY_FORCE indicates that the
- * libxl_set_vcpuaffinity_force() library call is available.
- */
-#define LIBXL_HAVE_SET_VCPUAFFINITY_FORCE 1
-
-/*
- * LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE indicates that a
- * 'direct_io_safe' field (of boolean type) is present in
- * libxl_device_disk.
- */
-#define LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE 1
-
-/*
- * The libxl_device_disk has the discard_enable field.
- */
-#define LIBXL_HAVE_LIBXL_DEVICE_DISK_DISCARD_ENABLE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN indicates that it is possible
- * to specify the start guest frame number used to map a range of I/O
- * memory machine frame numbers via the 'gfn' field (of type uint64)
- * of the 'iomem' structure. An array of iomem structures is embedded
- * in libxl_domain_build_info and used to map the indicated memory
- * ranges during domain build.
- */
-#define LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN 1
-
-/*
- * LIBXL_HAVE_SCHED_RTDS indicates that the RTDS real time scheduler
- * is available. A 'budget' field added in libxl_domain_sched_params.
- */
-#define LIBXL_HAVE_SCHED_RTDS 1
-
-/*
- * LIBXL_HAVE_SCHED_NULL indicates that the 'null' static scheduler
- * is available.
- */
-#define LIBXL_HAVE_SCHED_NULL 1
-
-/*
- * libxl_domain_build_info has u.hvm.viridian_enable and _disable bitmaps
- * of the specified width.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE 1
-#define LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH 64
-
-/*
- * libxl_domain_build_info has the u.hvm.mmio_hole_memkb field.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_MMIO_HOLE_MEMKB 1
-
-/*
- * libxl_domain_info returns ERROR_DOMAIN_NOTFOUND if the domain
- * is not present, instead of ERROR_INVAL.
- */
-#define LIBXL_HAVE_ERROR_DOMAIN_NOTFOUND 1
-
-/*
- * libxl_domain_build_info has device_tree and libxl_device_dtdev
- * exists. This mean Device Tree passthrough is supported for ARM
- */
-#define LIBXL_HAVE_DEVICETREE_PASSTHROUGH 1
-
-/*
- * libxl_domain_build_info has device_model_user to specify the user to
- * run the device model with. See docs/misc/qemu-deprivilege.txt.
- */
-#define LIBXL_HAVE_DEVICE_MODEL_USER 1
-
-/*
- * libxl_vcpu_sched_params is used to store per-vcpu params.
- */
-#define LIBXL_HAVE_VCPU_SCHED_PARAMS 1
-
-/*
- * LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS indicates RTDS scheduler
- * now supports per-vcpu settings.
- */
-#define LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS 1
-
-/*
- * LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA indicates RTDS scheduler
- * now supports per-vcpu extratime settings.
- */
-#define LIBXL_HAVE_SCHED_RTDS_VCPU_EXTRA 1
-
-/*
- * libxl_domain_build_info has the arm.gic_version field.
- */
-#define LIBXL_HAVE_BUILDINFO_ARM_GIC_VERSION 1
-
-/*
- * libxl_domain_build_info has the arch_arm.tee field.
- */
-#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_TEE 1
-
-/*
- * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
- * 'soft reset' for domains and there is 'soft_reset' shutdown reason
- * in enum libxl_shutdown_reason.
- */
-#define LIBXL_HAVE_SOFT_RESET 1
-
-/*
- * LIBXL_HAVE_APIC_ASSIST indicates that the 'apic_assist' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_APIC_ASSIST 1
-
-/*
- * LIBXL_HAVE_BUILD_ID means that libxl_version_info has the extra
- * field for the hypervisor build_id.
- */
-#define LIBXL_HAVE_BUILD_ID 1
-
-/*
- * LIBXL_HAVE_QEMU_MONITOR_COMMAND indiactes the availability of the
- * libxl_qemu_monitor_command() function.
- */
-#define LIBXL_HAVE_QEMU_MONITOR_COMMAND 1
-
-/*
- * LIBXL_HAVE_SCHED_CREDIT2_PARAMS indicates the existance of a
- * libxl_sched_credit2_params structure, containing Credit2 scheduler
- * wide parameters (i.e., the ratelimiting value).
- */
-#define LIBXL_HAVE_SCHED_CREDIT2_PARAMS 1
-
-/*
- * LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY indicates that there is a field
- * in libxl_sched_credit_params called vcpu_migr_delay_us which controls
- * the resistance of the vCPUs of the cpupool to migrations among pCPUs.
- */
-#define LIBXL_HAVE_SCHED_CREDIT_MIGR_DELAY
-
-/*
- * LIBXL_HAVE_VIRIDIAN_CRASH_CTL indicates that the 'crash_ctl' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_CRASH_CTL 1
-
-/*
- * LIBXL_HAVE_VIRIDIAN_SYNIC indicates that the 'synic' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_SYNIC 1
-
-/*
- * LIBXL_HAVE_VIRIDIAN_STIMER indicates that the 'stimer' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_STIMER 1
-
-/*
- * LIBXL_HAVE_VIRIDIAN_HCALL_IPI indicates that the 'hcall_ipi' value
- * is present in the viridian enlightenment enumeration.
- */
-#define LIBXL_HAVE_VIRIDIAN_HCALL_IPI 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that
- * libxl_domain_build_info has the u.hvm.acpi_laptop_slate field.
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE 1
-
-/*
- * LIBXL_HAVE_P9S indicates that the p9 field in IDL has been changed to p9s
- */
-#define LIBXL_HAVE_P9S 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_ARM_VUART indicates that the toolstack supports virtual UART
- * for ARM.
- */
-#define LIBXL_HAVE_BUILDINFO_ARM_VUART 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_GRANT_LIMITS indicates that libxl_domain_build_info
- * has the max_grant_frames and max_maptrack_frames fields.
- */
-#define LIBXL_HAVE_BUILDINFO_GRANT_LIMITS 1
-
-#define LIBXL_MAX_GRANT_DEFAULT (~(uint32_t)0)
-#define LIBXL_MAX_GRANT_FRAMES_DEFAULT 32 /* deprecated */
-#define LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT 1024 /* deprecated */
-/*
- * LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT indicates that the default
- * values of max_grant_frames and max_maptrack_frames fields in
- * libxl_domain_build_info are the special sentinel value
- * LIBXL_MAX_GRANT_DEFAULT rather than the fixed values above.
- * This means to use the hypervisor's default.
- */
-#define LIBXL_HAVE_BUILDINFO_GRANT_DEFAULT 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_* indicates that libxl_domain_build_info has
- * the field represented by the '*'. The original position of those
- * fields is:
- *  - u.hvm.timer_mode
- *  - u.hvm.apic
- *  - u.hvm.nested_hvm
- *  - u.pv.bootloader
- *  - u.pv.bootloader_args
- */
-#define LIBXL_HAVE_BUILDINFO_TIMER_MODE 1
-#define LIBXL_HAVE_BUILDINFO_APIC 1
-#define LIBXL_HAVE_BUILDINFO_NESTED_HVM 1
-#define LIBXL_HAVE_BUILDINFO_BOOTLOADER 1
-#define LIBXL_HAVE_BUILDINFO_BOOTLOADER_ARGS 1
-
-/*
- * LIBXL_HAVE_EXTENDED_VKB indicates that libxl_device_vkb has extended fields:
- *  - unique_id;
- *  - feature_disable_keyboard;
- *  - feature_disable_pointer;
- *  - feature_abs_pointer;
- *  - feature_raw_pointer;
- *  - feature_multi_touch;
- *  - width;
- *  - height;
- *  - multi_touch_width;
- *  - multi_touch_height;
- *  - multi_touch_num_contacts.
- */
-#define LIBXL_HAVE_EXTENDED_VKB 1
-
-/*
- * LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW indicates that libxl_physinfo has
- * cap_hap and cap_shadow fields reflecting the hardware and Xen availability
- * of Hardware Assisted, and Shadow paging support.
- */
-#define LIBXL_HAVE_PHYSINFO_CAP_HAP_SHADOW 1
-
-/*
- * LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE indicates that libxl_physinfo
- * has a cap_iommu_hap_pt_share field that indicates whether the hardware
- * supports sharing the IOMMU and HAP page tables.
- */
-#define LIBXL_HAVE_PHYSINFO_CAP_IOMMU_HAP_PT_SHARE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB indicates thate libxl_domain_build_info
- * has an iommu_memkb field which should be set with the amount of memory
- * overhead needed by the domain for populating IOMMU page tables.
- */
-#define LIBXL_HAVE_BUILDINFO_IOMMU_MEMKB 1
-
-/*
- * LIBXL_HAVE_CREATEINFO_PASSTHROUGH indicates that
- * libxl_domain_create_info has a passthrough field (which is a
- * libxl_passthrough enumeration) that indicates whether device pass-
- * through is enabled for the domain and, if so, whether the IOMMU and
- * HAP page tables may be shared or not.
- */
-#define LIBXL_HAVE_CREATEINFO_PASSTHROUGH 1
-
-/*
- * LIBXL_HAVE_DISK_SAFE_REMOVE indicates that the
- * libxl_device_disk_safe_remove() function is defined.
- */
-#define LIBXL_HAVE_DISK_SAFE_REMOVE 1
-
-/*
- * libxl ABI compatibility
- *
- * The only guarantee which libxl makes regarding ABI compatibility
- * across releases is that the SONAME will always be bumped whenever
- * the ABI is changed in an incompatible way.
- *
- * This applies within stable branches as well as
- * development branches. It is possible that a new stable release of
- * Xen may require a rebuild of applications using the
- * library. However per the API compatibility gaurantees such a
- * rebuild should not normally require any source level changes.
- *
- * As with the API compatiblity the SONAME will only be bumped for the
- * first ABI incompatible change in a development branch.
- */
-
-/*
- * libxl memory management
- *
- * From the point of view of the application (ie, libxl's caller),
- * struct libxl_ctx* is threadsafe, and all returned allocated
- * structures are obtained from malloc(), and must be freed by the
- * caller either directly or by calling an appropriate free function
- * provided by libxl.  Ie the application does not get automatic
- * assistance from libxl in managing these allocations.
- *
- * Specific details are in the header comments which should be found
- * in libxl.h or libxlutil.h, next to the relevant function
- * declarations.
- *
- * Internally, libxl has a garbage collection scheme which allows much libxl
- * code to allocate strings etc. for internal use without needing to
- * free them.  These are called "temporary allocations".
- *
- * The pool for these temporary allocations, along with any other
- * thread-specific data which is private to libxl but shared between
- * libxl functions (such as the current xenstore transaction), is
- * stored in the "gc context" which is a special enhanced context
- * structure allocated automatically by convenience macros at every
- * entry to libxl.
- *
- * Every libxl function falls into one of these categories:
- *
- * 1. Public functions (declared in libxl.h, libxlutil.h), which may
- *    be called by libxl applications.  If a public function returns
- *    any allocated object to its caller, that object must have come
- *    from malloc.
- *
- *    The definitions of public functions MUST use the gc context
- *    initialisation macros (or do the equivalent work themselves).
- *    These macros will ensure that all temporary allocations will be
- *    automatically freed before the function returns to its caller.
- *
- *    A public function may be called from within libxl; the call
- *    context initialisation macros will make sure that the internal
- *    caller's context is reused (eg, so that the same xenstore
- *    transaction is used).  But in-libxl callers of libxl public
- *    functions should note that any libxl public function may cause
- *    recursively reentry into libxl via the application's event
- *    callback hook.
- *
- *    Public functions have names like libxl_foobar.
- *
- * 2. Private functions, which may not be called by libxl
- *    applications; they are not declared in libxl.h or libxlutil.h
- *    and they may not be called other than by other libxl functions.
- *
- *    Private functions should not use the gc context initialisation
- *    macros.
- *
- *    Private functions have names like libxl__foobar (NB, two underscores).
- *    Also the declaration of such functions must be preceeded by the _hidden
- *    macro.
- *
- * Allocations made by a libxl function fall into one of the following
- * categories (where "object" includes any memory allocation):
- *
- * (a) Objects which are not returned to the function's caller.
- *     These should be allocated from the temporary pool.
- *
- * (b) Objects which are intended for return to the calling
- *     application.  This includes all allocated objects returned by
- *     any public function.
- *
- *     It may also include objects allocated by an internal function
- *     specifically for eventual return by the function's external
- *     callers, but this situation should be clearly documented in
- *     comments.
- *
- *     These should be allocated from malloc() et al. and comments
- *     near the function declaration should explain the memory
- *     ownership.  If a simple free() by the application is not
- *     sufficient, a suitable public freeing function should be
- *     provided.
- *
- * (c) Internal objects whose size and/or lifetime dictate explicit
- *     memory management within libxl.  This includes objects which
- *     will be embedded in opaque structures which will be returned to
- *     the libxl caller (more generally, any internal object whose
- *     lifetime exceeds the libxl entrypoint which creates it) and
- *     objects which are so large or numerous that explicit memory
- *     management is required.
- *
- *     These should be allocated from malloc() et al., and freed
- *     explicitly at the appropriate point.  The situation should be
- *     documented in comments.
- *
- * (d) Objects which are allocated by internal-only functions and
- *     returned to the function's (therefore, internal) caller but are
- *     strictly for internal use by other parts of libxl.  These
- *     should be allocated from the temporary pool.
- *
- *     Where a function's primary purpose is to return such an object,
- *     it should have a libxl__gc * as it's first argument.
- *
- *     Note that there are two ways to change an allocation from this
- *     category to the "public" category. Either the implementation
- *     is kept internal and a wrapper function duplicates all memory
- *     allocations so that they are suitable for return to external
- *     callers or the implementation uses plain malloc() et al calls
- *     and an internal wrapper adds the relevant pointers to the gc.
- *     The latter method is preferred for obvious performance reasons.
- *
- * No temporary objects allocated from the pool may be explicitly freed.
- * Therefore public functions which initialize a libxl__gc MUST call
- * libxl__free_all() before returning.
- *
- * Memory allocation failures are not handled gracefully.  If malloc
- * (or realloc) fails, libxl will cause the entire process to print
- * a message to stderr and exit with status 255.
- */
-/*
- * libxl types
- *
- * Most libxl types are defined by the libxl IDL (see
- * libxl_types.idl). The library provides a common set of methods for
- * initialising and freeing these types.
- *
- * IDL-generated libxl types should be used as follows: the user must
- * always call the "init" function before using a type, even if the
- * variable is simply being passed by reference as an out parameter
- * to a libxl function.  The user must always calls "dispose" exactly
- * once afterwards, to clean up, regardless of whether operations on
- * this object succeeded or failed.  See the xl code for examples.
- *
- * "init" and "dispose" are idempotent.
- *
- * void libxl_<type>_init(<type> *p):
- *
- *    Initialises the members of "p" to all defaults. These may either
- *    be special value which indicates to the library that it should
- *    select an appropriate default when using this field or actual
- *    default values.
- *
- *    Some fields within a data type (e.g. unions) cannot be sensibly
- *    initialised without further information. In these cases a
- *    separate subfield initialisation function is provided (see
- *    below).
- *
- *    An instance which has been initialised using this method can
- *    always be safely passed to the dispose function (see
- *    below). This is true even if the data type contains fields which
- *    require a separate call to a subfield initialisation function.
- *
- *    This method is provided for any aggregate type which is used as
- *    an input parameter.
- *
- * void libxl_<type>_init_<subfield>(<type> *p, subfield):
- *
- *    Initialise those parts of "p" which are not initialised by the
- *    main init function due to the unknown value of "subfield". Sets
- *    p->subfield as well as initialising any fields to their default
- *    values.
- *
- *    p->subfield must not have been previously initialised.
- *
- *    This method is provided for any aggregate type.
- *
- * void libxl_<type>_dispose(instance *p):
- *
- *    Frees any dynamically allocated memory used by the members of
- *    "p" but not the storage used by "p" itself (this allows for the
- *    allocation of arrays of types and for the composition of types).
- *
- * char *libxl_<type>_to_json(instance *p)
- *
- *    Generates a JSON object from "p" in the form of a NULL terminated
- *    string.
- *
- * <type *> libxl_<type>_from_json(const char *json)
- * int      libxl_<type>_from_json(const char *json)
- *
- *    Parses "json" and returns:
- *
- *    an int value, if <type> is enumeration type. The value is the enum value
- *    representing the respective string in "json".
- *
- *    an instance of <type>, if <type> is aggregate type. The returned
- *    instance has its fields filled in by the parser according to "json".
- *
- *    If the parsing fails, caller cannot rely on the value / instance
- *    returned.
- */
-#ifndef LIBXL_H
-#define LIBXL_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <sys/wait.h> /* for pid_t */
-
-#include <xentoollog.h>
-
-typedef struct libxl__ctx libxl_ctx;
-
-#include <libxl_uuid.h>
-#include <_libxl_list.h>
-
-/* API compatibility. */
-#ifdef LIBXL_API_VERSION
-#if LIBXL_API_VERSION != 0x040200 && LIBXL_API_VERSION != 0x040300 && \
-    LIBXL_API_VERSION != 0x040400 && LIBXL_API_VERSION != 0x040500 && \
-    LIBXL_API_VERSION != 0x040700 && LIBXL_API_VERSION != 0x040800 && \
-    LIBXL_API_VERSION != 0x041300 && LIBXL_API_VERSION != 0x041400
-#error Unknown LIBXL_API_VERSION
-#endif
-#endif
-
-/* LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION
- *
- * If this is defined we have libxl_retrieve_domain_configuration which
- * returns the current configuration of a domain, which can be used to
- * rebuild a domain.
- */
-#define LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain two arrays of libxl_bitmap-s, with all the necessary information
- * to set the hard affinity (vcpu_hard_affinity) and the soft affinity
- * (vcpu_soft_affinity) of the VCPUs.
- *
- * Note that, if the vcpu_hard_affinity array is used, libxl will ignore
- * the content of the cpumap field of libxl_domain_build_info. That is to
- * say, if the array is allocated and used by the caller, it is it and
- * only it that determines the hard affinity of the domain's VCPUs.
- *
- * The number of libxl_bitmap-s in the arrays should be equal to the
- * maximum number of VCPUs of the domain. If there only are N elements in
- * an array, with N smaller the the maximum number of VCPUs, the hard or
- * soft affinity (depending on which array we are talking about) will be
- * set only for the first N VCPUs. The other VCPUs will just have affinity,
- * both hard and soft, with all the host PCPUs.
- * Each bitmap should be big enough to accommodate the maximum number of
- * PCPUs of the host.
- */
-#define LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_VKB_DEVICE
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain a boolean hvm.vkb_device which instructs libxl whether to include
- * a vkbd at build time or not.
- */
-#define LIBXL_HAVE_BUILDINFO_VKB_DEVICE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain hvm.usbdevice_list, a libxl_string_list type that contains
- * a list of USB devices to specify on the qemu command-line.
- *
- * If it is set, callers may use either hvm.usbdevice or
- * hvm.usbdevice_list, but not both; if both are set, libxl will
- * throw an error.
- *
- * If this is not defined, callers can only use hvm.usbdevice.  Note
- * that this means only one device can be added at domain build time.
- */
-#define LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_USBVERSION
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain hvm.usbversion, a integer type that contains a USB
- * controller version to specify on the qemu upstream command-line.
- *
- * If it is set, callers may use hvm.usbversion to specify if the usb
- * controller is usb1, usb2 or usb3.
- *
- * If this is not defined, the hvm.usbversion field does not exist.
- */
-#define LIBXL_HAVE_BUILDINFO_USBVERSION 1
-
-/*
- * LIBXL_HAVE_DEVICE_BACKEND_DOMNAME
- *
- * If this is defined, libxl_device_* structures containing a backend_domid
- * field also contain a backend_domname field.  If backend_domname is set, it is
- * resolved to a domain ID when the device is used and takes precedence over the
- * backend_domid field.
- *
- * If this is not defined, the backend_domname field does not exist.
- */
-#define LIBXL_HAVE_DEVICE_BACKEND_DOMNAME 1
-
-/*
- * LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
- *
- * This argument was erroneously "const" in the 4.2 release despite
- * the requirement for the callback to free the event.
- */
-#if LIBXL_API_VERSION != 0x040200
-#define LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG 1
-#endif
-
-/*
- * LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
- *
- * The return value of libxl_basename is malloc'ed but the erroneously
- * marked as "const" in releases before 4.5.
- */
-#if !defined(LIBXL_API_VERSION) || LIBXL_API_VERSION >= 0x040500
-#define LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE 1
-#endif
-
-/*
- * LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES
- *
- * If this is defined, libxl_physinfo structure will contain an uint64 field
- * called outstanding_pages, containing the number of pages claimed but not
- * yet allocated for all domains.
- */
-#define LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES 1
-
-/*
- * LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN
- *
- * If this is defined, libxl_physinfo structure will contain an uint64 field
- * called max_possible_mfn, containing the highest possible mfn on this host,
- * possibly taking memory hotplug into account.
- */
-#define LIBXL_HAVE_PHYSINFO_MAX_POSSIBLE_MFN 1
-
-/*
- * LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
- *
- * If this is defined, libxl_dominfo will contain a MemKB type field called
- * outstanding_memkb, containing the amount of claimed but not yet allocated
- * memory for a specific domain.
- */
-#define LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1
-
-/*
- * LIBXL_HAVE_DOMINFO_NEVER_STOP
- *
- * If this is defined, libxl_dominfo will contain a flag called never_stop
- * indicating that the specific domain should never be stopped by the
- * toolstack.
- */
-#define LIBXL_HAVE_DOMINFO_NEVER_STOP 1
-
-/*
- * LIBXL_HAVE_QXL
- *
- * If defined, then the libxl_vga_interface_type will contain another value:
- * "QXL". This value define if qxl vga is supported.
- *
- * If this is not defined, the qxl vga support is missed.
- */
-#define LIBXL_HAVE_QXL 1
-
-/*
- * LIBXL_HAVE_SPICE_VDAGENT
- *
- * If defined, then the libxl_spice_info structure will contain a boolean type:
- * vdagent and clipboard_sharing. These values define if Spice vdagent and
- * clipboard sharing are enabled.
- *
- * If this is not defined, the Spice vdagent support is ignored.
- */
-#define LIBXL_HAVE_SPICE_VDAGENT 1
-
-/*
- * LIBXL_HAVE_SPICE_USBREDIRECTION
- *
- * If defined, then the libxl_spice_info structure will contain an integer type
- * field: usbredirection. This value defines if Spice usbredirection is enabled
- * and with how much channels.
- *
- * If this is not defined, the Spice usbredirection support is ignored.
- */
-#define LIBXL_HAVE_SPICE_USBREDIREDIRECTION 1
-
-/*
- * LIBXL_HAVE_SPICE_IMAGECOMPRESSION
- *
- * If defined, then the libxl_spice_info structure will contain a string type
- * field: image_compression. This value defines what Spice image compression
- * is used.
- *
- * If this is not defined, the Spice image compression setting support is ignored.
- */
-#define LIBXL_HAVE_SPICE_IMAGECOMPRESSION 1
-
-/*
- * LIBXL_HAVE_SPICE_STREAMINGVIDEO
- *
- * If defined, then the libxl_spice_info structure will contain a string type
- * field: streaming_video. This value defines what Spice streaming video setting
- * is used.
- *
- * If this is not defined, the Spice streaming video setting support is ignored.
- */
-#define LIBXL_HAVE_SPICE_STREAMINGVIDEO 1
-
-/*
- * LIBXL_HAVE_HVM_HDTYPE
- *
- * If defined, then the u.hvm structure will contain a enum type
- * hdtype.
- */
-#define LIBXL_HAVE_HVM_HDTYPE 1
-
-/*
- * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
- *
- * If this is defined, libxl_domain_create_restore()'s API has changed to
- * include a params structure.
- */
-#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1
-
-/*
- * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
- *
- * If this is defined, libxl_domain_create_restore()'s API includes the
- * send_back_fd param. This is used only with COLO, for the libxl migration
- * back channel; other callers should pass -1.
- */
-#define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1
-
-/*
- * LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
- *
- * If this is defined, libxl_domain_create_info contains a driver_domain
- * field that can be used to tell libxl that the domain that is going
- * to be created is a driver domain, so the necessary actions are taken.
- */
-#define LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1
-
-/*
- * LIBXL_HAVE_SIGCHLD_SELECTIVE_REAP
- *
- * If this is defined:
- *
- * Firstly, the enum libxl_sigchld_owner (in libxl_event.h) has the
- * value libxl_sigchld_owner_libxl_always_selective_reap which may be
- * passed to libxl_childproc_setmode in hooks->chldmode.
- *
- * Secondly, the function libxl_childproc_sigchld_occurred exists.
- */
-#define LIBXL_HAVE_SIGCHLD_OWNER_SELECTIVE_REAP 1
-
-/*
- * LIBXL_HAVE_SIGCHLD_SHARING
- *
- * If this is defined, it is permissible for multiple libxl ctxs
- * to simultaneously "own" SIGCHLD.  See "Subprocess handling"
- * in libxl_event.h.
- */
-#define LIBXL_HAVE_SIGCHLD_SHARING 1
-
-/*
- * LIBXL_HAVE_NO_SUSPEND_RESUME
- *
- * Is this is defined then the platform has no support for saving,
- * restoring or migrating a domain. In this case the related functions
- * should be expected to return failure. That is:
- *  - libxl_domain_suspend
- *  - libxl_domain_resume
- *  - libxl_domain_remus_start
- */
-#if defined(__arm__) || defined(__aarch64__)
-#define LIBXL_HAVE_NO_SUSPEND_RESUME 1
-#endif
-
-/*
- * LIBXL_HAVE_DOMAIN_SUSPEND_ONLY
- *
- * If this is defined, function libxl_domains_suspend_only() is available.
- */
-
-#define LIBXL_HAVE_DOMAIN_SUSPEND_ONLY 1
-
-/*
- * LIBXL_HAVE_DEVICE_PCI_SEIZE
- *
- * If this is defined, then the libxl_device_pci struct will contain
- * the "seize" boolean field.  If this field is set, libxl_pci_add will
- * check to see if the device is currently assigned to pciback, and if not,
- * it will attempt to do so (unbinding the device from the existing driver).
- */
-#define LIBXL_HAVE_DEVICE_PCI_SEIZE 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_KERNEL
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain 'kernel', 'ramdisk', 'cmdline' fields. 'kernel' is a string
- * to indicate kernel image location, 'ramdisk' is a string to indicate
- * ramdisk location, 'cmdline' is a string to indicate the paramters which
- * would be appended to kernel image.
- *
- * Both PV guest and HVM guest can use these fields for direct kernel boot.
- * But for compatibility reason, u.pv.kernel, u.pv.ramdisk and u.pv.cmdline
- * still exist.
- */
-#define LIBXL_HAVE_BUILDINFO_KERNEL 1
-
-/*
- * LIBXL_HAVE_DEVICE_CHANNEL
- *
- * If this is defined, then the libxl_device_channel struct exists
- * and channels can be attached to a domain. Channels manifest as consoles
- * with names, see docs/misc/console.txt.
- */
-#define LIBXL_HAVE_DEVICE_CHANNEL 1
-
-/*
- * LIBXL_HAVE_AO_ABORT indicates the availability of libxl_ao_abort
- */
-#define LIBXL_HAVE_AO_ABORT 1
-
-/* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be
- * called from within libxl itself. Callers outside libxl, who
- * do not #include libxl_internal.h, are fine. */
-#ifndef LIBXL_EXTERNAL_CALLERS_ONLY
-#define LIBXL_EXTERNAL_CALLERS_ONLY /* disappears for callers outside libxl */
-#endif
-
-/*
- *  LIBXL_HAVE_UUID_COPY_CTX_PARAM
- *
- * If this is defined, libxl_uuid_copy has changed to take a libxl_ctx
- * structure.
- */
-#define LIBXL_HAVE_UUID_COPY_CTX_PARAM 1
-
-/*
- * LIBXL_HAVE_SSID_LABEL
- *
- * If this is defined, then libxl IDL contains string of XSM security
- * label in all XSM related structures.
- *
- * If set this string takes precedence over the numeric field.
- */
-#define LIBXL_HAVE_SSID_LABEL 1
-
-/*
- * LIBXL_HAVE_CPUPOOL_NAME
- *
- * If this is defined, then libxl IDL contains string of CPU pool
- * name in all CPU pool related structures.
- *
- * If set this string takes precedence over the numeric field.
- */
-#define LIBXL_HAVE_CPUPOOL_NAME 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_SERIAL_LIST
- *
- * If this is defined, then the libxl_domain_build_info structure will
- * contain hvm.serial_list, a libxl_string_list type that contains
- * a list of serial ports to specify on the qemu command-line.
- *
- * If it is set, callers may use either hvm.serial or
- * hvm.serial_list, but not both; if both are set, libxl will
- * throw an error.
- *
- * If this is not defined, callers can only use hvm.serial.  Note
- * that this means only one serial port can be added at domain build time.
- */
-#define LIBXL_HAVE_BUILDINFO_SERIAL_LIST 1
-
-/*
- * LIBXL_HAVE_ALTP2M
- * If this is defined, then libxl supports alternate p2m functionality.
- */
-#define LIBXL_HAVE_ALTP2M 1
-
-/*
- * LIBXL_HAVE_REMUS
- * If this is defined, then libxl supports remus.
- */
-#define LIBXL_HAVE_REMUS 1
-
-/*
- * LIBXL_HAVE_COLO_USERSPACE_PROXY
- * If this is defined, then libxl supports COLO userspace proxy.
- */
-#define LIBXL_HAVE_COLO_USERSPACE_PROXY 1
-
-typedef uint8_t libxl_mac[6];
-#define LIBXL_MAC_FMT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
-#define LIBXL_MAC_FMTLEN ((2*6)+5) /* 6 hex bytes plus 5 colons */
-#define LIBXL_MAC_BYTES(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
-void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src);
-
-#if defined(__i386__) || defined(__x86_64__)
-/*
- * LIBXL_HAVE_PSR_CMT
- *
- * If this is defined, the Cache Monitoring Technology feature is supported.
- */
-#define LIBXL_HAVE_PSR_CMT 1
-
-/*
- * LIBXL_HAVE_PSR_MBM
- *
- * If this is defined, the Memory Bandwidth Monitoring feature is supported.
- */
-#define LIBXL_HAVE_PSR_MBM 1
-
-/*
- * LIBXL_HAVE_PSR_CAT
- *
- * If this is defined, the Cache Allocation Technology feature is supported.
- */
-#define LIBXL_HAVE_PSR_CAT 1
-
-/*
- * LIBXL_HAVE_PSR_CDP
- *
- * If this is defined, the Code and Data Prioritization feature is supported.
- */
-#define LIBXL_HAVE_PSR_CDP 1
-
-/*
- * LIBXL_HAVE_PSR_L2_CAT
- *
- * If this is defined, the L2 Cache Allocation Technology feature is supported.
- */
-#define LIBXL_HAVE_PSR_L2_CAT 1
-
-/*
- * LIBXL_HAVE_PSR_GENERIC
- *
- * If this is defined, the Memory Bandwidth Allocation feature is supported.
- * The following public functions are available:
- *   libxl_psr_{set/get}_val
- *   libxl_psr_get_hw_info
- *   libxl_psr_hw_info_list_free
- */
-#define LIBXL_HAVE_PSR_GENERIC 1
-
-/*
- * LIBXL_HAVE_MCA_CAPS
- *
- * If this is defined, setting MCA capabilities for HVM domain is supported.
- */
-#define LIBXL_HAVE_MCA_CAPS 1
-#endif
-
-/*
- * LIBXL_HAVE_PCITOPOLOGY
- *
- * If this is defined, then interface to query hypervisor about PCI device
- * topology is available.
- */
-#define LIBXL_HAVE_PCITOPOLOGY 1
-
-/*
- * LIBXL_HAVE_SOCKET_BITMAP
- *
- * If this is defined, then libxl_socket_bitmap_alloc and
- * libxl_get_online_socketmap exist.
- */
-#define LIBXL_HAVE_SOCKET_BITMAP 1
-
-/*
- * LIBXL_HAVE_SRM_V2
- *
- * If this is defined, then the libxl_domain_create_restore() interface takes
- * a "stream_version" parameter and supports a value of 2.
- *
- * libxl_domain_suspend() will produce a v2 stream.
- */
-#define LIBXL_HAVE_SRM_V2 1
-
-/*
- * LIBXL_HAVE_SRM_V1
- *
- * In the case that LIBXL_HAVE_SRM_V2 is set, LIBXL_HAVE_SRM_V1
- * indicates that libxl_domain_create_restore() can handle a "stream_version"
- * parameter of 1, and convert the stream format automatically.
- */
-#define LIBXL_HAVE_SRM_V1 1
-
-/*
- * libxl_domain_build_info has the u.hvm.gfx_passthru_kind field and
- * the libxl_gfx_passthru_kind enumeration is defined.
-*/
-#define LIBXL_HAVE_GFX_PASSTHRU_KIND
-
-/*
- * LIBXL_HAVE_CHECKPOINTED_STREAM
- *
- * If this is defined, then libxl_checkpointed_stream exists.
- */
-#define LIBXL_HAVE_CHECKPOINTED_STREAM 1
-
-/*
- * LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
- *
- * libxl_domain_build_info has u.hvm.system_firmware field which can be use
- * to provide a different firmware blob (like SeaBIOS or OVMF).
- */
-#define LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE
-
-/*
- * ERROR_REMUS_XXX error code only exists from Xen 4.5, Xen 4.6 and it
- * is changed to ERROR_CHECKPOINT_XXX in Xen 4.7
- */
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040500 \
-                               && LIBXL_API_VERSION < 0x040700
-#define ERROR_REMUS_DEVOPS_DOES_NOT_MATCH \
-        ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH
-#define ERROR_REMUS_DEVICE_NOT_SUPPORTED \
-        ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED
-#endif
-
-/*
- * LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN
- *
- * In the case that LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN is set the
- * libxl_vga_interface_type enumeration type contains a
- * LIBXL_VGA_INTERFACE_TYPE_UNKNOWN identifier. This is used to signal
- * that a libxl_vga_interface_type type has not been initialized yet.
- */
-#define LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN 1
-
-/*
- * LIBXL_HAVE_BYTEARRAY_UUID
- *
- * If this is defined, the internal member of libxl_uuid is defined
- * as a 16 byte array that contains the UUID in big endian format.
- * Also, the same structure layout is used across all OSes.
- */
-#define LIBXL_HAVE_BYTEARRAY_UUID 1
-
-/*
- * LIBXL_HAVE_MEMKB_64BITS
- *
- * If this is defined libxl_set_memory_target(), libxl_domain_setmaxmem()
- * and libxl_wait_for_free_memory()  will take a 64 bit value for the memory
- * size parameter.
- * From Xen 4.8 on libxl_get_memory_target(), libxl_domain_need_memory() and
- * libxl_get_free_memory() return the memory size in a 64 bit value, too.
- */
-#define LIBXL_HAVE_MEMKB_64BITS 1
-
-/*
- * LIBXL_HAVE_QED
- *
- * If this is defined QED disk formats can be used for both HVM and PV guests.
- */
-#define LIBXL_HAVE_QED 1
-
-/*
- * LIBXL_HAVE_SET_PARAMETERS
- *
- * If this is defined setting hypervisor parameters is supported.
- */
-#define LIBXL_HAVE_SET_PARAMETERS 1
-
-/*
- * LIBXL_HAVE_PV_SHIM
- *
- * If this is defined, libxl_domain_build_info's pvh type information
- * contains members pvshim, pvshim_path, pvshim_cmdline, pvshim_extra.
- */
-#define LIBXL_HAVE_PV_SHIM 1
-
-/*
- * LIBXL_HAVE_PVCALLS
- *
- * If this is defined, libxl supports creating pvcalls interfaces.
- */
-#define LIBXL_HAVE_PVCALLS 1
-
-/*
- * LIBXL_HAVE_FN_USING_QMP_ASYNC
- *
- * This define indicates that some function's API has changed and have an
- * extra parameter "ao_how" which means that the function can be executed
- * asynchronously. Those functions are:
- *   libxl_domain_pause()
- *   libxl_domain_unpause()
- *   libxl_send_trigger()
- *   libxl_set_vcpuonline()
- *   libxl_retrieve_domain_configuration()
- *   libxl_qemu_monitor_command()
- *   libxl_domain_shutdown()
- *   libxl_domain_reboot()
- */
-#define LIBXL_HAVE_FN_USING_QMP_ASYNC 1
-
-/*
- * LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
- *
- * If this is set, libxl_domain_need_memory takes a
- * libxl_domain_config* (non-const) and uint32_t domid_for_logging
- * (instead of a const libxl_domain_build_info*).
- *
- * If this is set, there is no need to call
- * libxl_get_required_shadow_memory and instead the caller should
- * simply leave shadow_memkb set to LIBXL_MEMKB_DEFAULT and allow
- * libxl to fill in a suitable default in the usual way.
- */
-#define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG
-
-/*
- * LIBXL_HAVE_CREATEINFO_DOMID
- *
- * libxl_domain_create_new() and libxl_domain_create_restore() will use
- * a domid specified in libxl_domain_create_info.
- */
-#define LIBXL_HAVE_CREATEINFO_DOMID
-
-/*
- * LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
- *
- * libxl_domain_create_info contains a boolean 'xend_suspend_evtchn_compat'
- * value to control creation of the xenstore path for a domain's suspend
- * event channel.
- */
-#define LIBXL_HAVE_CREATEINFO_XEND_SUSPEND_EVTCHN_COMPAT
-
-typedef char **libxl_string_list;
-void libxl_string_list_dispose(libxl_string_list *sl);
-int libxl_string_list_length(const libxl_string_list *sl);
-void libxl_string_list_copy(libxl_ctx *ctx, libxl_string_list *dst,
-                            const libxl_string_list *src);
-
-typedef char **libxl_key_value_list;
-void libxl_key_value_list_dispose(libxl_key_value_list *kvl);
-int libxl_key_value_list_length(const libxl_key_value_list *kvl);
-void libxl_key_value_list_copy(libxl_ctx *ctx,
-                               libxl_key_value_list *dst,
-                               const libxl_key_value_list *src);
-
-typedef uint32_t libxl_hwcap[8];
-void libxl_hwcap_copy(libxl_ctx *ctx, libxl_hwcap *dst, const libxl_hwcap *src);
-
-typedef uint64_t libxl_ev_user;
-
-typedef struct {
-    uint32_t size;          /* number of bytes in map */
-    uint8_t *map;
-} libxl_bitmap;
-void libxl_bitmap_init(libxl_bitmap *map);
-void libxl_bitmap_dispose(libxl_bitmap *map);
-
-/*
- * libxl_cpuid_policy is opaque in the libxl ABI.  Users of both libxl and
- * libxc may not make assumptions about xc_xend_cpuid.
- */
-typedef struct xc_xend_cpuid libxl_cpuid_policy;
-typedef libxl_cpuid_policy * libxl_cpuid_policy_list;
-void libxl_cpuid_dispose(libxl_cpuid_policy_list *cpuid_list);
-int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *l);
-void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
-                                  libxl_cpuid_policy_list *dst,
-                                  const libxl_cpuid_policy_list *src);
-
-#define LIBXL_PCI_FUNC_ALL (~0U)
-
-typedef uint32_t libxl_domid;
-typedef int libxl_devid;
-
-/*
- * Formatting Enumerations.
- *
- * Each enumeration type libxl_E declares an associated lookup table
- * libxl_E_string_table and a lookup function libxl_E_from_string.
- */
-typedef struct {
-    const char *s;
-    int v;
-} libxl_enum_string_table;
-
-struct libxl_event;
-typedef LIBXL_TAILQ_ENTRY(struct libxl_event) libxl_ev_link;
-
-/*
- * A boolean variable with an explicit default state.
- *
- * Users should treat this struct as opaque and use the following
- * defined macros and accessor functions.
- *
- * To allow users of the library to naively select all defaults this
- * state is represented as 0. False is < 0 and True is > 0.
- */
-typedef struct {
-    int val;
-} libxl_defbool;
-
-void libxl_defbool_set(libxl_defbool *db, bool b);
-/* Resets to default */
-void libxl_defbool_unset(libxl_defbool *db);
-/* Sets db only if it is currently == default */
-void libxl_defbool_setdefault(libxl_defbool *db, bool b);
-bool libxl_defbool_is_default(libxl_defbool db);
-/* db must not be == default */
-bool libxl_defbool_val(libxl_defbool db);
-
-const char *libxl_defbool_to_string(libxl_defbool b);
-
-#define LIBXL_TIMER_MODE_DEFAULT -1
-#define LIBXL_MEMKB_DEFAULT ~0ULL
-
-/*
- * We'd like to set a memory boundary to determine if we need to check
- * any overlap with reserved device memory.
- */
-#define LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT (2048 * 1024)
-
-#define LIBXL_MS_VM_GENID_LEN 16
-typedef struct {
-    uint8_t bytes[LIBXL_MS_VM_GENID_LEN];
-} libxl_ms_vm_genid;
-
-#include "_libxl_types.h"
-
-const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx);
-
-/*
- * Some libxl operations can take a long time.  These functions take a
- * parameter to control their concurrency:
- *     libxl_asyncop_how *ao_how
- *
- * If ao_how==NULL, the function will be synchronous.
- *
- * If ao_how!=NULL, the function will set the operation going, and if
- * this is successful will return 0.  In this case the zero error
- * response does NOT mean that the operation was successful; it just
- * means that it has been successfully started.  It will finish later,
- * perhaps with an error.
- *
- * If ao_how->callback!=NULL, the callback will be called when the
- * operation completes.  The same rules as for libxl_event_hooks
- * apply, including the reentrancy rules and the possibility of
- * "disaster", except that libxl calls ao_how->callback instead of
- * libxl_event_hooks.event_occurs.  (See libxl_event.h.)
- *
- * If ao_how->callback==NULL, a libxl_event will be generated which
- * can be obtained from libxl_event_wait or libxl_event_check.  The
- * event will have type OPERATION_COMPLETE (which is not used
- * elsewhere).
- *
- * Note that it is possible for an asynchronous operation which is to
- * result in a callback to complete during its initiating function
- * call.  In this case the initiating function will return 0
- * indicating the at the operation is "in progress", even though by
- * the time it returns the operation is complete and the callback has
- * already happened.
- *
- * The application must set and use ao_how->for_event (which will be
- * copied into libxl_event.for_user) or ao_how->for_callback (passed
- * to the callback) to determine which operation finished, and it must
- * of course check the rc value for errors.
- *
- * *ao_how does not need to remain valid after the initiating function
- * returns. All other parameters must remain valid for the lifetime of
- * the asynchronous operation, unless otherwise specified.
- *
- * Callbacks may occur on any thread in which the application calls
- * libxl.
- */
-
-typedef struct {
-    void (*callback)(libxl_ctx *ctx, int rc, void *for_callback);
-    union {
-        libxl_ev_user for_event; /* used if callback==NULL */
-        void *for_callback; /* passed to callback */
-    } u;
-} libxl_asyncop_how;
-
-/*
- * Some more complex asynchronous operations can report intermediate
- * progress.  How this is to be reported is controlled, for each
- * function, by a parameter
- *    libxl_asyncprogress_how *aop_FOO_how;
- * for each kind of progress FOO supported by that function.  Each
- * such kind of progress is associated with an event type.
- *
- * The function description will document whether, when, and how
- * many times, the intermediate progress will be reported, and
- * what the corresponding event type(s) are.
- *
- * If aop_FOO_how==NULL, intermediate progress reports are discarded.
- *
- * If aop_FOO_how->callback==NULL, intermediate progress reports
- * generate libxl events which can be obtained from libxl_event_wait
- * or libxl_event_check.
- *
- * If aop_FOO_how->callback!=NULL, libxl will report intermediate
- * progress by calling callback(ctx, &event, for_callback).
- *
- * The rules for these events are otherwise the same as those for
- * ordinary events.  The reentrancy and threading rules for the
- * callback are the same as those for ao completion callbacks.
- *
- * Note that the callback, if provided, is responsible for freeing
- * the event.
- *
- * If callbacks are requested, they will be made, and returned, before
- * the long-running libxl operation is considered finished (so if the
- * long-running libxl operation was invoked with ao_how==NULL then any
- * callbacks will occur strictly before the long-running operation
- * returns).  However, the callbacks may occur on any thread.
- *
- * In general, otherwise, no promises are made about the relative
- * order of callbacks in a multithreaded program.  In particular
- * different callbacks relating to the same long-running operation may
- * be delivered out of order.
- */
-
-typedef struct {
-    void (*callback)(libxl_ctx *ctx, libxl_event*, void *for_callback);
-    libxl_ev_user for_event; /* always used */
-    void *for_callback; /* passed to callback */
-} libxl_asyncprogress_how;
-
-/*
- * It is sometimes possible to abort an asynchronous operation.
- *
- * libxl_ao_abort searches for an ongoing asynchronous operation whose
- * ao_how is identical to *how, and tries to abort it.  The return
- * values from libxl_ao_abort are as follows:
- *
- *  0
- *
- *     The operation was found, and attempts are being made to cut it
- *     short.  However, it may still take some time to stop.  It is
- *     also possible that the operation will nevertheless complete
- *     successfully.
- *
- *  ERROR_NOTFOUND
- *
- *      No matching ongoing operation was found.  This might happen
- *      for an actual operation if the operation has already completed
- *      (perhaps on another thread).  The call to libxl_ao_abort has
- *      had no effect.
- *
- *  ERROR_ABORTED
- *
- *     The operation has already been the subject of at least one
- *     call to libxl_ao_abort.
- *
- * If the operation was indeed cut short due to the abort request, it
- * will complete, at some point in the future, with ERROR_ABORTED.  In
- * that case, depending on the operation it have performed some of the
- * work in question and left the operation half-done.  Consult the
- * documentation for individual operations.
- *
- * Note that an aborted operation might still fail for other reasons
- * even after the abort was requested.
- *
- * If your application is multithreaded you must not reuse an
- * ao_how->for_event or ao_how->for_callback value (with a particular
- * ao_how->callback) unless you are sure that none of your other
- * threads are going to abort the previous operation using that
- * value; otherwise you risk aborting the wrong operation if the
- * intended target of the abort request completes in the meantime.
- *
- * It is possible to abort even an operation which is being performed
- * synchronously, but since in that case how==NULL you had better only
- * have one such operation, because it is not possible to tell them
- * apart (and libxl_ao_abort will abort only the first one it finds).
- * (And, if you want to do this, obviously the abort would have to be
- * requested on a different thread.)
- */
-int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
-                   LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-#define LIBXL_VERSION 0
-
-/* context functions */
-int libxl_ctx_alloc(libxl_ctx **pctx, int version,
-                    unsigned flags /* none currently defined */,
-                    xentoollog_logger *lg);
-int libxl_ctx_free(libxl_ctx *ctx /* 0 is OK */);
-
-/* domain related functions */
-
-#define INVALID_DOMID ~0
-#define RANDOM_DOMID (INVALID_DOMID - 1)
-
-/* If the result is ERROR_ABORTED, the domain may or may not exist
- * (in a half-created state).  *domid will be valid and will be the
- * domain id, or INVALID_DOMID, as appropriate */
-
-int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
-                            uint32_t *domid,
-                            const libxl_asyncop_how *ao_how,
-                            const libxl_asyncprogress_how *aop_console_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
-                                uint32_t *domid, int restore_fd,
-                                int send_back_fd,
-                                const libxl_domain_restore_params *params,
-                                const libxl_asyncop_how *ao_how,
-                                const libxl_asyncprogress_how *aop_console_how)
-                                LIBXL_EXTERNAL_CALLERS_ONLY;
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040400
-
-static inline int libxl_domain_create_restore_0x040200(
-    libxl_ctx *ctx, libxl_domain_config *d_config,
-    uint32_t *domid, int restore_fd,
-    const libxl_asyncop_how *ao_how,
-    const libxl_asyncprogress_how *aop_console_how)
-    LIBXL_EXTERNAL_CALLERS_ONLY
-{
-    libxl_domain_restore_params params;
-    int ret;
-
-    libxl_domain_restore_params_init(&params);
-
-    ret = libxl_domain_create_restore(
-        ctx, d_config, domid, restore_fd, -1, &params, ao_how, aop_console_how);
-
-    libxl_domain_restore_params_dispose(&params);
-    return ret;
-}
-
-#define libxl_domain_create_restore libxl_domain_create_restore_0x040200
-
-#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040400 \
-                                 && LIBXL_API_VERSION < 0x040700
-
-static inline int libxl_domain_create_restore_0x040400(
-    libxl_ctx *ctx, libxl_domain_config *d_config,
-    uint32_t *domid, int restore_fd,
-    const libxl_domain_restore_params *params,
-    const libxl_asyncop_how *ao_how,
-    const libxl_asyncprogress_how *aop_console_how)
-    LIBXL_EXTERNAL_CALLERS_ONLY
-{
-    return libxl_domain_create_restore(ctx, d_config, domid, restore_fd,
-                                       -1, params, ao_how, aop_console_how);
-}
-
-#define libxl_domain_create_restore libxl_domain_create_restore_0x040400
-
-#endif
-
-int libxl_domain_soft_reset(libxl_ctx *ctx,
-                            libxl_domain_config *d_config,
-                            uint32_t domid,
-                            const libxl_asyncop_how *ao_how,
-                            const libxl_asyncprogress_how
-                            *aop_console_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-
-  /* A progress report will be made via ao_console_how, of type
-   * domain_create_console_available, when the domain's primary
-   * console is available and can be connected to.
-   */
-
-void libxl_domain_config_init(libxl_domain_config *d_config);
-void libxl_domain_config_dispose(libxl_domain_config *d_config);
-
-/*
- * Retrieve domain configuration and filled it in d_config. The
- * returned configuration can be used to rebuild a domain. It only
- * works with DomU.
- */
-int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
-                                        libxl_domain_config *d_config,
-                                        const libxl_asyncop_how *ao_how)
-                                        LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_retrieve_domain_configuration_0x041200(
-    libxl_ctx *ctx, uint32_t domid, libxl_domain_config *d_config)
-{
-    return libxl_retrieve_domain_configuration(ctx, domid, d_config, NULL);
-}
-#define libxl_retrieve_domain_configuration \
-    libxl_retrieve_domain_configuration_0x041200
-#endif
-
-int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd,
-                         int flags, /* LIBXL_SUSPEND_* */
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-#define LIBXL_SUSPEND_DEBUG 1
-#define LIBXL_SUSPEND_LIVE 2
-
-/*
- * Only suspend domain, do not save its state to file, do not destroy it.
- * Suspended domain can be resumed with libxl_domain_resume()
- */
-int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* @param suspend_cancel [from xenctrl.h:xc_domain_resume( @param fast )]
- *   If this parameter is true, use co-operative resume. The guest
- *   must support this.
- */
-int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
-                        const libxl_asyncop_how *ao_how)
-                        LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * This function doesn't return unless something has gone wrong with
- * the replication to the secondary. If this function returns then the
- * caller should resume the (primary) domain.
- */
-int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
-                             uint32_t domid, int send_fd, int recv_fd,
-                             const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
-                          const libxl_asyncop_how *ao_how)
-                          LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
-                        const libxl_asyncop_how *ao_how)
-                        LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_domain_shutdown_0x041200(libxl_ctx *ctx,
-                                                 uint32_t domid)
-{
-    return libxl_domain_shutdown(ctx, domid, NULL);
-}
-#define libxl_domain_shutdown libxl_domain_shutdown_0x041200
-static inline int libxl_domain_reboot_0x041200(libxl_ctx *ctx,
-                                               uint32_t domid)
-{
-    return libxl_domain_reboot(ctx, domid, NULL);
-}
-#define libxl_domain_reboot libxl_domain_reboot_0x041200
-#endif
-
-int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid);
-
-/* get max. number of cpus supported by hypervisor */
-int libxl_get_max_cpus(libxl_ctx *ctx);
-
-/* get the actual number of currently online cpus on the host */
-int libxl_get_online_cpus(libxl_ctx *ctx);
-  /* Beware that no locking or serialization is provided by libxl,
-   * so the information can be outdated as far as the function
-   * returns. If there are other entities in the system capable
-   * of onlining/offlining CPUs, it is up to the application
-   * to guarantee consistency, if that is important. */
-
-/* get max. number of NUMA nodes supported by hypervisor */
-int libxl_get_max_nodes(libxl_ctx *ctx);
-
-int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
-                        const char *old_name, const char *new_name);
-
-  /* if old_name is NULL, any old name is OK; otherwise we check
-   * transactionally that the domain has the old old name; if
-   * trans is not 0 we use caller's transaction and caller must do retries */
-
-int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
-                       const libxl_asyncop_how *ao_how)
-                       LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_domain_pause_0x041200(
-    libxl_ctx *ctx, uint32_t domid)
-{
-    return libxl_domain_pause(ctx, domid, NULL);
-}
-static inline int libxl_domain_unpause_0x041200(
-    libxl_ctx *ctx, uint32_t domid)
-{
-    return libxl_domain_unpause(ctx, domid, NULL);
-}
-#define libxl_domain_pause libxl_domain_pause_0x041200
-#define libxl_domain_unpause libxl_domain_unpause_0x041200
-#endif
-
-
-int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
-                           const char *filename,
-                           const libxl_asyncop_how *ao_how)
-                           LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t target_memkb);
-int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int64_t target_memkb, int relative, int enforce);
-int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint64_t *out_target);
-int libxl_get_memory_target_0x040700(libxl_ctx *ctx, uint32_t domid,
-                                     uint32_t *out_target)
-    LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * WARNING
- * This memory management API is unstable even in Xen 4.2.
- * It has a numer of deficiencies and we intend to replace it.
- *
- * The semantics of these functions should not be relied on to be very
- * coherent or stable.  We will however endeavour to keep working
- * existing programs which use them in roughly the same way as libxl.
- */
-/* how much free memory in the system a domain needs to be built */
-int libxl_domain_need_memory(libxl_ctx *ctx,
-                             libxl_domain_config *config
-                             /* ^ will be partially defaulted */,
-                             uint32_t domid_for_logging /* INVALID_DOMID ok */,
-                             uint64_t *need_memkb);
-int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
-                                      const libxl_domain_build_info *b_info_in,
-                                      uint64_t *need_memkb);
-int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
-                                      const libxl_domain_build_info *b_info_in,
-                                      uint32_t *need_memkb)
-    LIBXL_EXTERNAL_CALLERS_ONLY;
-/* how much free memory is available in the system */
-int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb);
-int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
-    LIBXL_EXTERNAL_CALLERS_ONLY;
-/* wait for a given amount of memory to be free in the system */
-int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint64_t memory_kb, int wait_secs);
-/*
- * Wait for the memory target of a domain to be reached. Does not
- * decrement wait_secs if the domain is making progress toward reaching
- * the target. If the domain is not making progress, wait_secs is
- * decremented. If the timeout expires before the target is reached, the
- * function returns ERROR_FAIL.
- *
- * Older versions of this function (Xen 4.5 and older), decremented
- * wait_secs even if the domain was making progress, resulting in far
- * lower overall wait times. To make sure that your calling routine
- * works with new and old implementations of the function, pass enough
- * time for the guest to reach its target as an argument.
- */
-int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs);
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
-#define libxl_get_memory_target libxl_get_memory_target_0x040700
-#define libxl_domain_need_memory libxl_domain_need_memory_0x040700
-#define libxl_get_free_memory libxl_get_free_memory_0x040700
-#elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-#define libxl_domain_need_memory libxl_domain_need_memory_0x041200
-#endif
-
-int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass);
-
-/*
- * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
- * the caller that it has connected to the guest console.
- */
-int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
-                       libxl_console_type type, int notify_fd);
-/* libxl_primary_console_exec finds the domid and console number
- * corresponding to the primary console of the given vm, then calls
- * libxl_console_exec with the right arguments (domid might be different
- * if the guest is using stubdoms).
- * This function can be called after creating the device model, in
- * case of HVM guests, and before libxl_run_bootloader in case of PV
- * guests using pygrub.
- * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity
- * the caller that it has connected to the guest console.
- */
-int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm,
-                               int notify_fd);
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800
-
-static inline int libxl_console_exec_0x040700(libxl_ctx *ctx,
-                                              uint32_t domid, int cons_num,
-                                              libxl_console_type type)
-{
-    return libxl_console_exec(ctx, domid, cons_num, type, -1);
-}
-#define libxl_console_exec libxl_console_exec_0x040700
-
-static inline int libxl_primary_console_exec_0x040700(libxl_ctx *ctx,
-                                                      uint32_t domid_vm)
-{
-    return libxl_primary_console_exec(ctx, domid_vm, -1);
-}
-#define libxl_primary_console_exec libxl_primary_console_exec_0x040700
-
-#endif
-
-/* libxl_console_get_tty retrieves the specified domain's console tty path
- * and stores it in path. Caller is responsible for freeing the memory.
- */
-int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
-                          libxl_console_type type, char **path);
-
-/* libxl_primary_console_get_tty retrieves the specified domain's primary
- * console tty path and stores it in path. Caller is responsible for freeing
- * the memory.
- */
-int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, char **path);
-
-/* May be called with info_r == NULL to check for domain's existence.
- * Returns ERROR_DOMAIN_NOTFOUND if domain does not exist (used to return
- * ERROR_INVAL for this scenario). */
-int libxl_domain_info(libxl_ctx*, libxl_dominfo *info_r,
-                      uint32_t domid);
-
-/* These functions each return (on success) an array of elements,
- * and the length via the int* out parameter.  These arrays and
- * their contents come from malloc, and must be freed with the
- * corresponding libxl_THING_list_free function.
- */
-libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out);
-void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain);
-
-libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out);
-void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool);
-
-libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out);
-void libxl_vminfo_list_free(libxl_vminfo *list, int nb_vm);
-
-#define LIBXL_CPUTOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
-libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out);
-void libxl_cputopology_list_free(libxl_cputopology *, int nb_cpu);
-
-#define LIBXL_PCITOPOLOGY_INVALID_ENTRY (~(uint32_t)0)
-libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs);
-void libxl_pcitopology_list_free(libxl_pcitopology *, int num_devs);
-
-#define LIBXL_NUMAINFO_INVALID_ENTRY (~(uint32_t)0)
-libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr);
-void libxl_numainfo_list_free(libxl_numainfo *, int nr);
-
-libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
-                                int *nb_vcpu, int *nr_cpus_out);
-void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus);
-
-/*
- * Devices
- * =======
- *
- * Each device is represented by a libxl_device_<TYPE> data structure
- * which is defined via the IDL. In addition some devices have an
- * additional data type libxl_device_<TYPE>_getinfo which contains
- * further runtime information about the device.
- *
- * In addition to the general methods available for libxl types (see
- * "libxl types" above) a common set of methods are available for each
- * device type. These are described below.
- *
- * Querying
- * --------
- *
- * libxl_device_<type>_list(ctx, domid, nr):
- *
- *   Returns an array of libxl_device_<type> length nr representing
- *   the devices attached to the specified domain.
- *
- * libxl_device_<type>_getinfo(ctx, domid, device, info):
- *
- *   Initialises info with details of the given device which must be
- *   attached to the specified domain.
- *
- * Creation / Control
- * ------------------
- *
- * libxl_device_<type>_add(ctx, domid, device):
- *
- *   Adds the given device to the specified domain. This can be called
- *   while the guest is running (hotplug) or before boot (coldplug).
- *
- *   This function only sets up the device but does not wait for the
- *   domain to connect to the device and therefore cannot block on the
- *   guest.
- *
- *   device is an in/out parameter:  fields left unspecified when the
- *   structure is passed in are filled in with appropriate values for
- *   the device created.
- *
- * libxl_device_<type>_destroy(ctx, domid, device):
- *
- *   Removes the given device from the specified domain without guest
- *   co-operation. It is guest specific what affect this will have on
- *   a running guest.
- *
- *   This function does not interact with the guest and therefore
- *   cannot block on the guest.
- *
- * libxl_device_<type>_remove(ctx, domid, device):
- *
- *   Removes the given device from the specified domain by performing
- *   an orderly unplug with guest co-operation. This requires that the
- *   guest is running.
- *
- *   This method is currently synchronous and therefore can block
- *   while interacting with the guest. There is a time-out of 10s on
- *   this interaction after which libxl_device_<type>_destroy()
- *   semantics apply.
- *
- * libxl_device_<type>_safe_remove(ctx, domid, device):
- *
- *   This has the same semantics as libxl_device_<type>_remove() but,
- *   in the event of hitting the 10s time-out, this function will fail.
- *
- * Controllers
- * -----------
- *
- * Most devices are treated individually.  Some classes of device,
- * however, like USB or SCSI, inherently have the need to have a
- * hierarchy of different levels, with lower-level devices "attached"
- * to higher-level ones.  USB for instance has "controllers" at the
- * top, which have buses, on which are devices, which consist of
- * multiple interfaces.  SCSI has "hosts" at the top, then buses,
- * targets, and LUNs.
- *
- * In that case, for each <class>, there will be a set of functions
- * and types for each <level>.  For example, for <class>=usb, there
- * may be <levels> ctrl (controller) and dev (device), with ctrl being
- * level 0.
- *
- * libxl_device_<class><level0>_<function> will act more or
- * less like top-level non-bus devices: they will either create or
- * accept a libxl_devid which will be unique within the
- * <class><level0> libxl_devid namespace.
- *
- * Lower-level devices must have a unique way to be identified.  One
- * way to do this would be to name it via the name of the next level
- * up plus an index; for instance, <ctrl devid, port number>.  Another
- * way would be to have another devid namespace for that level.  This
- * identifier will be used for queries and removals.
- *
- * Lower-level devices will include in their
- * libxl_device_<class><level> struct a field referring to the unique
- * index of the level above.  For instance, libxl_device_usbdev might
- * contain the controller devid.
- *
- * In the case where there are multiple different ways to implement a
- * given device -- for instance, one which is fully PV and one which
- * uses an emulator -- the controller will contain a field which
- * specifies what type of implementation is used.  The implementations
- * of individual devices will be known by the controller to which they
- * are attached.
- *
- * If libxl_device_<class><level>_add receives an empty reference to
- * the level above, it may return an error.  Or it may (but is not
- * required to) automatically choose a suitable device in the level
- * above to which to attach the new device at this level.  It may also
- * (but is not required to) automatically create a new device at the
- * level above if no suitable devices exist.  Each class should
- * document its behavior.
- *
- * libxl_device_<class><level>_list will list all devices of <class>
- * at <level> in the domain.  For example, libxl_device_usbctrl_list
- * will list all usb controllers; libxl_class_usbdev_list will list
- * all usb devices across all controllers.
- *
- * For each class, the domain config file will contain a single list
- * for each level.  libxl will first iterate through the list of
- * top-level devices, then iterate through each level down in turn,
- * adding devices to devices in the level above.  For instance, there
- * will be one list for all usb controllers, and one list for all usb
- * devices.
- *
- * If libxl_device_<class><level>_add automatically creates
- * higher-level devices as necessary, then it is permissible for the
- * higher-level lists to be empty and the device list to have devices
- * with the field containing a reference to the higher level device
- * uninitialized.
- */
-
-/* Disks */
-int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
-                          libxl_device_disk *disk,
-                          const libxl_asyncop_how *ao_how)
-                          LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_disk *disk,
-                             const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_destroy(libxl_ctx *ctx, uint32_t domid,
-                              libxl_device_disk *disk,
-                              const libxl_asyncop_how *ao_how)
-                              LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_safe_remove(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_device_disk *disk,
-                                  const libxl_asyncop_how *ao_how)
-                                  LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx,
-                                          uint32_t domid, int *num)
-                                          LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_disk_list_free(libxl_device_disk* list, int num)
-                                 LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
-                              const libxl_device_disk *disk, libxl_diskinfo *diskinfo)
-                              LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * Insert a CD-ROM device. A device corresponding to disk must already
- * be attached to the guest.
- */
-int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
-                       const libxl_asyncop_how *ao_how)
-                       LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * USB
- *
- * For each device removed or added, one of these protocols is available:
- * - PV (i.e., PVUSB)
- * - DEVICEMODEL (i.e, qemu)
- *
- * PV is available for either PV or HVM domains.  DEVICEMODEL is only
- * available for HVM domains.  The caller can additionally specify
- * "AUTO", in which case the library will try to determine the best
- * protocol automatically.
- *
- * At the moment, the only protocol implemented is PV.
- *
- * One can add/remove USB controllers to/from guest, and attach/detach USB
- * devices to/from USB controllers.
- *
- * To add USB controllers and USB devices, one can adding USB controllers
- * first and then attaching USB devices to some USB controller, or adding
- * USB devices to guest directly, it will automatically create a USB
- * controller for USB devices to attach.
- *
- * To remove USB controllers or USB devices, one can remove USB devices
- * under USB controller one by one and then remove USB controller, or
- * remove USB controller directly, it will remove all USB devices under
- * it automatically.
- *
- */
-/* USB Controllers*/
-int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_usbctrl *usbctrl,
-                             const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid,
-                                libxl_device_usbctrl *usbctrl,
-                                const libxl_asyncop_how *ao_how)
-                                LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid,
-                                 libxl_device_usbctrl *usbctrl,
-                                 const libxl_asyncop_how *ao_how)
-                                 LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx,
-                                                uint32_t domid, int *num);
-
-void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr);
-
-
-int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
-                                 const libxl_device_usbctrl *usbctrl,
-                                 libxl_usbctrlinfo *usbctrlinfo);
-
-/* USB Devices */
-
-int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_usbdev *usbdev,
-                            const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-
-int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
-                               libxl_device_usbdev *usbdev,
-                               const libxl_asyncop_how *ao_how)
-                               LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_usbdev *
-libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num);
-
-void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr);
-
-/* Network Interfaces */
-int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_nic_remove(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_nic *nic,
-                            const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_nic_destroy(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_nic *nic,
-                             const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx,
-                                        uint32_t domid, int *num)
-                                        LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_nic_list_free(libxl_device_nic* list, int num)
-                                LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
-                             const libxl_device_nic *nic, libxl_nicinfo *nicinfo)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * Virtual Channels
- * Channels manifest as consoles with names, see docs/misc/channels.txt
- */
-libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
-                                                uint32_t domid,
-                                                int *num);
-int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
-                                 const libxl_device_channel *channel,
-                                 libxl_channelinfo *channelinfo);
-
-/* Virtual TPMs */
-int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm,
-                          const libxl_asyncop_how *ao_how)
-                          LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vtpm_remove(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_vtpm *vtpm,
-                            const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vtpm_destroy(libxl_ctx *ctx, uint32_t domid,
-                              libxl_device_vtpm *vtpm,
-                              const libxl_asyncop_how *ao_how)
-                              LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vtpm *libxl_device_vtpm_list(libxl_ctx *ctx,
-                                          uint32_t domid, int *num)
-                                          LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vtpm_list_free(libxl_device_vtpm*, int num)
-                                 LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vtpm_getinfo(libxl_ctx *ctx, uint32_t domid,
-                              const libxl_device_vtpm *vtpm, libxl_vtpminfo *vtpminfo)
-                              LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Virtual displays */
-int libxl_device_vdispl_add(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_vdispl *displ,
-                            const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vdispl_remove(libxl_ctx *ctx, uint32_t domid,
-                               libxl_device_vdispl *vdispl,
-                               const libxl_asyncop_how *ao_how)
-                               LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vdispl_destroy(libxl_ctx *ctx, uint32_t domid,
-                                libxl_device_vdispl *vdispl,
-                                const libxl_asyncop_how *ao_how)
-                                LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vdispl *libxl_device_vdispl_list(libxl_ctx *ctx,
-                                              uint32_t domid, int *num)
-                                              LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vdispl_list_free(libxl_device_vdispl* list, int num)
-                                   LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
-                                const libxl_device_vdispl *vdispl,
-                                libxl_vdisplinfo *vdisplinfo)
-                                LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Virtual sounds */
-int libxl_device_vsnd_add(libxl_ctx *ctx, uint32_t domid,
-                          libxl_device_vsnd *vsnd,
-                          const libxl_asyncop_how *ao_how)
-                          LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vsnd_remove(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_vsnd *vsnd,
-                             const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vsnd_destroy(libxl_ctx *ctx, uint32_t domid,
-                              libxl_device_vsnd *vsnd,
-                              const libxl_asyncop_how *ao_how)
-                              LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vsnd *libxl_device_vsnd_list(libxl_ctx *ctx,
-                                          uint32_t domid, int *num)
-                                          LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vsnd_list_free(libxl_device_vsnd* list, int num)
-                                 LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
-                              const libxl_device_vsnd *vsnd,
-                              libxl_vsndinfo *vsndlinfo)
-                              LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Keyboard */
-int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vkb_remove(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_vkb *vkb,
-                            const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vkb_destroy(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_vkb *vkb,
-                             const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_vkb *libxl_device_vkb_list(libxl_ctx *ctx,
-                                        uint32_t domid, int *num)
-                                        LIBXL_EXTERNAL_CALLERS_ONLY;
-void libxl_device_vkb_list_free(libxl_device_vkb* list, int num)
-                                LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
-                             const libxl_device_vkb *vkb,
-                             libxl_vkbinfo *vkbinfo)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Framebuffer */
-int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vfb_remove(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_vfb *vfb,
-                            const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_vfb_destroy(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_vfb *vfb,
-                             const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* 9pfs */
-int libxl_device_p9_remove(libxl_ctx *ctx, uint32_t domid,
-                           libxl_device_p9 *p9,
-                           const libxl_asyncop_how *ao_how)
-                           LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_p9_destroy(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_p9 *p9,
-                            const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* pvcalls interface */
-int libxl_device_pvcallsif_remove(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_device_pvcallsif *pvcallsif,
-                                  const libxl_asyncop_how *ao_how)
-                                  LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_pvcallsif_destroy(libxl_ctx *ctx, uint32_t domid,
-                                   libxl_device_pvcallsif *pvcallsif,
-                                   const libxl_asyncop_how *ao_how)
-                                   LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* PCI Passthrough */
-int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
-                         libxl_device_pci *pcidev,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_pci *pcidev,
-                            const libxl_asyncop_how *ao_how)
-                            LIBXL_EXTERNAL_CALLERS_ONLY;
-int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_pci *pcidev,
-                             const libxl_asyncop_how *ao_how)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid,
-                                        int *num);
-
-/*
- * Turns the current process into a backend device service daemon
- * for a driver domain.
- *
- * From a libxl API point of view, this starts a long-running
- * operation.  That operation consists of "being a driver domain"
- * and never completes.
- *
- * Attempting to abort this operation is not advisable; proper
- * shutdown of the driver domain task is not supported.
- */
-int libxl_device_events_handler(libxl_ctx *ctx,
-                                const libxl_asyncop_how *ao_how)
-                                LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * Functions related to making devices assignable -- that is, bound to
- * the pciback driver, ready to be given to a guest via
- * libxl_pci_device_add.
- *
- * - ..._add() will unbind the device from its current driver (if
- * already bound) and re-bind it to pciback; at that point it will be
- * ready to be assigned to a VM.  If rebind is set, it will store the
- * path to the old driver in xenstore so that it can be handed back to
- * dom0 on restore.
- *
- * - ..._remove() will unbind the device from pciback, and if
- * rebind is non-zero, attempt to assign it back to the driver
- * from whence it came.
- *
- * - ..._list() will return a list of the PCI devices available to be
- * assigned.
- *
- * add and remove are idempotent: if the device in question is already
- * added or is not bound, the functions will emit a warning but return
- * SUCCESS.
- */
-int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
-int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind);
-libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num);
-
-/* CPUID handling */
-int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str);
-int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
-                                  const char* str);
-#if LIBXL_API_VERSION < 0x041400
-/*
- * Dropped from the API in Xen 4.14.  At the time of writing, these functions
- * don't appear to ever have had external callers.
- *
- * These have always been used internally during domain construction, and
- * can't easily be used externally because of their implicit parameters in
- * other pieces of global state.
- *
- * Furthermore, an API user can't usefully determine whether they get
- * libxl_cpuid (the real implementation) or libxl_nocpuid (no-op stubs).
- *
- * The internal behaviour of these functions also needs to change.  Therefore
- * for simplicitly, provide the no-op stubs.  Yes technically this is an API
- * change in some cases for existing software, but there is 0 of that in
- * practice.
- */
-static inline void libxl_cpuid_apply_policy(libxl_ctx *ctx __attribute__((unused)),
-                                            uint32_t domid __attribute__((unused)))
-{}
-static inline void libxl_cpuid_set(libxl_ctx *ctx __attribute__((unused)),
-                                   uint32_t domid __attribute__((unused)),
-                                   libxl_cpuid_policy_list cpuid __attribute__((unused)))
-{}
-#endif
-
-/*
- * Functions for allowing users of libxl to store private data
- * relating to a domain.  The data is an opaque sequence of bytes and
- * is not interpreted or used by libxl.
- *
- * Data is indexed by the userdata userid, which is a short printable
- * ASCII string.  The following list is a registry of userdata userids
- * (the registry may be updated by posting a patch to xen-devel):
- *
- *  userid        Data contents
- *  "xl"          domain config file in xl format, Unix line endings
- *  "libvirt-xml" domain config file in libvirt XML format.  See
- *                http://libvirt.org/formatdomain.html
- *  "domain-userdata-lock"  lock file to protect domain userdata in libxl.
- *                          It's a per-domain lock. Applications should
- *                          not touch this file.
- *  "libxl-json"  libxl_domain_config object in JSON format, generated
- *                by libxl. Applications should not access this file
- *                directly. This file is protected by domain-userdata-lock
- *                for against Read-Modify-Write operation and domain
- *                destruction.
- *
- * libxl does not enforce the registration of userdata userids or the
- * semantics of the data.  For specifications of the data formats
- * see the code or documentation for the libxl caller in question.
- */
-int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
-                              const char *userdata_userid,
-                              const uint8_t *data, int datalen)
-                              LIBXL_EXTERNAL_CALLERS_ONLY;
-  /* If datalen==0, data is not used and the user data for
-   * that domain and userdata_userid is deleted. */
-int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
-                                 const char *userdata_userid,
-                                 uint8_t **data_r, int *datalen_r)
-                                 LIBXL_EXTERNAL_CALLERS_ONLY;
-  /* On successful return, *data_r is from malloc.
-   * If there is no data for that domain and userdata_userid,
-   * *data_r and *datalen_r will be set to 0.
-   * data_r and datalen_r may be 0.
-   * On error return, *data_r and *datalen_r are undefined.
-   */
-int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
-                          const char *userdata_userid);
-
-
-int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo);
-int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
-                           const libxl_bitmap *cpumap_hard,
-                           const libxl_bitmap *cpumap_soft);
-int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
-                                 uint32_t vcpuid,
-                                 const libxl_bitmap *cpumap_hard,
-                                 const libxl_bitmap *cpumap_soft);
-int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
-                               unsigned int max_vcpus,
-                               const libxl_bitmap *cpumap_hard,
-                               const libxl_bitmap *cpumap_soft);
-
-#if defined (LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
-
-#define libxl_set_vcpuaffinity(ctx, domid, vcpuid, map) \
-    libxl_set_vcpuaffinity((ctx), (domid), (vcpuid), (map), NULL)
-#define libxl_set_vcpuaffinity_all(ctx, domid, max_vcpus, map) \
-    libxl_set_vcpuaffinity_all((ctx), (domid), (max_vcpus), (map), NULL)
-
-#endif
-
-int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_bitmap *nodemap);
-int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_bitmap *nodemap);
-int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
-                         libxl_bitmap *cpumap,
-                         const libxl_asyncop_how *ao_how)
-                         LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_set_vcpuonline_0x041200(libxl_ctx *ctx,
-                                                uint32_t domid,
-                                                libxl_bitmap *cpumap)
-{
-    return libxl_set_vcpuonline(ctx, domid, cpumap, NULL);
-}
-#define libxl_set_vcpuonline libxl_set_vcpuonline_0x041200
-#endif
-
-/* A return value less than 0 should be interpreted as a libxl_error, while a
- * return value greater than or equal to 0 should be interpreted as a
- * libxl_scheduler. */
-int libxl_get_scheduler(libxl_ctx *ctx);
-
-/* Per-scheduler parameters */
-int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
-                                  libxl_sched_credit_params *scinfo);
-int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
-                                  libxl_sched_credit_params *scinfo);
-int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
-                                   libxl_sched_credit2_params *scinfo);
-int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
-                                   libxl_sched_credit2_params *scinfo);
-
-/* Scheduler Per-domain parameters */
-
-#define LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT    -1
-#define LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT       -1
-#define LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT    -1
-#define LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT     -1
-#define LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT   -1
-#define LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT -1
-#define LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT    -1
-
-/* Per-VCPU parameters */
-#define LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT   -1
-
-/* Get the per-domain scheduling parameters.
- * For schedulers that support per-vcpu settings (e.g., RTDS),
- * calling *_domain_get functions will get default scheduling
- * parameters.
- */
-int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_domain_sched_params *params);
-
-/* Set the per-domain scheduling parameters.
- * For schedulers that support per-vcpu settings (e.g., RTDS),
- * calling *_domain_set functions will set all vcpus with the same
- * scheduling parameters.
- */
-int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
-                                  const libxl_domain_sched_params *params);
-
-/* Get the per-vcpu scheduling parameters */
-int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
-                                libxl_vcpu_sched_params *params);
-
-/* Get the per-vcpu scheduling parameters of all vcpus of a domain */
-int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
-                                    libxl_vcpu_sched_params *params);
-
-/* Set the per-vcpu scheduling parameters */
-int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
-                                const libxl_vcpu_sched_params *params);
-
-/* Set the per-vcpu scheduling parameters of all vcpus of a domain */
-int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
-                                    const libxl_vcpu_sched_params *params);
-
-int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
-                       libxl_trigger trigger, uint32_t vcpuid,
-                       const libxl_asyncop_how *ao_how)
-                       LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_send_trigger_0x041200(
-    libxl_ctx *ctx, uint32_t domid, libxl_trigger trigger, uint32_t vcpuid)
-{
-    return libxl_send_trigger(ctx, domid, trigger, vcpuid, NULL);
-}
-#define libxl_send_trigger libxl_send_trigger_0x041200
-#endif
-int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq);
-int libxl_send_debug_keys(libxl_ctx *ctx, char *keys);
-int libxl_set_parameters(libxl_ctx *ctx, char *params);
-
-typedef struct libxl__xen_console_reader libxl_xen_console_reader;
-
-libxl_xen_console_reader *
-    libxl_xen_console_read_start(libxl_ctx *ctx, int clear);
-int libxl_xen_console_read_line(libxl_ctx *ctx,
-                                libxl_xen_console_reader *cr,
-                                char **line_r);
-void libxl_xen_console_read_finish(libxl_ctx *ctx,
-                                   libxl_xen_console_reader *cr);
-
-uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid);
-
-char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long);
-int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid);
-int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid);
-int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name,
-                   uint32_t set);
-int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid, char* uuid,
-                           int auth);
-int libxl_tmem_freeable(libxl_ctx *ctx);
-
-int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap);
-
-/*
- * Set poolid to LIBXL_CPUOOL_POOLID_ANY to have Xen choose a
- * free poolid for you.
- */
-#define LIBXL_CPUPOOL_POOLID_ANY 0xFFFFFFFF
-int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
-                         libxl_scheduler sched,
-                         libxl_bitmap cpumap, libxl_uuid *uuid,
-                         uint32_t *poolid);
-int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid);
-int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid);
-int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu);
-int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
-int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
-                                const libxl_bitmap *cpumap);
-int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu);
-int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
-int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
-                                   const libxl_bitmap *cpumap);
-int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid);
-int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid);
-
-int libxl_domid_valid_guest(uint32_t domid);
-
-int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
-                               uint32_t *ssidref);
-int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref, char **buf,
-                               size_t *len);
-int libxl_flask_getenforce(libxl_ctx *ctx);
-int libxl_flask_setenforce(libxl_ctx *ctx, int mode);
-int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size);
-
-int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id);
-bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id);
-void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
-                            const libxl_ms_vm_genid *src);
-
-#if defined(__i386__) || defined(__x86_64__)
-int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid);
-int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid);
-int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid);
-int libxl_psr_cmt_enabled(libxl_ctx *ctx);
-int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid);
-int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
-                                    uint32_t socketid,
-                                    uint32_t *l3_cache_size);
-int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
-                                      uint32_t domid,
-                                      uint32_t socketid,
-                                      uint32_t *l3_cache_occupancy);
-
-int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type);
-int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
-                             uint32_t domid,
-                             libxl_psr_cmt_type type,
-                             uint64_t scope,
-                             uint64_t *sample_r,
-                             uint64_t *tsc_r);
-
-/*
- * Function to set a domain's cbm. It operates on a single or multiple
- * target(s) defined in 'target_map'. The definition of 'target_map' is
- * related to 'type':
- * 'L3_CBM': 'target_map' specifies all the sockets to be operated on.
- */
-int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
-                          libxl_psr_cbm_type type, libxl_bitmap *target_map,
-                          uint64_t cbm);
-/*
- * Function to get a domain's cbm. It operates on a single 'target'.
- * The definition of 'target' is related to 'type':
- * 'L3_CBM': 'target' specifies which socket to be operated on.
- */
-int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
-                          libxl_psr_cbm_type type, uint32_t target,
-                          uint64_t *cbm_r);
-
-/*
- * On success, the function returns an array of elements in 'info',
- * and the length in 'nr'.
- */
-int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
-                           unsigned int *nr, unsigned int lvl);
-int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
-                              int *nr);
-void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr);
-
-typedef enum libxl_psr_cbm_type libxl_psr_type;
-
-/*
- * Function to set a domain's value. It operates on a single or multiple
- * target(s) defined in 'target_map'. 'target_map' specifies all the sockets
- * to be operated on.
- */
-int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
-                      libxl_psr_type type, libxl_bitmap *target_map,
-                      uint64_t val);
-/*
- * Function to get a domain's cbm. It operates on a single 'target'.
- * 'target' specifies which socket to be operated on.
- */
-int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
-                      libxl_psr_type type, unsigned int target,
-                      uint64_t *val);
-/*
- * On success, the function returns an array of elements in 'info',
- * and the length in 'nr'.
- */
-int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
-                          unsigned int lvl, unsigned int *nr,
-                          libxl_psr_hw_info **info);
-void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr);
-#endif
-
-/* misc */
-
-/* Each of these sets or clears the flag according to whether the
- * 2nd parameter is nonzero.  On failure, they log, and
- * return ERROR_FAIL, but also leave errno valid. */
-int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec);
-int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock);
-
-/*
- * Issue a qmp monitor command to the device model of the specified domain.
- * The function returns the output of the command in a new allocated buffer
- * via output.
- */
-int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
-                               const char *command_line, char **output,
-                               const libxl_asyncop_how *ao_how)
-                               LIBXL_EXTERNAL_CALLERS_ONLY;
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x041300
-static inline int libxl_qemu_monitor_command_0x041200(libxl_ctx *ctx,
-    uint32_t domid, const char *command_line, char **output)
-{
-    return libxl_qemu_monitor_command(ctx, domid, command_line, output,
-                                      NULL);
-}
-#define libxl_qemu_monitor_command libxl_qemu_monitor_command_0x041200
-#endif
-
-#include <libxl_event.h>
-
-/*
- * This function is for use only during host initialisation. If it is
- * invoked on a host with running domains, or concurrent libxl
- * processes then the system may malfuntion.
- */
-int libxl_clear_domid_history(libxl_ctx *ctx);
-
-#endif /* LIBXL_H */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_9pfs.c b/tools/libxl/libxl_9pfs.c
deleted file mode 100644 (file)
index e5c41e9..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2017      Aporeto
- * Author Stefano Stabellini <stefano@aporeto.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__device_p9_setdefault(libxl__gc *gc, uint32_t domid,
-                                       libxl_device_p9 *p9, bool hotplug)
-{
-    return libxl__resolve_domid(gc, p9->backend_domname, &p9->backend_domid);
-}
-
-static int libxl__set_xenstore_p9(libxl__gc *gc, uint32_t domid,
-                                  libxl_device_p9 *p9,
-                                  flexarray_t *back, flexarray_t *front,
-                                  flexarray_t *ro_front)
-{
-    flexarray_append_pair(back, "path", p9->path);
-    flexarray_append_pair(back, "security_model", p9->security_model);
-
-    flexarray_append_pair(front, "tag", p9->tag);
-
-    return 0;
-}
-
-#define libxl__add_p9s NULL
-#define libxl_device_p9_list NULL
-#define libxl_device_p9_compare NULL
-
-static LIBXL_DEFINE_UPDATE_DEVID(p9)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(p9)
-
-LIBXL_DEFINE_DEVICE_REMOVE(p9)
-
-DEFINE_DEVICE_TYPE_STRUCT(p9, 9PFS,
-    .skip_attach = 1,
-    .set_xenstore_config = (device_set_xenstore_config_fn_t)
-                           libxl__set_xenstore_p9,
-);
diff --git a/tools/libxl/libxl_aoutils.c b/tools/libxl/libxl_aoutils.c
deleted file mode 100644 (file)
index c4c095a..0000000
+++ /dev/null
@@ -1,667 +0,0 @@
-/*
- * Copyright (C) 2010      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*----- xswait -----*/
-
-static libxl__ev_xswatch_callback xswait_xswatch_callback;
-static libxl__ev_time_callback xswait_timeout_callback;
-static void xswait_report_error(libxl__egc*, libxl__xswait_state*, int rc);
-
-void libxl__xswait_init(libxl__xswait_state *xswa)
-{
-    libxl__ev_time_init(&xswa->time_ev);
-    libxl__ev_xswatch_init(&xswa->watch_ev);
-}
-
-void libxl__xswait_stop(libxl__gc *gc, libxl__xswait_state *xswa)
-{
-    libxl__ev_time_deregister(gc, &xswa->time_ev);
-    libxl__ev_xswatch_deregister(gc, &xswa->watch_ev);
-}
-
-bool libxl__xswait_inuse(const libxl__xswait_state *xswa)
-{
-    bool time_inuse = libxl__ev_time_isregistered(&xswa->time_ev);
-    bool watch_inuse = libxl__ev_xswatch_isregistered(&xswa->watch_ev);
-    assert(time_inuse == watch_inuse);
-    return time_inuse;
-}
-
-int libxl__xswait_start(libxl__gc *gc, libxl__xswait_state *xswa)
-{
-    int rc;
-
-    rc = libxl__ev_time_register_rel(xswa->ao, &xswa->time_ev,
-                                     xswait_timeout_callback, xswa->timeout_ms);
-    if (rc) goto err;
-
-    rc = libxl__ev_xswatch_register(gc, &xswa->watch_ev,
-                                    xswait_xswatch_callback, xswa->path);
-    if (rc) goto err;
-
-    return 0;
-
- err:
-    libxl__xswait_stop(gc, xswa);
-    return rc;
-}
-
-void xswait_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *xsw,
-                             const char *watch_path, const char *event_path)
-{
-    EGC_GC;
-    libxl__xswait_state *xswa = CONTAINER_OF(xsw, *xswa, watch_ev);
-    int rc;
-    const char *data;
-
-    if (xswa->path[0] == '@') {
-        data = 0;
-    } else {
-        rc = libxl__xs_read_checked(gc, XBT_NULL, xswa->path, &data);
-        if (rc) { xswait_report_error(egc, xswa, rc); return; }
-    }
-
-    xswa->callback(egc, xswa, 0, data);
-}
-
-void xswait_timeout_callback(libxl__egc *egc, libxl__ev_time *ev,
-                             const struct timeval *requested_abs,
-                             int rc)
-{
-    EGC_GC;
-    libxl__xswait_state *xswa = CONTAINER_OF(ev, *xswa, time_ev);
-    LOG(DEBUG, "%s: xswait timeout (path=%s)", xswa->what, xswa->path);
-    xswait_report_error(egc, xswa, rc);
-}
-
-static void xswait_report_error(libxl__egc *egc, libxl__xswait_state *xswa,
-                                int rc)
-{
-    EGC_GC;
-    libxl__xswait_stop(gc, xswa);
-    xswa->callback(egc, xswa, rc, 0);
-}
-
-
-/*----- data copier -----*/
-
-void libxl__datacopier_init(libxl__datacopier_state *dc)
-{
-    assert(dc->ao);
-    libxl__ao_abortable_init(&dc->abrt);
-    libxl__ev_fd_init(&dc->toread);
-    libxl__ev_fd_init(&dc->towrite);
-    LIBXL_TAILQ_INIT(&dc->bufs);
-}
-
-void libxl__datacopier_kill(libxl__datacopier_state *dc)
-{
-    STATE_AO_GC(dc->ao);
-    libxl__datacopier_buf *buf, *tbuf;
-
-    libxl__ao_abortable_deregister(&dc->abrt);
-    libxl__ev_fd_deregister(gc, &dc->toread);
-    libxl__ev_fd_deregister(gc, &dc->towrite);
-    LIBXL_TAILQ_FOREACH_SAFE(buf, &dc->bufs, entry, tbuf)
-        free(buf);
-    LIBXL_TAILQ_INIT(&dc->bufs);
-}
-
-static void datacopier_callback(libxl__egc *egc, libxl__datacopier_state *dc,
-                                int rc, int onwrite, int errnoval)
-{
-    libxl__datacopier_kill(dc);
-    dc->callback(egc, dc, rc, onwrite, errnoval);
-}
-
-static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
-                                int fd, short events, short revents);
-
-static void datacopier_check_state(libxl__egc *egc, libxl__datacopier_state *dc)
-{
-    STATE_AO_GC(dc->ao);
-    int rc;
-    
-    if (dc->used && !dc->readbuf) {
-        if (!libxl__ev_fd_isregistered(&dc->towrite)) {
-            rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
-                                       dc->writefd, POLLOUT);
-            if (rc) {
-                LOG(ERROR, "unable to establish write event on %s"
-                    " during copy of %s", dc->writewhat, dc->copywhat);
-                datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
-                return;
-            }
-        }
-    } else if (!libxl__ev_fd_isregistered(&dc->toread) ||
-               dc->bytes_to_read == 0) {
-        /* we have had eof */
-        datacopier_callback(egc, dc, 0, 0, 0);
-        return;
-    } else {
-        /* nothing buffered, but still reading */
-        libxl__ev_fd_deregister(gc, &dc->towrite);
-    }
-}
-
-void libxl__datacopier_prefixdata(libxl__egc *egc, libxl__datacopier_state *dc,
-                                  const void *data, size_t len)
-{
-    EGC_GC;
-    libxl__datacopier_buf *buf;
-    const uint8_t *ptr;
-
-    /*
-     * It is safe for this to be called immediately after _start, as
-     * is documented in the public comment.  _start's caller must have
-     * the ctx locked, so other threads don't get to mess with the
-     * contents, and the fd events cannot happen reentrantly.  So we
-     * are guaranteed to beat the first data from the read fd.
-     */
-
-    assert(len < dc->maxsz - dc->used);
-
-    for (ptr = data; len; len -= buf->used, ptr += buf->used) {
-        buf = libxl__malloc(NOGC, sizeof(*buf));
-        buf->used = min(len, sizeof(buf->buf));
-        memcpy(buf->buf, ptr, buf->used);
-
-        dc->used += buf->used;
-        LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
-    }
-}
-
-static int datacopier_pollhup_handled(libxl__egc *egc,
-                                      libxl__datacopier_state *dc,
-                                      int fd, short revents, int onwrite)
-{
-    STATE_AO_GC(dc->ao);
-
-    if (dc->callback_pollhup && (revents & POLLHUP)) {
-        LOG(DEBUG, "received POLLHUP on fd %d: %s during copy of %s",
-            fd, onwrite ? dc->writewhat : dc->readwhat, dc->copywhat);
-        libxl__datacopier_kill(dc);
-        dc->callback_pollhup(egc, dc, ERROR_FAIL, onwrite, -1);
-        return 1;
-    }
-    return 0;
-}
-
-static void datacopier_abort(libxl__egc *egc, libxl__ao_abortable *abrt,
-                             int rc)
-{
-    libxl__datacopier_state *dc = CONTAINER_OF(abrt, *dc, abrt);
-    STATE_AO_GC(dc->ao);
-
-    datacopier_callback(egc, dc, rc, -1, 0);
-}
-
-static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev,
-                                int fd, short events, short revents) {
-    libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread);
-    STATE_AO_GC(dc->ao);
-
-    if (datacopier_pollhup_handled(egc, dc, fd, revents, 0))
-        return;
-
-    if (revents & ~(POLLIN|POLLHUP)) {
-        LOG(ERROR, "unexpected poll event 0x%x on fd %d (expected POLLIN "
-            "and/or POLLHUP) reading %s during copy of %s",
-            revents, fd, dc->readwhat, dc->copywhat);
-        datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
-        return;
-    }
-    assert(revents & (POLLIN|POLLHUP));
-    for (;;) {
-        libxl__datacopier_buf *buf = NULL;
-        int r;
-
-        if (dc->readbuf) {
-            r = read(ev->fd, dc->readbuf + dc->used, dc->bytes_to_read);
-        } else {
-            while (dc->used >= dc->maxsz) {
-                libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs);
-                dc->used -= rm->used;
-                assert(dc->used >= 0);
-                LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry);
-                free(rm);
-            }
-
-            buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs);
-            if (!buf || buf->used >= sizeof(buf->buf)) {
-                buf = libxl__malloc(NOGC, sizeof(*buf));
-                buf->used = 0;
-                LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
-            }
-            r = read(ev->fd, buf->buf + buf->used,
-                     min_t(size_t, sizeof(buf->buf) - buf->used,
-                           (dc->bytes_to_read == -1) ? SIZE_MAX : dc->bytes_to_read));
-        }
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            assert(errno);
-            if (errno == EWOULDBLOCK) {
-                if (revents & POLLHUP) {
-                    LOG(ERROR,
-                        "poll reported HUP but fd read gave EWOULDBLOCK"
-                        " on %s during copy of %s",
-                        dc->readwhat, dc->copywhat);
-                    datacopier_callback(egc, dc, ERROR_FAIL, -1, 0);
-                    return;
-                }
-                break;
-            }
-            LOGE(ERROR, "error reading %s during copy of %s",
-                 dc->readwhat, dc->copywhat);
-            datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
-            return;
-        }
-        if (r == 0) {
-            if (dc->callback_pollhup) {
-                /* It might be that this "eof" is actually a HUP.  If
-                 * the caller cares about the difference,
-                 * double-check using poll(2). */
-                struct pollfd hupchk;
-                hupchk.fd = ev->fd;
-                hupchk.events = POLLIN;
-                hupchk.revents = 0;
-                r = poll(&hupchk, 1, 0);
-                if (r < 0)
-                    LIBXL__EVENT_DISASTER(gc,
-     "unexpected failure polling fd for datacopier eof hup check",
-                                  errno, 0);
-                if (datacopier_pollhup_handled(egc, dc, fd, hupchk.revents, 0))
-                    return;
-            }
-            libxl__ev_fd_deregister(gc, &dc->toread);
-            break;
-        }
-        if (dc->log) {
-            int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log);
-            if (wrote != r) {
-                assert(ferror(dc->log));
-                assert(errno);
-                LOGE(ERROR, "error logging %s", dc->copywhat);
-                datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
-                return;
-            }
-        }
-        if (!dc->readbuf) {
-            buf->used += r;
-            assert(buf->used <= sizeof(buf->buf));
-        }
-        dc->used += r;
-        if (dc->bytes_to_read > 0)
-            dc->bytes_to_read -= r;
-        if (dc->bytes_to_read == 0)
-            break;
-    }
-    datacopier_check_state(egc, dc);
-}
-
-static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
-                                int fd, short events, short revents) {
-    libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, towrite);
-    STATE_AO_GC(dc->ao);
-
-    if (datacopier_pollhup_handled(egc, dc, fd, revents, 1))
-        return;
-
-    if (revents & ~POLLOUT) {
-        LOG(ERROR, "unexpected poll event 0x%x on fd %d (should be POLLOUT)"
-            " writing %s during copy of %s",
-            revents, fd, dc->writewhat, dc->copywhat);
-        datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
-        return;
-    }
-    assert(revents & POLLOUT);
-    for (;;) {
-        libxl__datacopier_buf *buf = LIBXL_TAILQ_FIRST(&dc->bufs);
-        if (!buf)
-            break;
-        if (!buf->used) {
-            LIBXL_TAILQ_REMOVE(&dc->bufs, buf, entry);
-            free(buf);
-            continue;
-        }
-        int r = write(ev->fd, buf->buf, buf->used);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            if (errno == EWOULDBLOCK) break;
-            assert(errno);
-            LOGE(ERROR, "error writing to %s during copy of %s",
-                 dc->writewhat, dc->copywhat);
-            datacopier_callback(egc, dc, ERROR_FAIL, 1, errno);
-            return;
-        }
-        assert(r > 0);
-        assert(r <= buf->used);
-        buf->used -= r;
-        dc->used -= r;
-        assert(dc->used >= 0);
-        memmove(buf->buf, buf->buf+r, buf->used);
-    }
-    datacopier_check_state(egc, dc);
-}
-
-int libxl__datacopier_start(libxl__datacopier_state *dc)
-{
-    int rc;
-    STATE_AO_GC(dc->ao);
-
-    libxl__datacopier_init(dc);
-
-    assert(dc->readfd >= 0 || dc->writefd >= 0);
-    assert(!(dc->readbuf && dc->bytes_to_read == -1));
-
-    dc->abrt.ao = ao;
-    dc->abrt.callback = datacopier_abort;
-    rc = libxl__ao_abortable_register(&dc->abrt);
-    if (rc) goto out;
-
-    if (dc->readfd >= 0) {
-        rc = libxl__ev_fd_register(gc, &dc->toread, datacopier_readable,
-                                   dc->readfd, POLLIN);
-        if (rc) goto out;
-    }
-
-    if (dc->writefd >= 0) {
-        rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
-                                   dc->writefd, POLLOUT);
-        if (rc) goto out;
-    }
-
-    return 0;
-
- out:
-    libxl__datacopier_kill(dc);
-    return rc;
-}
-
-/*----- openpty -----*/
-
-/* implementation */
-    
-static void openpty_cleanup(libxl__openpty_state *op)
-{
-    int i;
-
-    for (i=0; i<op->count; i++) {
-        libxl__openpty_result *res = &op->results[i];
-        libxl__carefd_close(res->master);  res->master = 0;
-        libxl__carefd_close(res->slave);   res->slave = 0;
-    }
-}
-
-static void openpty_exited(libxl__egc *egc, libxl__ev_child *child,
-                           pid_t pid, int status) {
-    libxl__openpty_state *op = CONTAINER_OF(child, *op, child);
-    STATE_AO_GC(op->ao);
-
-    if (status) {
-        /* Perhaps the child gave us the fds and then exited nonzero.
-         * Well that would be odd but we don't really care. */
-        libxl_report_child_exitstatus(CTX, op->rc ? LIBXL__LOG_ERROR
-                                                  : LIBXL__LOG_WARNING,
-                                      "openpty child", pid, status);
-    }
-    if (op->rc)
-        openpty_cleanup(op);
-    op->callback(egc, op);
-}
-
-int libxl__openptys(libxl__openpty_state *op,
-                    struct termios *termp,
-                    struct winsize *winp) {
-    /*
-     * This is completely crazy.  openpty calls grantpt which the spec
-     * says may fork, and may not be called with a SIGCHLD handler.
-     * Now our application may have a SIGCHLD handler so that's bad.
-     * We could perhaps block it but we'd need to block it on all
-     * threads.  This is just Too Hard.
-     *
-     * So instead, we run openpty in a child process.  That child
-     * process then of course has only our own thread and our own
-     * signal handlers.  We pass the fds back.
-     *
-     * Since our only current caller actually wants two ptys, we
-     * support calling openpty multiple times for a single fork.
-     */
-    STATE_AO_GC(op->ao);
-    int count = op->count;
-    int r, i, rc, sockets[2], ptyfds[count][2];
-    libxl__carefd *for_child = 0;
-    pid_t pid = -1;
-
-    for (i=0; i<count; i++) {
-        ptyfds[i][0] = ptyfds[i][1] = -1;
-        libxl__openpty_result *res = &op->results[i];
-        assert(!res->master);
-        assert(!res->slave);
-    }
-    sockets[0] = sockets[1] = -1; /* 0 is for us, 1 for our child */
-
-    libxl__carefd_begin();
-    r = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
-    if (r) { sockets[0] = sockets[1] = -1; }
-    for_child = libxl__carefd_opened(CTX, sockets[1]);
-    if (r) { LOGE(ERROR,"socketpair failed"); rc = ERROR_FAIL; goto out; }
-
-    pid = libxl__ev_child_fork(gc, &op->child, openpty_exited);
-    if (pid == -1) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (!pid) {
-        /* child */
-        close(sockets[0]);
-        signal(SIGCHLD, SIG_DFL);
-
-        for (i=0; i<count; i++) {
-            r = openpty(&ptyfds[i][0], &ptyfds[i][1], NULL, termp, winp);
-            if (r) { LOGE(ERROR,"openpty failed"); _exit(-1); }
-        }
-        rc = libxl__sendmsg_fds(gc, sockets[1], '\0',
-                                2*count, &ptyfds[0][0], "ptys");
-        if (rc) { LOGE(ERROR,"sendmsg to parent failed"); _exit(-1); }
-        _exit(0);
-    }
-
-    libxl__carefd_close(for_child);
-    for_child = 0;
-
-    /* this should be fast so do it synchronously */
-
-    libxl__carefd_begin();
-    char buf[1];
-    rc = libxl__recvmsg_fds(gc, sockets[0], buf,1,
-                            2*count, &ptyfds[0][0], "ptys");
-    if (!rc) {
-        for (i=0; i<count; i++) {
-            libxl__openpty_result *res = &op->results[i];
-            res->master = libxl__carefd_record(CTX, ptyfds[i][0]);
-            res->slave =  libxl__carefd_record(CTX, ptyfds[i][1]);
-        }
-    }
-    /* now the pty fds are in the carefds, if they were ever open */
-    libxl__carefd_unlock();
-    if (rc)
-        goto out;
-
-    rc = 0;
-
- out:
-    if (sockets[0] >= 0) close(sockets[0]);
-    libxl__carefd_close(for_child);
-    if (libxl__ev_child_inuse(&op->child)) {
-        op->rc = rc;
-        /* we will get a callback when the child dies */
-        return 0;
-    }
-
-    assert(rc);
-    openpty_cleanup(op);
-    return rc;
-}
-
-/*----- async exec -----*/
-
-static void async_exec_timeout(libxl__egc *egc,
-                               libxl__ev_time *ev,
-                               const struct timeval *requested_abs,
-                               int rc)
-{
-    libxl__async_exec_state *aes = CONTAINER_OF(ev, *aes, time);
-    STATE_AO_GC(aes->ao);
-
-    if (!aes->rc)
-        aes->rc = rc;
-
-    libxl__ev_time_deregister(gc, &aes->time);
-
-    assert(libxl__ev_child_inuse(&aes->child));
-    LOG(ERROR, "killing execution of %s because of timeout", aes->what);
-
-    if (kill(aes->child.pid, SIGKILL)) {
-        LOGEV(ERROR, errno, "unable to kill %s [%ld]",
-              aes->what, (unsigned long)aes->child.pid);
-    }
-
-    return;
-}
-
-static void async_exec_done(libxl__egc *egc,
-                            libxl__ev_child *child,
-                            pid_t pid, int status)
-{
-    libxl__async_exec_state *aes = CONTAINER_OF(child, *aes, child);
-    STATE_AO_GC(aes->ao);
-
-    libxl__ev_time_deregister(gc, &aes->time);
-
-    if (status) {
-        if (!aes->rc)
-            libxl_report_child_exitstatus(CTX, LIBXL__LOG_ERROR,
-                                          aes->what, pid, status);
-    }
-
-    aes->callback(egc, aes, aes->rc, status);
-}
-
-void libxl__async_exec_init(libxl__async_exec_state *aes)
-{
-    libxl__ev_time_init(&aes->time);
-    libxl__ev_child_init(&aes->child);
-}
-
-int libxl__async_exec_start(libxl__async_exec_state *aes)
-{
-    pid_t pid;
-
-    /* Convenience aliases */
-    libxl__ao *ao = aes->ao;
-    AO_GC;
-    libxl__ev_child *const child = &aes->child;
-    char ** const args = aes->args;
-
-    aes->rc = 0;
-
-    /* Set execution timeout */
-    if (libxl__ev_time_register_rel(ao, &aes->time,
-                                    async_exec_timeout,
-                                    aes->timeout_ms)) {
-        LOG(ERROR, "unable to register timeout for executing: %s", aes->what);
-        goto out;
-    }
-
-    LOG(DEBUG, "forking to execute: %s ", aes->what);
-
-    /* Fork and exec */
-    pid = libxl__ev_child_fork(gc, child, async_exec_done);
-    if (pid == -1) {
-        LOG(ERROR, "unable to fork");
-        goto out;
-    }
-
-    if (!pid) {
-        /* child */
-        libxl__exec(gc, aes->stdfds[0], aes->stdfds[1],
-                    aes->stdfds[2], args[0], args, aes->env);
-    }
-
-    return 0;
-
-out:
-    return ERROR_FAIL;
-}
-
-bool libxl__async_exec_inuse(const libxl__async_exec_state *aes)
-{
-    bool time_inuse = libxl__ev_time_isregistered(&aes->time);
-    bool child_inuse = libxl__ev_child_inuse(&aes->child);
-    assert(time_inuse == child_inuse);
-    return child_inuse;
-}
-
-void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what)
-{
-    int r = kill(pid, sig);
-    if (r) LOGE(WARN, "failed to kill() %s [%lu] (signal %d)",
-                what, (unsigned long)pid, sig);
-}
-
-/* Generic function to signal (HUP) a pid stored in xenstore */
-int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
-                        const char *what)
-{
-    const char *xs_pid;
-    int ret, pid;
-
-    ret = libxl__xs_read_checked(gc, XBT_NULL, xs_path_pid, &xs_pid);
-    if (ret || !xs_pid) {
-        LOG(ERROR, "unable to find %s pid in %s", what, xs_path_pid);
-        ret = ret ? : ERROR_FAIL;
-        goto out;
-    }
-    pid = atoi(xs_pid);
-
-    ret = kill(pid, SIGHUP);
-    if (ret < 0 && errno == ESRCH) {
-        LOG(ERROR, "%s already exited", what);
-        ret = 0;
-    } else if (ret == 0) {
-        LOG(DEBUG, "%s signaled", what);
-        ret = 0;
-    } else {
-        LOGE(ERROR, "failed to kill %s [%d]", what, pid);
-        ret = ERROR_FAIL;
-        goto out;
-    }
-
-out:
-    return ret;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_arch.h b/tools/libxl/libxl_arch.h
deleted file mode 100644 (file)
index 6a91775..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2012      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef LIBXL_ARCH_H
-#define LIBXL_ARCH_H
-
-/* fill the arch specific configuration for the domain */
-_hidden
-int libxl__arch_domain_prepare_config(libxl__gc *gc,
-                                      libxl_domain_config *d_config,
-                                      struct xen_domctl_createdomain *config);
-
-/* save the arch specific configuration for the domain */
-_hidden
-int libxl__arch_domain_save_config(libxl__gc *gc,
-                                   libxl_domain_config *d_config,
-                                   libxl__domain_build_state *state,
-                                   const struct xen_domctl_createdomain *config);
-
-/* arch specific internal domain creation function */
-_hidden
-int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
-               uint32_t domid);
-
-/* setup arch specific hardware description, i.e. DTB on ARM */
-_hidden
-int libxl__arch_domain_init_hw_description(libxl__gc *gc,
-                                           libxl_domain_build_info *info,
-                                           libxl__domain_build_state *state,
-                                           struct xc_dom_image *dom);
-/* finalize arch specific hardware description. */
-_hidden
-int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
-                                      uint32_t domid,
-                                      libxl_domain_config *d_config,
-                                      struct xc_dom_image *dom);
-
-/* perform any pending hardware initialization */
-_hidden
-int libxl__arch_build_dom_finish(libxl__gc *gc,
-                                 libxl_domain_build_info *info,
-                                 struct xc_dom_image *dom,
-                                 libxl__domain_build_state *state);
-
-/* build vNUMA vmemrange with arch specific information */
-_hidden
-int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
-                                      uint32_t domid,
-                                      libxl_domain_build_info *b_info,
-                                      libxl__domain_build_state *state);
-
-/* arch specific irq map function */
-_hidden
-int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq);
-
-_hidden
-void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
-                                               libxl_domain_create_info *c_info);
-
-_hidden
-void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
-                                              libxl_domain_build_info *b_info);
-
-_hidden
-int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
-                                     uint32_t domid /* for logging, only */,
-                                            libxl_domain_config *d_config,
-                                            const libxl_physinfo *physinfo);
-
-_hidden
-int libxl__arch_extra_memory(libxl__gc *gc,
-                             const libxl_domain_build_info *info,
-                             uint64_t *out);
-
-#if defined(__i386__) || defined(__x86_64__)
-
-#define LAPIC_BASE_ADDRESS  0xfee00000
-#define ACPI_INFO_PHYSICAL_ADDRESS 0xfc000000
-
-int libxl__dom_load_acpi(libxl__gc *gc,
-                         const libxl_domain_build_info *b_info,
-                         struct xc_dom_image *dom);
-#endif
-
-#endif
diff --git a/tools/libxl/libxl_arm.c b/tools/libxl/libxl_arm.c
deleted file mode 100644 (file)
index 975a4d7..0000000
+++ /dev/null
@@ -1,1230 +0,0 @@
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-#include "libxl_libfdt_compat.h"
-#include "libxl_arm.h"
-
-#include <xenctrl_dom.h>
-#include <stdbool.h>
-#include <libfdt.h>
-#include <assert.h>
-#include <xen/device_tree_defs.h>
-
-static const char *gicv_to_string(libxl_gic_version gic_version)
-{
-    switch (gic_version) {
-    case LIBXL_GIC_VERSION_V2:
-        return "V2";
-    case LIBXL_GIC_VERSION_V3:
-        return "V3";
-    default:
-        return "unknown";
-    }
-}
-
-int libxl__arch_domain_prepare_config(libxl__gc *gc,
-                                      libxl_domain_config *d_config,
-                                      struct xen_domctl_createdomain *config)
-{
-    uint32_t nr_spis = 0;
-    unsigned int i;
-    uint32_t vuart_irq;
-    bool vuart_enabled = false;
-
-    /*
-     * If pl011 vuart is enabled then increment the nr_spis to allow allocation
-     * of SPI VIRQ for pl011.
-     */
-    if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
-        nr_spis += (GUEST_VPL011_SPI - 32) + 1;
-        vuart_irq = GUEST_VPL011_SPI;
-        vuart_enabled = true;
-    }
-
-    for (i = 0; i < d_config->b_info.num_irqs; i++) {
-        uint32_t irq = d_config->b_info.irqs[i];
-        uint32_t spi;
-
-        /*
-         * This check ensures the if user has requested pass-through of a certain irq
-         * which conflicts with vpl011 irq then it flags an error to indicate to the
-         * user that the specific HW irq cannot be used as it is dedicated for vpl011.
-         * 
-         * TODO:
-         * The vpl011 irq should be assigned such that it never conflicts with user
-         * specified irqs thereby preventing its pass-through. This TODO is for
-         * implementing that logic in future.
-         */
-        if (vuart_enabled && irq == vuart_irq) {
-            LOG(ERROR, "Physical IRQ %u conflicting with pl011 SPI\n", irq);
-            return ERROR_FAIL;
-        }
-
-        if (irq < 32)
-            continue;
-
-        spi = irq - 32;
-
-        if (nr_spis <= spi)
-            nr_spis = spi + 1;
-    }
-
-    LOG(DEBUG, "Configure the domain");
-
-    config->arch.nr_spis = nr_spis;
-    LOG(DEBUG, " - Allocate %u SPIs", nr_spis);
-
-    switch (d_config->b_info.arch_arm.gic_version) {
-    case LIBXL_GIC_VERSION_DEFAULT:
-        config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE;
-        break;
-    case LIBXL_GIC_VERSION_V2:
-        config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V2;
-        break;
-    case LIBXL_GIC_VERSION_V3:
-        config->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_V3;
-        break;
-    default:
-        LOG(ERROR, "Unknown GIC version %d",
-            d_config->b_info.arch_arm.gic_version);
-        return ERROR_FAIL;
-    }
-
-    switch (d_config->b_info.tee) {
-    case LIBXL_TEE_TYPE_NONE:
-        config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_NONE;
-        break;
-    case LIBXL_TEE_TYPE_OPTEE:
-        config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_OPTEE;
-        break;
-    default:
-        LOG(ERROR, "Unknown TEE type %d",
-            d_config->b_info.tee);
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-int libxl__arch_domain_save_config(libxl__gc *gc,
-                                   libxl_domain_config *d_config,
-                                   libxl__domain_build_state *state,
-                                   const struct xen_domctl_createdomain *config)
-{
-    switch (config->arch.gic_version) {
-    case XEN_DOMCTL_CONFIG_GIC_V2:
-        d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V2;
-        break;
-    case XEN_DOMCTL_CONFIG_GIC_V3:
-        d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V3;
-        break;
-    default:
-        LOG(ERROR, "Unexpected gic version %u", config->arch.gic_version);
-        return ERROR_FAIL;
-    }
-
-    state->clock_frequency = config->arch.clock_frequency;
-
-    return 0;
-}
-
-int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
-                              uint32_t domid)
-{
-    return 0;
-}
-
-int libxl__arch_extra_memory(libxl__gc *gc,
-                             const libxl_domain_build_info *info,
-                             uint64_t *out)
-{
-    int rc = 0;
-    uint64_t size = 0;
-
-    if (libxl_defbool_val(info->acpi)) {
-        rc = libxl__get_acpi_size(gc, info, &size);
-        if (rc < 0) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    *out = LIBXL_MAXMEM_CONSTANT + DIV_ROUNDUP(size, 1024);
-out:
-    return rc;
-}
-
-static struct arch_info {
-    const char *guest_type;
-    const char *timer_compat;
-    const char *cpu_compat;
-} arch_info[] = {
-    {"xen-3.0-armv7l",  "arm,armv7-timer", "arm,cortex-a15" },
-    {"xen-3.0-aarch64", "arm,armv8-timer", "arm,armv8" },
-};
-
-typedef uint32_t be32;
-typedef be32 gic_interrupt[3];
-
-#define PROP_INITRD_START "linux,initrd-start"
-#define PROP_INITRD_END "linux,initrd-end"
-
-static void set_cell(be32 **cellp, int size, uint64_t val)
-{
-    int cells = size;
-
-    while (size--) {
-        (*cellp)[size] = cpu_to_fdt32(val);
-        val >>= 32;
-    }
-
-    (*cellp) += cells;
-}
-
-static void set_interrupt(gic_interrupt interrupt, unsigned int irq,
-                          unsigned int cpumask, unsigned int level)
-{
-    be32 *cells = interrupt;
-    int is_ppi = (irq < 32);
-
-    /* SGIs are not describe in the device tree */
-    assert(irq >= 16);
-
-    irq -= (is_ppi) ? 16: 32; /* PPIs start at 16, SPIs at 32 */
-
-    /* See linux Documentation/devictree/bindings/arm/gic.txt */
-    set_cell(&cells, 1, is_ppi); /* is a PPI? */
-    set_cell(&cells, 1, irq);
-    set_cell(&cells, 1, (cpumask << 8) | level);
-}
-
-static void set_range(be32 **cellp,
-                      int address_cells, int size_cells,
-                      uint64_t address, uint64_t size)
-{
-    set_cell(cellp, address_cells, address);
-    set_cell(cellp, size_cells, size);
-}
-
-static int fdt_property_compat(libxl__gc *gc, void *fdt, unsigned nr_compat, ...)
-{
-    const char *compats[nr_compat];
-    int i;
-    size_t sz;
-    va_list ap;
-    char *compat, *p;
-
-    va_start(ap, nr_compat);
-    sz = 0;
-    for (i = 0; i < nr_compat; i++) {
-        const char *c = va_arg(ap, const char *);
-        compats[i] = c;
-        sz += strlen(compats[i]) + 1;
-    }
-    va_end(ap);
-
-    p = compat = libxl__zalloc(gc, sz);
-    for (i = 0; i < nr_compat; i++) {
-        strcpy(p, compats[i]);
-        p += strlen(compats[i]) + 1;
-    }
-
-    return fdt_property(fdt, "compatible", compat, sz);
-}
-
-static int fdt_property_interrupts(libxl__gc *gc, void *fdt,
-                                   gic_interrupt *intr,
-                                   unsigned num_irq)
-{
-    int res;
-
-    res = fdt_property(fdt, "interrupts", intr, sizeof (intr[0]) * num_irq);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
-    if (res) return res;
-
-    return 0;
-}
-
-static int fdt_property_regs(libxl__gc *gc, void *fdt,
-                             unsigned addr_cells,
-                             unsigned size_cells,
-                             unsigned num_regs, ...)
-{
-    uint32_t regs[num_regs*(addr_cells+size_cells)];
-    be32 *cells = &regs[0];
-    int i;
-    va_list ap;
-    uint64_t base, size;
-
-    va_start(ap, num_regs);
-    for (i = 0 ; i < num_regs; i++) {
-        base = addr_cells ? va_arg(ap, uint64_t) : 0;
-        size = size_cells ? va_arg(ap, uint64_t) : 0;
-        set_range(&cells, addr_cells, size_cells, base, size);
-    }
-    va_end(ap);
-
-    return fdt_property(fdt, "reg", regs, sizeof(regs));
-}
-
-static int make_root_properties(libxl__gc *gc,
-                                const libxl_version_info *vers,
-                                void *fdt)
-{
-    int res;
-
-    res = fdt_property_string(fdt, "model", GCSPRINTF("XENVM-%d.%d",
-                                                      vers->xen_version_major,
-                                                      vers->xen_version_minor));
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 2,
-                              GCSPRINTF("xen,xenvm-%d.%d",
-                                        vers->xen_version_major,
-                                        vers->xen_version_minor),
-                              "xen,xenvm");
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "interrupt-parent", GUEST_PHANDLE_GIC);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "#address-cells", GUEST_ROOT_ADDRESS_CELLS);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "#size-cells", GUEST_ROOT_SIZE_CELLS);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_chosen_node(libxl__gc *gc, void *fdt, bool ramdisk,
-                            libxl__domain_build_state *state,
-                            const libxl_domain_build_info *info)
-{
-    int res;
-
-    /* See linux Documentation/devicetree/... */
-    res = fdt_begin_node(fdt, "chosen");
-    if (res) return res;
-
-    if (state->pv_cmdline) {
-        LOG(DEBUG, "/chosen/bootargs = %s", state->pv_cmdline);
-        res = fdt_property_string(fdt, "bootargs", state->pv_cmdline);
-        if (res) return res;
-    }
-
-    if (ramdisk) {
-        uint64_t dummy = 0;
-        LOG(DEBUG, "/chosen adding placeholder linux,initrd properties");
-        res = fdt_property(fdt, PROP_INITRD_START, &dummy, sizeof(dummy));
-        if (res) return res;
-        res = fdt_property(fdt, PROP_INITRD_END, &dummy, sizeof(dummy));
-        if (res) return res;
-    }
-
-    if (libxl_defbool_val(info->acpi)) {
-        const uint64_t acpi_base = GUEST_ACPI_BASE;
-        const char *name = GCSPRINTF("module@%"PRIx64, acpi_base);
-
-        res = fdt_begin_node(fdt, name);
-        if (res) return res;
-
-        res = fdt_property_compat(gc, fdt, 2, "xen,guest-acpi",
-                                  "multiboot,module");
-        if (res) return res;
-
-        res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
-                                1, 0, 0);
-        if (res) return res;
-
-        res = fdt_end_node(fdt);
-        if (res) return res;
-    }
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_cpus_node(libxl__gc *gc, void *fdt, int nr_cpus,
-                          const struct arch_info *ainfo)
-{
-    int res, i;
-    uint64_t mpidr_aff;
-
-    res = fdt_begin_node(fdt, "cpus");
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "#address-cells", 1);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "#size-cells", 0);
-    if (res) return res;
-
-    for (i = 0; i < nr_cpus; i++) {
-        const char *name;
-
-        mpidr_aff = libxl__compute_mpdir(i);
-        name = GCSPRINTF("cpu@%"PRIx64, mpidr_aff);
-
-        res = fdt_begin_node(fdt, name);
-        if (res) return res;
-
-        res = fdt_property_string(fdt, "device_type", "cpu");
-        if (res) return res;
-
-        res = fdt_property_compat(gc, fdt, 1, ainfo->cpu_compat);
-        if (res) return res;
-
-        res = fdt_property_string(fdt, "enable-method", "psci");
-        if (res) return res;
-
-        res = fdt_property_regs(gc, fdt, 1, 0, 1, mpidr_aff);
-        if (res) return res;
-
-        res = fdt_end_node(fdt);
-        if (res) return res;
-    }
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_psci_node(libxl__gc *gc, void *fdt)
-{
-    int res;
-
-    res = fdt_begin_node(fdt, "psci");
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 3, "arm,psci-1.0",
-                              "arm,psci-0.2", "arm,psci");
-    if (res) return res;
-
-    res = fdt_property_string(fdt, "method", "hvc");
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "cpu_off", PSCI_cpu_off);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "cpu_on", PSCI_cpu_on);
-    if (res) return res;
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_optee_node(libxl__gc *gc, void *fdt)
-{
-    int res;
-    LOG(DEBUG, "Creating OP-TEE node in dtb");
-
-    res = fdt_begin_node(fdt, "firmware");
-    if (res) return res;
-
-    res = fdt_begin_node(fdt, "optee");
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 1, "linaro,optee-tz");
-    if (res) return res;
-
-    res = fdt_property_string(fdt, "method", "hvc");
-    if (res) return res;
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_memory_nodes(libxl__gc *gc, void *fdt,
-                             const struct xc_dom_image *dom)
-{
-    int res, i;
-    const char *name;
-    const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
-
-    for (i = 0; i < GUEST_RAM_BANKS; i++) {
-        name = GCSPRINTF("memory@%"PRIx64, bankbase[i]);
-
-        LOG(DEBUG, "Creating placeholder node /%s", name);
-
-        res = fdt_begin_node(fdt, name);
-        if (res) return res;
-
-        res = fdt_property_string(fdt, "device_type", "memory");
-        if (res) return res;
-
-        res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
-                                1, 0, 0);
-        if (res) return res;
-
-        res = fdt_end_node(fdt);
-        if (res) return res;
-    }
-
-    return 0;
-}
-
-static int make_gicv2_node(libxl__gc *gc, void *fdt,
-                           uint64_t gicd_base, uint64_t gicd_size,
-                           uint64_t gicc_base, uint64_t gicc_size)
-{
-    int res;
-    const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
-
-    res = fdt_begin_node(fdt, name);
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 2,
-                              "arm,cortex-a15-gic",
-                              "arm,cortex-a9-gic");
-    if (res) return res;
-
-
-    res = fdt_property_cell(fdt, "#interrupt-cells", 3);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "#address-cells", 0);
-    if (res) return res;
-
-    res = fdt_property(fdt, "interrupt-controller", NULL, 0);
-    if (res) return res;
-
-    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
-                            2,
-                            gicd_base, gicd_size,
-                            gicc_base, gicc_size);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
-    if (res) return res;
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_gicv3_node(libxl__gc *gc, void *fdt)
-{
-    int res;
-    const uint64_t gicd_base = GUEST_GICV3_GICD_BASE;
-    const uint64_t gicd_size = GUEST_GICV3_GICD_SIZE;
-    const uint64_t gicr0_base = GUEST_GICV3_GICR0_BASE;
-    const uint64_t gicr0_size = GUEST_GICV3_GICR0_SIZE;
-    const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
-
-    res = fdt_begin_node(fdt, name);
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 1, "arm,gic-v3");
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "#interrupt-cells", 3);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "#address-cells", 0);
-    if (res) return res;
-
-    res = fdt_property(fdt, "interrupt-controller", NULL, 0);
-    if (res) return res;
-
-    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
-                            2,
-                            gicd_base, gicd_size,
-                            gicr0_base, gicr0_size);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "linux,phandle", GUEST_PHANDLE_GIC);
-    if (res) return res;
-
-    res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_GIC);
-    if (res) return res;
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_timer_node(libxl__gc *gc, void *fdt,
-                           const struct arch_info *ainfo,
-                           uint32_t frequency)
-{
-    int res;
-    gic_interrupt ints[3];
-
-    res = fdt_begin_node(fdt, "timer");
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 1, ainfo->timer_compat);
-    if (res) return res;
-
-    set_interrupt(ints[0], GUEST_TIMER_PHYS_S_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
-    set_interrupt(ints[1], GUEST_TIMER_PHYS_NS_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
-    set_interrupt(ints[2], GUEST_TIMER_VIRT_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
-
-    res = fdt_property_interrupts(gc, fdt, ints, 3);
-    if (res) return res;
-
-    if ( frequency )
-        fdt_property_u32(fdt, "clock-frequency", frequency);
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_hypervisor_node(libxl__gc *gc, void *fdt,
-                                const libxl_version_info *vers)
-{
-    int res;
-    gic_interrupt intr;
-
-    /* See linux Documentation/devicetree/bindings/arm/xen.txt */
-    res = fdt_begin_node(fdt, "hypervisor");
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 2,
-                              GCSPRINTF("xen,xen-%d.%d",
-                                        vers->xen_version_major,
-                                        vers->xen_version_minor),
-                              "xen,xen");
-    if (res) return res;
-
-    /* reg 0 is grant table space */
-    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
-                            1,GUEST_GNTTAB_BASE, GUEST_GNTTAB_SIZE);
-    if (res) return res;
-
-    /*
-     * interrupts is evtchn upcall:
-     *  - Active-low level-sensitive
-     *  - All cpus
-     */
-    set_interrupt(intr, GUEST_EVTCHN_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
-
-    res = fdt_property_interrupts(gc, fdt, &intr, 1);
-    if (res) return res;
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static int make_vpl011_uart_node(libxl__gc *gc, void *fdt,
-                                 const struct arch_info *ainfo,
-                                 struct xc_dom_image *dom)
-{
-    int res;
-    gic_interrupt intr;
-
-    res = fdt_begin_node(fdt, "sbsa-pl011");
-    if (res) return res;
-
-    res = fdt_property_compat(gc, fdt, 1, "arm,sbsa-uart");
-    if (res) return res;
-
-    res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
-                            1,
-                            GUEST_PL011_BASE, GUEST_PL011_SIZE);
-    if (res) return res;
-
-    set_interrupt(intr, GUEST_VPL011_SPI, 0xf, DT_IRQ_TYPE_LEVEL_HIGH);
-
-    res = fdt_property_interrupts(gc, fdt, &intr, 1);
-    if (res) return res;
-
-    /* Use a default baud rate of 115200. */
-    fdt_property_u32(fdt, "current-speed", 115200);
-
-    res = fdt_end_node(fdt);
-    if (res) return res;
-
-    return 0;
-}
-
-static const struct arch_info *get_arch_info(libxl__gc *gc,
-                                             const struct xc_dom_image *dom)
-{
-    int i;
-
-    for (i=0; i < ARRAY_SIZE(arch_info); i++) {
-        const struct arch_info *info = &arch_info[i];
-        if (!strcmp(dom->guest_type, info->guest_type))
-            return info;
-    }
-    LOG(ERROR, "Unable to find arch FDT info for %s", dom->guest_type);
-    return NULL;
-}
-
-static void debug_dump_fdt(libxl__gc *gc, void *fdt)
-{
-    int fd = -1, rc, r;
-
-    const char *dtb = getenv("LIBXL_DEBUG_DUMP_DTB");
-
-    if (!dtb) goto out;
-
-    fd = open(dtb, O_CREAT|O_TRUNC|O_WRONLY, 0666);
-    if (fd < 0) {
-        LOGE(DEBUG, "cannot open %s for LIBXL_DEBUG_DUMP_DTB", dtb);
-        goto out;
-    }
-
-    rc = libxl_write_exactly(CTX, fd, fdt, fdt_totalsize(fdt), dtb, "dtb");
-    if (rc < 0) goto out;
-
-out:
-    if (fd >= 0) {
-        r = close(fd);
-        if (r < 0) LOGE(DEBUG, "failed to close DTB debug dump output");
-    }
-}
-
-#ifdef ENABLE_PARTIAL_DEVICE_TREE
-
-static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
-{
-    int r;
-
-    if (fdt_magic(fdt) != FDT_MAGIC) {
-        LOG(ERROR, "Partial FDT is not a valid Flat Device Tree");
-        return ERROR_FAIL;
-    }
-
-    r = fdt_check_header(fdt);
-    if (r) {
-        LOG(ERROR, "Failed to check the partial FDT (%d)", r);
-        return ERROR_FAIL;
-    }
-
-    if (fdt_totalsize(fdt) > size) {
-        LOG(ERROR, "Partial FDT totalsize is too big");
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-static int copy_properties(libxl__gc *gc, void *fdt, void *pfdt,
-                           int nodeoff)
-{
-    int propoff, nameoff, r;
-    const struct fdt_property *prop;
-
-    for (propoff = fdt_first_property_offset(pfdt, nodeoff);
-         propoff >= 0;
-         propoff = fdt_next_property_offset(pfdt, propoff)) {
-
-        if (!(prop = fdt_get_property_by_offset(pfdt, propoff, NULL))) {
-            return -FDT_ERR_INTERNAL;
-        }
-
-        nameoff = fdt32_to_cpu(prop->nameoff);
-        r = fdt_property(fdt, fdt_string(pfdt, nameoff),
-                         prop->data, fdt32_to_cpu(prop->len));
-        if (r) return r;
-    }
-
-    /* FDT_ERR_NOTFOUND => There is no more properties for this node */
-    return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
-}
-
-/* Copy a node from the partial device tree to the guest device tree */
-static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
-                     int nodeoff, int depth)
-{
-    int r;
-
-    r = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
-    if (r) return r;
-
-    r = copy_properties(gc, fdt, pfdt, nodeoff);
-    if (r) return r;
-
-    for (nodeoff = fdt_first_subnode(pfdt, nodeoff);
-         nodeoff >= 0;
-         nodeoff = fdt_next_subnode(pfdt, nodeoff)) {
-        r = copy_node(gc, fdt, pfdt, nodeoff, depth + 1);
-        if (r) return r;
-    }
-
-    if (nodeoff != -FDT_ERR_NOTFOUND)
-        return nodeoff;
-
-    r = fdt_end_node(fdt);
-    if (r) return r;
-
-    return 0;
-}
-
-static int copy_node_by_path(libxl__gc *gc, const char *path,
-                             void *fdt, void *pfdt)
-{
-    int nodeoff, r;
-    const char *name = strrchr(path, '/');
-
-    if (!name)
-        return -FDT_ERR_INTERNAL;
-
-    name++;
-
-    /*
-     * The FDT function to look at a node doesn't take into account the
-     * unit (i.e anything after @) when search by name. Check if the
-     * name exactly matches.
-     */
-    nodeoff = fdt_path_offset(pfdt, path);
-    if (nodeoff < 0)
-        return nodeoff;
-
-    if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
-        return -FDT_ERR_NOTFOUND;
-
-    r = copy_node(gc, fdt, pfdt, nodeoff, 0);
-    if (r) return r;
-
-    return 0;
-}
-
-/*
- * The partial device tree is not copied entirely. Only the relevant bits are
- * copied to the guest device tree:
- *  - /passthrough node
- *  - /aliases node
- */
-static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
-{
-    int r;
-
-    r = copy_node_by_path(gc, "/passthrough", fdt, pfdt);
-    if (r < 0) {
-        LOG(ERROR, "Can't copy the node \"/passthrough\" from the partial FDT");
-        return r;
-    }
-
-    r = copy_node_by_path(gc, "/aliases", fdt, pfdt);
-    if (r < 0 && r != -FDT_ERR_NOTFOUND) {
-        LOG(ERROR, "Can't copy the node \"/aliases\" from the partial FDT");
-        return r;
-    }
-
-    return 0;
-}
-
-#else
-
-static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
-{
-    LOG(ERROR, "partial device tree not supported");
-
-    return ERROR_FAIL;
-}
-
-static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
-{
-    /*
-     * We should never be here when the partial device tree is not
-     * supported.
-     * */
-    return -FDT_ERR_INTERNAL;
-}
-
-#endif /* ENABLE_PARTIAL_DEVICE_TREE */
-
-#define FDT_MAX_SIZE (1<<20)
-
-static int libxl__prepare_dtb(libxl__gc *gc, libxl_domain_build_info *info,
-                              libxl__domain_build_state *state,
-                              struct xc_dom_image *dom)
-{
-    void *fdt = NULL;
-    void *pfdt = NULL;
-    int rc, res;
-    size_t fdt_size = 0;
-    int pfdt_size = 0;
-
-    const libxl_version_info *vers;
-    const struct arch_info *ainfo;
-
-    vers = libxl_get_version_info(CTX);
-    if (vers == NULL) return ERROR_FAIL;
-
-    ainfo = get_arch_info(gc, dom);
-    if (ainfo == NULL) return ERROR_FAIL;
-
-    LOG(DEBUG, "constructing DTB for Xen version %d.%d guest",
-        vers->xen_version_major, vers->xen_version_minor);
-    LOG(DEBUG, " - vGIC version: %s",
-        gicv_to_string(info->arch_arm.gic_version));
-
-    if (info->device_tree) {
-        LOG(DEBUG, " - Partial device tree provided: %s", info->device_tree);
-
-        rc = libxl_read_file_contents(CTX, info->device_tree,
-                                      &pfdt, &pfdt_size);
-        if (rc) {
-            LOGEV(ERROR, rc, "failed to read the partial device file %s",
-                  info->device_tree);
-            return ERROR_FAIL;
-        }
-        libxl__ptr_add(gc, pfdt);
-
-        if (check_partial_fdt(gc, pfdt, pfdt_size))
-            return ERROR_FAIL;
-    }
-
-/*
- * Call "call" handling FDT_ERR_*. Will either:
- * - loop back to retry_resize
- * - set rc and goto out
- * - fall through successfully
- *
- * On FDT_ERR_NOSPACE we start again from scratch rather than
- * realloc+libfdt_open_into because "call" may have failed half way
- * through a series of steps leaving the partial tree in an
- * inconsistent state, e.g. leaving a node open.
- */
-#define FDT( call ) do {                                        \
-    int fdt_res = (call);                                       \
-    if (fdt_res == -FDT_ERR_NOSPACE && fdt_size < FDT_MAX_SIZE) \
-        goto next_resize;                                       \
-    else if (fdt_res < 0) {                                     \
-        LOG(ERROR, "FDT: %s failed: %d = %s",                   \
-            #call, fdt_res, fdt_strerror(fdt_res));             \
-        rc = ERROR_FAIL;                                        \
-        goto out;                                               \
-    }                                                           \
-} while(0)
-
-    for (;;) {
-next_resize:
-        if (fdt_size) {
-            fdt_size <<= 1;
-            LOG(DEBUG, "Increasing FDT size to %zd and retrying", fdt_size);
-        } else {
-            fdt_size = 4096;
-        }
-
-        fdt = libxl__realloc(gc, fdt, fdt_size);
-
-        FDT( fdt_create(fdt, fdt_size) );
-
-        FDT( fdt_finish_reservemap(fdt) );
-
-        FDT( fdt_begin_node(fdt, "") );
-
-        FDT( make_root_properties(gc, vers, fdt) );
-        FDT( make_chosen_node(gc, fdt, !!dom->modules[0].blob, state, info) );
-        FDT( make_cpus_node(gc, fdt, info->max_vcpus, ainfo) );
-        FDT( make_psci_node(gc, fdt) );
-
-        FDT( make_memory_nodes(gc, fdt, dom) );
-
-        switch (info->arch_arm.gic_version) {
-        case LIBXL_GIC_VERSION_V2:
-            FDT( make_gicv2_node(gc, fdt,
-                                 GUEST_GICD_BASE, GUEST_GICD_SIZE,
-                                 GUEST_GICC_BASE, GUEST_GICC_SIZE) );
-            break;
-        case LIBXL_GIC_VERSION_V3:
-            FDT( make_gicv3_node(gc, fdt) );
-            break;
-        default:
-            LOG(ERROR, "Unknown GIC version %s",
-                gicv_to_string(info->arch_arm.gic_version));
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        FDT( make_timer_node(gc, fdt, ainfo, state->clock_frequency) );
-        FDT( make_hypervisor_node(gc, fdt, vers) );
-
-        if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
-            FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
-
-        if (info->tee == LIBXL_TEE_TYPE_OPTEE)
-            FDT( make_optee_node(gc, fdt) );
-
-        if (pfdt)
-            FDT( copy_partial_fdt(gc, fdt, pfdt) );
-
-        FDT( fdt_end_node(fdt) );
-
-        FDT( fdt_finish(fdt) );
-        break;
-    }
-#undef FDT
-
-    LOG(DEBUG, "fdt total size %d", fdt_totalsize(fdt));
-
-    res = xc_dom_devicetree_mem(dom, fdt, fdt_totalsize(fdt));
-    if (res) {
-        LOGE(ERROR, "xc_dom_devicetree_mem failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-int libxl__arch_domain_init_hw_description(libxl__gc *gc,
-                                           libxl_domain_build_info *info,
-                                           libxl__domain_build_state *state,
-                                           struct xc_dom_image *dom)
-{
-    int rc;
-    uint64_t val;
-
-    if (info->type != LIBXL_DOMAIN_TYPE_PVH) {
-        LOG(ERROR, "Unsupported Arm guest type %s",
-            libxl_domain_type_to_string(info->type));
-        return ERROR_INVAL;
-    }
-
-    /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ. */
-    val = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI,
-                    HVM_PARAM_CALLBACK_IRQ_TYPE_MASK);
-    /* Active-low level-sensitive  */
-    val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL,
-                     HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK);
-    val |= GUEST_EVTCHN_PPI;
-    rc = xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CALLBACK_IRQ,
-                          val);
-    if (rc)
-        return rc;
-
-    rc = libxl__prepare_dtb(gc, info, state, dom);
-    if (rc) goto out;
-
-    if (!libxl_defbool_val(info->acpi)) {
-        LOG(DEBUG, "Generating ACPI tables is disabled by user.");
-        rc = 0;
-        goto out;
-    }
-
-    if (strcmp(dom->guest_type, "xen-3.0-aarch64")) {
-        /* ACPI is only supported for 64-bit guest currently. */
-        LOG(ERROR, "Can not enable libxl option 'acpi' for %s", dom->guest_type);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__prepare_acpi(gc, info, dom);
-
-out:
-    return rc;
-}
-
-static void finalise_one_node(libxl__gc *gc, void *fdt, const char *uname,
-                              uint64_t base, uint64_t size)
-{
-    int node, res;
-    const char *name = GCSPRINTF("%s@%"PRIx64, uname, base);
-
-    node = fdt_path_offset(fdt, name);
-    assert(node > 0);
-
-    if (size == 0) {
-        LOG(DEBUG, "Nopping out placeholder node %s", name);
-        fdt_nop_node(fdt, node);
-    } else {
-        uint32_t regs[GUEST_ROOT_ADDRESS_CELLS+GUEST_ROOT_SIZE_CELLS];
-        be32 *cells = &regs[0];
-
-        LOG(DEBUG, "Populating placeholder node %s", name);
-
-        set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS, base, size);
-
-        res = fdt_setprop_inplace(fdt, node, "reg", regs, sizeof(regs));
-        assert(!res);
-    }
-}
-
-int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
-                                               uint32_t domid,
-                                               libxl_domain_config *d_config,
-                                               struct xc_dom_image *dom)
-{
-    void *fdt = dom->devicetree_blob;
-    int i;
-    const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
-
-    const struct xc_dom_seg *ramdisk = dom->modules[0].blob ?
-        &dom->modules[0].seg : NULL;
-
-    if (ramdisk) {
-        int chosen, res;
-        uint64_t val;
-
-        /* Neither the fdt_path_offset() nor either of the
-         * fdt_setprop_inplace() calls can fail. If they do then
-         * make_chosen_node() (see above) has got something very
-         * wrong.
-         */
-        chosen = fdt_path_offset(fdt, "/chosen");
-        assert(chosen > 0);
-
-        LOG(DEBUG, "/chosen updating initrd properties to cover "
-            "%"PRIx64"-%"PRIx64,
-            ramdisk->vstart, ramdisk->vend);
-
-        val = cpu_to_fdt64(ramdisk->vstart);
-        res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_START,
-                                  &val, sizeof(val));
-        assert(!res);
-
-        val = cpu_to_fdt64(ramdisk->vend);
-        res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_END,
-                                  &val, sizeof(val));
-        assert(!res);
-
-    }
-
-    for (i = 0; i < GUEST_RAM_BANKS; i++) {
-        const uint64_t size = (uint64_t)dom->rambank_size[i] << XC_PAGE_SHIFT;
-
-        finalise_one_node(gc, fdt, "/memory", bankbase[i], size);
-    }
-
-    if (dom->acpi_modules[0].data) {
-        finalise_one_node(gc, fdt, "/chosen/module", GUEST_ACPI_BASE,
-                          dom->acpi_modules[0].length);
-    }
-
-    debug_dump_fdt(gc, fdt);
-
-    return 0;
-}
-
-int libxl__arch_build_dom_finish(libxl__gc *gc,
-                                 libxl_domain_build_info *info,
-                                 struct xc_dom_image *dom,
-                                 libxl__domain_build_state *state)
-{
-    int rc = 0, ret;
-
-    if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
-        rc = 0;
-        goto out;
-    }
-
-    ret = xc_dom_vuart_init(CTX->xch,
-                            XEN_DOMCTL_VUART_TYPE_VPL011,
-                            dom->guest_domid,
-                            dom->console_domid,
-                            dom->vuart_gfn,
-                            &state->vuart_port);
-    if (ret < 0) {
-        rc = ERROR_FAIL;
-        LOG(ERROR, "xc_dom_vuart_init failed\n");
-    }
-
-out:
-    return rc;
-}
-
-int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
-                                      uint32_t domid,
-                                      libxl_domain_build_info *info,
-                                      libxl__domain_build_state *state)
-{
-    return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, info, state);
-}
-
-int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
-{
-    return xc_domain_bind_pt_spi_irq(CTX->xch, domid, irq, irq);
-}
-
-void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
-                                               libxl_domain_create_info *c_info)
-{
-    /*
-     * Arm guest are now considered as PVH by the toolstack. To allow
-     * compatibility with previous toolstack, PV guest are automatically
-     * converted to PVH.
-     */
-    if (c_info->type == LIBXL_DOMAIN_TYPE_PV) {
-        LOG(WARN, "Converting PV guest to PVH.");
-        LOG(WARN, "Arm guest are now PVH.");
-        LOG(WARN, "Please fix your configuration file/toolstack.");
-
-        c_info->type = LIBXL_DOMAIN_TYPE_PVH;
-        /* All other fields can remain untouched */
-    }
-}
-
-void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
-                                              libxl_domain_build_info *b_info)
-{
-    /* ACPI is disabled by default */
-    libxl_defbool_setdefault(&b_info->acpi, false);
-
-    if (b_info->type != LIBXL_DOMAIN_TYPE_PV)
-        return;
-
-    LOG(DEBUG, "Converting build_info to PVH");
-
-    /* Re-initialize type to PVH and all associated fields to defaults. */
-    memset(&b_info->u, '\0', sizeof(b_info->u));
-    b_info->type = LIBXL_DOMAIN_TYPE_INVALID;
-    libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_PVH);
-}
-
-int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
-                                            uint32_t domid,
-                                            libxl_domain_config *d_config,
-                                            const libxl_physinfo *physinfo)
-{
-    int rc;
-    libxl_domain_create_info *const c_info = &d_config->c_info;
-
-    if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
-        c_info->passthrough = LIBXL_PASSTHROUGH_SHARE_PT;
-    }
-
-    switch (c_info->passthrough) {
-    case LIBXL_PASSTHROUGH_DISABLED:
-    case LIBXL_PASSTHROUGH_SHARE_PT:
-        break;
-
-    default:
-        LOGD(ERROR, domid,
-             "passthrough=\"%s\" not supported on ARM\n",
-             libxl_passthrough_to_string(c_info->passthrough));
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    rc = 0;
- out:
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_arm.h b/tools/libxl/libxl_arm.h
deleted file mode 100644 (file)
index 52c2ab5..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2016      Linaro Ltd.
- *
- * Author: Shannon Zhao <shannon.zhao@linaro.org>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-
-_hidden
-int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
-                        struct xc_dom_image *dom);
-
-_hidden
-int libxl__get_acpi_size(libxl__gc *gc,
-                         const libxl_domain_build_info *info,
-                         uint64_t *out);
-
-static inline uint64_t libxl__compute_mpdir(unsigned int cpuid)
-{
-    /*
-     * According to ARM CPUs bindings, the reg field should match
-     * the MPIDR's affinity bits. We will use AFF0 and AFF1 when
-     * constructing the reg value of the guest at the moment, for it
-     * is enough for the current max vcpu number.
-     */
-    return (cpuid & 0x0f) | (((cpuid >> 4) & 0xff) << 8);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_arm_acpi.c b/tools/libxl/libxl_arm_acpi.c
deleted file mode 100644 (file)
index ba874c3..0000000
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * ARM DomU ACPI generation
- *
- * Copyright (C) 2016      Linaro Ltd.
- *
- * Author: Shannon Zhao <shannon.zhao@linaro.org>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_arm.h"
-
-#include <stdint.h>
-
-/* Below typedefs are useful for the headers under acpi/ */
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int64_t s64;
-
-#include <acpi/acconfig.h>
-#include <acpi/actbl.h>
-
-#ifndef BITS_PER_LONG
-#ifdef _LP64
-#define BITS_PER_LONG 64
-#else
-#define BITS_PER_LONG 32
-#endif
-#endif
-#define ACPI_MACHINE_WIDTH BITS_PER_LONG
-#define COMPILER_DEPENDENT_INT64 int64_t
-#define COMPILER_DEPENDENT_UINT64 uint64_t
-
-#include <acpi/actypes.h>
-
-_hidden
-extern const unsigned char dsdt_anycpu_arm[];
-_hidden
-extern const int dsdt_anycpu_arm_len;
-
-#define ACPI_OEM_ID "Xen\0\0"
-#define ACPI_OEM_TABLE_ID "ARM\0\0\0\0"
-#define ACPI_ASL_COMPILER_ID "XL\0"
-
-enum {
-    RSDP,
-    XSDT,
-    GTDT,
-    MADT,
-    FADT,
-    DSDT,
-    MAX_TABLE_NUMS,
-};
-
-struct acpitable {
-    uint64_t addr;
-    size_t size;
-};
-
-static int libxl__estimate_madt_size(libxl__gc *gc,
-                                     const libxl_domain_build_info *info,
-                                     size_t *size)
-{
-    int rc = 0;
-
-    switch (info->arch_arm.gic_version) {
-    case LIBXL_GIC_VERSION_V2:
-        *size = sizeof(struct acpi_table_madt) +
-                ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
-                sizeof(struct acpi_madt_generic_distributor);
-        break;
-    case LIBXL_GIC_VERSION_V3:
-        *size = sizeof(struct acpi_table_madt) +
-                ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
-                sizeof(struct acpi_madt_generic_distributor) +
-                sizeof(struct acpi_madt_generic_redistributor);
-        break;
-    default:
-        LOG(ERROR, "Unknown GIC version");
-        *size = 0;
-        rc = ERROR_FAIL;
-        break;
-    }
-
-    return rc;
-}
-
-int libxl__get_acpi_size(libxl__gc *gc,
-                         const libxl_domain_build_info *info,
-                         uint64_t *out)
-{
-    uint64_t size;
-    int rc = 0;
-
-
-    rc = libxl__estimate_madt_size(gc, info, &size);
-    if (rc < 0)
-        goto out;
-
-    *out = ROUNDUP(size, 3) +
-           ROUNDUP(sizeof(struct acpi_table_rsdp), 3) +
-           ROUNDUP(sizeof(struct acpi_table_xsdt), 3) +
-           ROUNDUP(sizeof(struct acpi_table_gtdt), 3) +
-           ROUNDUP(sizeof(struct acpi_table_fadt), 3) +
-           ROUNDUP(sizeof(dsdt_anycpu_arm_len), 3);
-
-out:
-    return rc;
-}
-
-static int libxl__allocate_acpi_tables(libxl__gc *gc,
-                                       libxl_domain_build_info *info,
-                                       struct xc_dom_image *dom,
-                                       struct acpitable acpitables[])
-{
-    int rc;
-    size_t size;
-
-    acpitables[RSDP].addr = GUEST_ACPI_BASE;
-    acpitables[RSDP].size = sizeof(struct acpi_table_rsdp);
-    dom->acpi_modules[0].length += ROUNDUP(acpitables[RSDP].size, 3);
-
-    acpitables[XSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
-    /*
-     * Currently only 3 tables(GTDT, FADT, MADT) are pointed by XSDT. Alloc
-     * entries for them.
-     */
-    acpitables[XSDT].size = sizeof(struct acpi_table_xsdt) +
-                            sizeof(uint64_t) * 2;
-    dom->acpi_modules[0].length += ROUNDUP(acpitables[XSDT].size, 3);
-
-    acpitables[GTDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
-    acpitables[GTDT].size = sizeof(struct acpi_table_gtdt);
-    dom->acpi_modules[0].length += ROUNDUP(acpitables[GTDT].size, 3);
-
-    acpitables[MADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
-
-    rc = libxl__estimate_madt_size(gc, info, &size);
-    if (rc < 0)
-        goto out;
-
-    acpitables[MADT].size = size;
-    dom->acpi_modules[0].length += ROUNDUP(acpitables[MADT].size, 3);
-
-    acpitables[FADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
-    acpitables[FADT].size = sizeof(struct acpi_table_fadt);
-    dom->acpi_modules[0].length += ROUNDUP(acpitables[FADT].size, 3);
-
-    acpitables[DSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
-    acpitables[DSDT].size = dsdt_anycpu_arm_len;
-    dom->acpi_modules[0].length += ROUNDUP(acpitables[DSDT].size, 3);
-
-    assert(dom->acpi_modules[0].length <= GUEST_ACPI_SIZE);
-    dom->acpi_modules[0].data = libxl__zalloc(gc, dom->acpi_modules[0].length);
-
-    rc = 0;
-out:
-    return rc;
-}
-
-static void calculate_checksum(void *table, uint32_t checksum_offset,
-                               uint32_t length)
-{
-    uint8_t *p, sum = 0;
-
-    p = table;
-    p[checksum_offset] = 0;
-
-    while (length--)
-        sum = sum + *p++;
-
-    p = table;
-    p[checksum_offset] = -sum;
-}
-
-static void make_acpi_rsdp(libxl__gc *gc, struct xc_dom_image *dom,
-                           struct acpitable acpitables[])
-{
-    uint64_t offset = acpitables[RSDP].addr - GUEST_ACPI_BASE;
-    struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset;
-
-    memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
-    BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(rsdp->oem_id));
-    memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id));
-    rsdp->length = acpitables[RSDP].size;
-    rsdp->revision = 0x02;
-    rsdp->xsdt_physical_address = acpitables[XSDT].addr;
-    calculate_checksum(rsdp,
-                       offsetof(struct acpi_table_rsdp, extended_checksum),
-                       acpitables[RSDP].size);
-}
-
-static void make_acpi_header(struct acpi_table_header *h, const char *sig,
-                             size_t len, uint8_t rev)
-{
-    memcpy(h->signature, sig, 4);
-    h->length = len;
-    h->revision = rev;
-    BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(h->oem_id));
-    memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id));
-    BUILD_BUG_ON(sizeof(ACPI_OEM_TABLE_ID) != sizeof(h->oem_table_id));
-    memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id));
-    h->oem_revision = 0;
-    BUILD_BUG_ON(sizeof(ACPI_ASL_COMPILER_ID) != sizeof(h->asl_compiler_id));
-    memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID,
-           sizeof(h->asl_compiler_id));
-    h->asl_compiler_revision = 0;
-    h->checksum = 0;
-}
-
-static void make_acpi_xsdt(libxl__gc *gc, struct xc_dom_image *dom,
-                           struct acpitable acpitables[])
-{
-    uint64_t offset = acpitables[XSDT].addr - GUEST_ACPI_BASE;
-    struct acpi_table_xsdt *xsdt = (void *)dom->acpi_modules[0].data + offset;
-
-    xsdt->table_offset_entry[0] = acpitables[MADT].addr;
-    xsdt->table_offset_entry[1] = acpitables[GTDT].addr;
-    xsdt->table_offset_entry[2] = acpitables[FADT].addr;
-    make_acpi_header(&xsdt->header, "XSDT", acpitables[XSDT].size, 1);
-    calculate_checksum(xsdt, offsetof(struct acpi_table_header, checksum),
-                       acpitables[XSDT].size);
-}
-
-static void make_acpi_gtdt(libxl__gc *gc, struct xc_dom_image *dom,
-                           struct acpitable acpitables[])
-{
-    uint64_t offset = acpitables[GTDT].addr - GUEST_ACPI_BASE;
-    struct acpi_table_gtdt *gtdt = (void *)dom->acpi_modules[0].data + offset;
-
-    gtdt->non_secure_el1_interrupt = GUEST_TIMER_PHYS_NS_PPI;
-    gtdt->non_secure_el1_flags =
-                             (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
-                             |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
-    gtdt->virtual_timer_interrupt = GUEST_TIMER_VIRT_PPI;
-    gtdt->virtual_timer_flags =
-                             (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
-                             |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
-
-    gtdt->counter_block_addresss = ~((uint64_t)0);
-    gtdt->counter_read_block_address = ~((uint64_t)0);
-
-    make_acpi_header(&gtdt->header, "GTDT", acpitables[GTDT].size, 2);
-    calculate_checksum(gtdt, offsetof(struct acpi_table_header, checksum),
-                       acpitables[GTDT].size);
-}
-
-static void make_acpi_madt_gicc(void *table, int nr_cpus, uint64_t gicc_base)
-{
-    int i;
-    struct acpi_madt_generic_interrupt *gicc = table;
-
-    for (i = 0; i < nr_cpus; i++) {
-        gicc->header.type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
-        gicc->header.length = ACPI_MADT_GICC_SIZE_v5;
-        gicc->base_address = gicc_base;
-        gicc->cpu_interface_number = i;
-        gicc->arm_mpidr = libxl__compute_mpdir(i);
-        gicc->uid = i;
-        gicc->flags = ACPI_MADT_ENABLED;
-        gicc = table + ACPI_MADT_GICC_SIZE_v5;
-    }
-}
-
-static void make_acpi_madt_gicd(void *table, uint64_t gicd_base,
-                                uint8_t gic_version)
-{
-    struct acpi_madt_generic_distributor *gicd = table;
-
-    gicd->header.type = ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR;
-    gicd->header.length = sizeof(*gicd);
-    gicd->base_address = gicd_base;
-    /* This version field has no meaning before ACPI 5.1 errata. */
-    gicd->version = gic_version;
-}
-
-static void make_acpi_madt_gicr(void *table, uint64_t gicr_base,
-                                uint64_t gicr_size)
-{
-    struct acpi_madt_generic_redistributor *gicr = table;
-
-    gicr->header.type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
-    gicr->header.length = sizeof(*gicr);
-    gicr->base_address = gicr_base;
-    gicr->length = gicr_size;
-}
-
-static int make_acpi_madt(libxl__gc *gc, struct xc_dom_image *dom,
-                          libxl_domain_build_info *info,
-                          struct acpitable acpitables[])
-{
-    uint64_t offset = acpitables[MADT].addr - GUEST_ACPI_BASE;
-    void *table = dom->acpi_modules[0].data + offset;
-    struct acpi_table_madt *madt = table;
-    int rc = 0;
-
-    switch (info->arch_arm.gic_version) {
-    case LIBXL_GIC_VERSION_V2:
-        table += sizeof(struct acpi_table_madt);
-        make_acpi_madt_gicc(table, info->max_vcpus, GUEST_GICC_BASE);
-
-        table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
-        make_acpi_madt_gicd(table, GUEST_GICD_BASE, ACPI_MADT_GIC_VERSION_V2);
-        break;
-    case LIBXL_GIC_VERSION_V3:
-        table += sizeof(struct acpi_table_madt);
-        make_acpi_madt_gicc(table, info->max_vcpus, 0);
-
-        table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
-        make_acpi_madt_gicd(table, GUEST_GICV3_GICD_BASE,
-                            ACPI_MADT_GIC_VERSION_V3);
-
-        table += sizeof(struct acpi_madt_generic_distributor);
-        make_acpi_madt_gicr(table, GUEST_GICV3_GICR0_BASE,
-                            GUEST_GICV3_GICR0_SIZE);
-        break;
-    default:
-        LOG(ERROR, "Unknown GIC version");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    make_acpi_header(&madt->header, "APIC", acpitables[MADT].size, 3);
-    calculate_checksum(madt, offsetof(struct acpi_table_header, checksum),
-                       acpitables[MADT].size);
-
-out:
-    return rc;
-}
-
-static void make_acpi_fadt(libxl__gc *gc, struct xc_dom_image *dom,
-                           struct acpitable acpitables[])
-{
-    uint64_t offset = acpitables[FADT].addr - GUEST_ACPI_BASE;
-    struct acpi_table_fadt *fadt = (void *)dom->acpi_modules[0].data + offset;
-
-    /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
-    fadt->flags = ACPI_FADT_HW_REDUCED;
-    fadt->arm_boot_flags = ACPI_FADT_PSCI_COMPLIANT | ACPI_FADT_PSCI_USE_HVC;
-
-    /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
-    fadt->minor_revision = 0x1;
-    fadt->dsdt = acpitables[DSDT].addr;
-
-    make_acpi_header(&fadt->header, "FACP", acpitables[FADT].size, 5);
-    calculate_checksum(fadt, offsetof(struct acpi_table_header, checksum),
-                       acpitables[FADT].size);
-}
-
-static void make_acpi_dsdt(libxl__gc *gc, struct xc_dom_image *dom,
-                           struct acpitable acpitables[])
-{
-    uint64_t offset = acpitables[DSDT].addr - GUEST_ACPI_BASE;
-    void *dsdt = dom->acpi_modules[0].data + offset;
-
-    memcpy(dsdt, dsdt_anycpu_arm, dsdt_anycpu_arm_len);
-}
-
-int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
-                        struct xc_dom_image *dom)
-{
-    const libxl_version_info *vers;
-    int rc = 0;
-    struct acpitable acpitables[MAX_TABLE_NUMS];
-
-    vers = libxl_get_version_info(CTX);
-    if (vers == NULL) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    LOG(DEBUG, "constructing ACPI tables for Xen version %d.%d guest",
-        vers->xen_version_major, vers->xen_version_minor);
-
-    dom->acpi_modules[0].data = NULL;
-    dom->acpi_modules[0].length = 0;
-    dom->acpi_modules[0].guest_addr_out = GUEST_ACPI_BASE;
-
-    rc = libxl__allocate_acpi_tables(gc, info, dom, acpitables);
-    if (rc)
-        goto out;
-
-    make_acpi_rsdp(gc, dom, acpitables);
-    make_acpi_xsdt(gc, dom, acpitables);
-    make_acpi_gtdt(gc, dom, acpitables);
-    rc = make_acpi_madt(gc, dom, info, acpitables);
-    if (rc)
-        goto out;
-
-    make_acpi_fadt(gc, dom, acpitables);
-    make_acpi_dsdt(gc, dom, acpitables);
-
-out:
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_arm_no_acpi.c b/tools/libxl/libxl_arm_no_acpi.c
deleted file mode 100644 (file)
index 5dde0cd..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * ARM DomU ACPI generation
- *
- * Copyright (C) 2016      Linaro Ltd.
- *
- * Author: Shannon Zhao <shannon.zhao@linaro.org>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_arm.h"
-
-int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
-                        struct xc_dom_image *dom)
-{
-    return ERROR_FAIL;
-}
-
-int libxl__get_acpi_size(libxl__gc *gc,
-                         const libxl_domain_build_info *info,
-                         uint64_t *out)
-{
-    return ERROR_FAIL;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_bootloader.c b/tools/libxl/libxl_bootloader.c
deleted file mode 100644 (file)
index 18e9ebd..0000000
+++ /dev/null
@@ -1,680 +0,0 @@
-/*
- * Copyright (C) 2010      Citrix Ltd.
- * Author Ian Campbell <ian.campbell@citrix.com>
- *
- * This program 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; version 2.1 only.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <termios.h>
-#ifdef HAVE_UTMP_H
-#include <utmp.h>
-#endif
-
-#ifdef INCLUDE_LIBUTIL_H
-#include INCLUDE_LIBUTIL_H
-#endif
-
-#include "libxl_internal.h"
-
-#define BOOTLOADER_BUF_OUT 65536
-#define BOOTLOADER_BUF_IN   4096
-
-static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op);
-static void bootloader_keystrokes_copyfail(libxl__egc *egc,
-       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
-static void bootloader_display_copyfail(libxl__egc *egc,
-       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
-static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc,
-                                   int rc);
-static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
-                                pid_t pid, int status);
-
-/*----- bootloader arguments -----*/
-
-static void bootloader_arg(libxl__bootloader_state *bl, const char *arg)
-{
-    assert(bl->nargs < bl->argsspace);
-    bl->args[bl->nargs++] = arg;
-}
-
-static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
-                                 const char *bootloader_path)
-{
-    const libxl_domain_build_info *info = bl->info;
-
-    bl->argsspace = 9 + libxl_string_list_length(&info->bootloader_args);
-
-    GCNEW_ARRAY(bl->args, bl->argsspace);
-
-#define ARG(arg) bootloader_arg(bl, (arg))
-
-    ARG(bootloader_path);
-
-    if (info->kernel)
-        ARG(GCSPRINTF("--kernel=%s", info->kernel));
-    if (info->ramdisk)
-        ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk));
-    if (info->cmdline && *info->cmdline != '\0')
-        ARG(GCSPRINTF("--args=%s", info->cmdline));
-
-    ARG(GCSPRINTF("--output=%s", bl->outputpath));
-    ARG("--output-format=simple0");
-    ARG(GCSPRINTF("--output-directory=%s", bl->outputdir));
-
-    if (info->bootloader_args) {
-        char **p = info->bootloader_args;
-        while (*p) {
-            ARG(*p);
-            p++;
-        }
-    }
-
-    ARG(bl->dls.diskpath);
-
-    /* Sentinel for execv */
-    ARG(NULL);
-
-#undef ARG
-}
-
-/*----- synchronous subroutines -----*/
-
-static int setup_xenconsoled_pty(libxl__egc *egc, libxl__bootloader_state *bl,
-                                 char *slave_path, size_t slave_path_len)
-{
-    STATE_AO_GC(bl->ao);
-    struct termios termattr;
-    int r, rc;
-    int slave = libxl__carefd_fd(bl->ptys[1].slave);
-    int master = libxl__carefd_fd(bl->ptys[1].master);
-
-    r = ttyname_r(slave, slave_path, slave_path_len);
-    if (r == -1) {
-        LOGED(ERROR, bl->domid, "ttyname_r failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /*
-     * On Solaris, the pty master side will get cranky if we try
-     * to write to it while there is no slave. To work around this,
-     * keep the slave descriptor open until we're done. Set it
-     * to raw terminal parameters, otherwise it will echo back
-     * characters, which will confuse the I/O loop below.
-     * Furthermore, a raw master pty device has no terminal
-     * semantics on Solaris, so don't try to set any attributes
-     * for it.
-     */
-    tcgetattr(master, &termattr);
-    cfmakeraw(&termattr);
-    tcsetattr(master, TCSANOW, &termattr);
-
-    return 0;
-
- out:
-    return rc;
-}
-
-static const char *bootloader_result_command(libxl__gc *gc, const char *buf,
-                         const char *prefix, size_t prefixlen, uint32_t domid) {
-    if (strncmp(buf, prefix, prefixlen))
-        return 0;
-
-    const char *rhs = buf + prefixlen;
-    if (!CTYPE(isspace,*rhs))
-        return 0;
-
-    while (CTYPE(isspace,*rhs))
-        rhs++;
-
-    LOGD(DEBUG, domid, "bootloader output contained %s %s", prefix, rhs);
-
-    return rhs;
-}
-
-static int parse_bootloader_result(libxl__egc *egc,
-                                   libxl__bootloader_state *bl)
-{
-    STATE_AO_GC(bl->ao);
-    char buf[PATH_MAX*2];
-    FILE *f = 0;
-    int rc = ERROR_FAIL;
-
-    f = fopen(bl->outputpath, "r");
-    if (!f) {
-        LOGED(ERROR, bl->domid, "open bootloader output file %s",
-              bl->outputpath);
-        goto out;
-    }
-
-    for (;;) {
-        /* Read a nul-terminated "line" and put the result in
-         * buf, and its length (not including the nul) in l */
-        int l = 0, c;
-        while ((c = getc(f)) != EOF && c != '\0') {
-            if (l < sizeof(buf)-1)
-                buf[l] = c;
-            l++;
-        }
-        if (c == EOF) {
-            if (ferror(f)) {
-                LOGED(ERROR, bl->domid, "read bootloader output file %s",
-                      bl->outputpath);
-                goto out;
-            }
-            if (!l)
-                break;
-        }
-        if (l >= sizeof(buf)) {
-            LOGD(WARN, bl->domid, "bootloader output contained"
-                 " overly long item `%.150s...'", buf);
-            continue;
-        }
-        buf[l] = 0;
-
-        const char *rhs;
-#define COMMAND(s) ((rhs = bootloader_result_command(gc, buf, s, sizeof(s)-1, bl->domid)))
-
-        if (COMMAND("kernel")) {
-            bl->kernel->path = libxl__strdup(gc, rhs);
-            libxl__file_reference_map(bl->kernel);
-            unlink(bl->kernel->path);
-        } else if (COMMAND("ramdisk")) {
-            bl->ramdisk->path = libxl__strdup(gc, rhs);
-            libxl__file_reference_map(bl->ramdisk);
-            unlink(bl->ramdisk->path);
-        } else if (COMMAND("args")) {
-            bl->cmdline = libxl__strdup(gc, rhs);
-        } else if (l) {
-            LOGD(WARN, bl->domid,
-                 "unexpected output from bootloader: `%s'", buf);
-        }
-    }
-    rc = 0;
-
- out:
-    if (f) fclose(f);
-    return rc;
-}
-
-
-/*----- init and cleanup -----*/
-
-void libxl__bootloader_init(libxl__bootloader_state *bl)
-{
-    assert(bl->ao);
-    bl->rc = 0;
-    bl->dls.diskpath = NULL;
-    bl->openpty.ao = bl->ao;
-    bl->dls.ao = bl->ao;
-    bl->ptys[0].master = bl->ptys[0].slave = 0;
-    bl->ptys[1].master = bl->ptys[1].slave = 0;
-    libxl__ev_child_init(&bl->child);
-    libxl__domaindeathcheck_init(&bl->deathcheck);
-    bl->keystrokes.ao = bl->ao;  libxl__datacopier_init(&bl->keystrokes);
-    bl->display.ao = bl->ao;     libxl__datacopier_init(&bl->display);
-    bl->got_pollhup = 0;
-}
-
-static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl)
-{
-    STATE_AO_GC(bl->ao);
-    int i;
-
-    if (bl->outputpath) libxl__remove_file(gc, bl->outputpath);
-    if (bl->outputdir) libxl__remove_directory(gc, bl->outputdir);
-
-    libxl__domaindeathcheck_stop(gc,&bl->deathcheck);
-    libxl__datacopier_kill(&bl->keystrokes);
-    libxl__datacopier_kill(&bl->display);
-    for (i=0; i<2; i++) {
-        libxl__carefd_close(bl->ptys[i].master);
-        libxl__carefd_close(bl->ptys[i].slave);
-    }
-    if (bl->display.log) {
-        fclose(bl->display.log);
-        bl->display.log = NULL;
-    }
-}
-
-static void bootloader_setpaths(libxl__gc *gc, libxl__bootloader_state *bl)
-{
-    uint32_t domid = bl->domid;
-    bl->outputdir = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".d", domid);
-    bl->outputpath = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".out", domid);
-}
-
-/* Callbacks */
-
-static void bootloader_local_detached_cb(libxl__egc *egc,
-                                         libxl__disk_local_state *dls,
-                                         int rc);
-
-static void bootloader_callback(libxl__egc *egc, libxl__bootloader_state *bl,
-                                int rc)
-{
-    if (!bl->rc)
-        bl->rc = rc;
-
-    bootloader_cleanup(egc, bl);
-
-    bl->dls.callback = bootloader_local_detached_cb;
-    libxl__device_disk_local_initiate_detach(egc, &bl->dls);
-}
-
-static void bootloader_local_detached_cb(libxl__egc *egc,
-                                         libxl__disk_local_state *dls,
-                                         int rc)
-{
-    STATE_AO_GC(dls->ao);
-    libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
-
-    if (rc) {
-        LOGD(ERROR, bl->domid,
-             "unable to detach locally attached disk");
-        if (!bl->rc)
-            bl->rc = rc;
-    }
-
-    bl->callback(egc, bl, bl->rc);
-}
-
-/* might be called at any time, provided it's init'd */
-static void bootloader_stop(libxl__egc *egc,
-                             libxl__bootloader_state *bl, int rc)
-{
-    STATE_AO_GC(bl->ao);
-    int r;
-
-    libxl__datacopier_kill(&bl->keystrokes);
-    libxl__datacopier_kill(&bl->display);
-    if (libxl__ev_child_inuse(&bl->child)) {
-        r = kill(bl->child.pid, SIGTERM);
-        if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]",
-                     rc ? "after failure, " : "", (unsigned long)bl->child.pid);
-    }
-    if (!bl->rc)
-        bl->rc = rc;
-}
-
-/*----- main flow of control -----*/
-
-/* Callbacks */
-
-static void bootloader_disk_attached_cb(libxl__egc *egc,
-                                        libxl__disk_local_state *dls,
-                                        int rc);
-
-void libxl__bootloader_run(libxl__egc *egc, libxl__bootloader_state *bl)
-{
-    STATE_AO_GC(bl->ao);
-    const libxl_domain_build_info *info = bl->info;
-    uint32_t domid = bl->domid;
-    char *logfile_tmp = NULL;
-    int rc, r;
-
-    libxl__bootloader_init(bl);
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        LOGD(DEBUG, domid, "not a PV/PVH domain, skipping bootloader");
-        rc = 0;
-        goto out_ok;
-    }
-
-    if (!info->bootloader) {
-        LOGD(DEBUG, domid,
-             "no bootloader configured, using user supplied kernel");
-        bl->kernel->path = bl->info->kernel;
-        bl->ramdisk->path = bl->info->ramdisk;
-        bl->cmdline = bl->info->cmdline;
-        rc = 0;
-        goto out_ok;
-    }
-
-    if (!bl->disk) {
-        LOGD(ERROR, domid, "cannot run bootloader with no boot disk");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    bootloader_setpaths(gc, bl);
-
-    const char *logfile_leaf = GCSPRINTF("bootloader.%"PRIu32, domid);
-    rc = libxl_create_logfile(CTX, logfile_leaf, &logfile_tmp);
-    if (rc) goto out;
-
-    /* Transfer ownership of log filename to bl and the gc */
-    bl->logfile = logfile_tmp;
-    libxl__ptr_add(gc, logfile_tmp);
-    logfile_tmp = NULL;
-
-    bl->display.log = fopen(bl->logfile, "a");
-    if (!bl->display.log) {
-        LOGED(ERROR, domid,
-              "failed to create bootloader logfile %s", bl->logfile);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    for (;;) {
-        r = mkdir(bl->outputdir, 0600);
-        if (!r) break;
-        if (errno == EINTR) continue;
-        if (errno == EEXIST) break;
-        LOGED(ERROR, domid,
-              "failed to create bootloader dir %s", bl->outputdir);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    for (;;) {
-        r = open(bl->outputpath, O_WRONLY|O_CREAT|O_TRUNC, 0600);
-        if (r>=0) { close(r); break; }
-        if (errno == EINTR) continue;
-        LOGED(ERROR, domid,
-              "failed to precreate bootloader output %s", bl->outputpath);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-
-    /* This sets the state of the dls struct from Undefined to Idle */
-    libxl__device_disk_local_init(&bl->dls);
-    bl->dls.ao = ao;
-    bl->dls.in_disk = bl->disk;
-    bl->dls.blkdev_start = info->blkdev_start;
-    bl->dls.callback = bootloader_disk_attached_cb;
-    libxl__device_disk_local_initiate_attach(egc, &bl->dls);
-    return;
-
- out:
-    assert(rc);
- out_ok:
-    free(logfile_tmp);
-    bootloader_callback(egc, bl, rc);
-}
-
-static void bootloader_disk_attached_cb(libxl__egc *egc,
-                                        libxl__disk_local_state *dls,
-                                        int rc)
-{
-    STATE_AO_GC(dls->ao);
-    libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
-    const libxl_domain_build_info *info = bl->info;
-    const char *bootloader;
-
-    if (rc) {
-        LOGD(ERROR, bl->domid,
-             "failed to attach local disk for bootloader execution");
-        goto out;
-    }
-
-    LOGD(DEBUG, bl->domid,
-         "Config bootloader value: %s", info->bootloader);
-
-    if ( !strcmp(info->bootloader, "/usr/bin/pygrub") )
-        LOGD(WARN, bl->domid,
-             "bootloader='/usr/bin/pygrub' is deprecated; use " \
-             "bootloader='pygrub' instead");
-
-    bootloader = info->bootloader;
-
-    /* If the full path is not specified, check in the libexec path */
-    if ( bootloader[0] != '/' ) {
-        const char *bltmp;
-        struct stat st;
-
-        bltmp = libxl__abs_path(gc, bootloader, libxl__private_bindir_path());
-        /* Check to see if the file exists in this location; if not,
-         * fall back to checking the path */
-        LOGD(DEBUG, bl->domid,
-             "Checking for bootloader in libexec path: %s", bltmp);
-
-        if ( lstat(bltmp, &st) )
-            LOGD(DEBUG, bl->domid,
-                 "%s doesn't exist, falling back to config path",
-                 bltmp);
-        else
-            bootloader = bltmp;
-    }
-
-    make_bootloader_args(gc, bl, bootloader);
-
-    bl->openpty.ao = ao;
-    bl->openpty.callback = bootloader_gotptys;
-    bl->openpty.count = 2;
-    bl->openpty.results = bl->ptys;
-    rc = libxl__openptys(&bl->openpty, 0,0);
-    if (rc) goto out;
-
-    return;
-
- out:
-    assert(rc);
-    bootloader_callback(egc, bl, rc);
-}
-
-static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op)
-{
-    libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty);
-    STATE_AO_GC(bl->ao);
-    int rc, r;
-    char *const env[] = { "TERM", "vt100", NULL };
-
-    if (bl->openpty.rc) {
-        rc = bl->openpty.rc;
-        goto out;
-    }
-
-    /*
-     * We need to present the bootloader's tty as a pty slave that xenconsole
-     * can access.  Since the bootloader itself needs a pty slave,
-     * we end up with a connection like this:
-     *
-     * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
-     *
-     * where we copy characters between the two master fds, as well as
-     * listening on the bootloader's fifo for the results.
-     */
-
-    char *dom_console_xs_path;
-    char dom_console_slave_tty_path[PATH_MAX];
-    rc = setup_xenconsoled_pty(egc, bl,
-                               &dom_console_slave_tty_path[0],
-                               sizeof(dom_console_slave_tty_path));
-    if (rc) goto out;
-
-    char *dompath = libxl__xs_get_dompath(gc, bl->domid);
-    if (!dompath) { rc = ERROR_FAIL; goto out; }
-
-    dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath);
-
-    rc = libxl__xs_printf(gc, XBT_NULL, dom_console_xs_path, "%s",
-                          dom_console_slave_tty_path);
-    if (rc) {
-        LOGED(ERROR, bl->domid, "xs write console path %s := %s failed",
-             dom_console_xs_path, dom_console_slave_tty_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    bl->deathcheck.what = "stopping bootloader";
-    bl->deathcheck.domid = bl->domid;
-    bl->deathcheck.callback = bootloader_domaindeath;
-    rc = libxl__domaindeathcheck_start(ao, &bl->deathcheck);
-    if (rc) goto out;
-
-    if (bl->console_available)
-        bl->console_available(egc, bl);
-
-    int bootloader_master = libxl__carefd_fd(bl->ptys[0].master);
-    int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master);
-
-    libxl_fd_set_nonblock(CTX, bootloader_master, 1);
-    libxl_fd_set_nonblock(CTX, xenconsole_master, 1);
-
-    bl->keystrokes.writefd   = bl->display.readfd   = bootloader_master;
-    bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty";
-
-    bl->keystrokes.readfd   = bl->display.writefd   = xenconsole_master;
-    bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty";
-
-    bl->keystrokes.ao = ao;
-    bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT;
-    bl->keystrokes.bytes_to_read = -1;
-    bl->keystrokes.copywhat =
-        GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid);
-    bl->keystrokes.callback =         bootloader_keystrokes_copyfail;
-    bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail;
-        /* pollhup gets called with errnoval==-1 which is not otherwise
-         * possible since errnos are nonnegative, so it's unambiguous */
-    rc = libxl__datacopier_start(&bl->keystrokes);
-    if (rc) goto out;
-
-    bl->display.ao = ao;
-    bl->display.maxsz = BOOTLOADER_BUF_IN;
-    bl->display.bytes_to_read = -1;
-    bl->display.copywhat =
-        GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid);
-    bl->display.callback =         bootloader_display_copyfail;
-    bl->display.callback_pollhup = bootloader_display_copyfail;
-    rc = libxl__datacopier_start(&bl->display);
-    if (rc) goto out;
-
-    LOGD(DEBUG, bl->domid, "executing bootloader: %s", bl->args[0]);
-    for (const char **blarg = bl->args;
-         *blarg;
-         blarg++)
-        LOGD(DEBUG, bl->domid, "  bootloader arg: %s", *blarg);
-
-    struct termios termattr;
-
-    pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished);
-    if (pid == -1) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (!pid) {
-        /* child */
-        r = login_tty(libxl__carefd_fd(bl->ptys[0].slave));
-        if (r) { LOGED(ERROR, bl->domid, "login_tty failed"); exit(-1); }
-        libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env);
-    }
-
-    /* parent */
-
-    /*
-     * On Solaris, the master pty side does not have terminal semantics,
-     * so don't try to set any attributes, as it will fail.
-     */
-#if !defined(__sun__)
-    tcgetattr(bootloader_master, &termattr);
-    cfmakeraw(&termattr);
-    tcsetattr(bootloader_master, TCSANOW, &termattr);
-#endif
-
-    return;
-
- out:
-    bootloader_callback(egc, bl, rc);
-}
-
-/* perhaps one of these will be called, but perhaps not */
-static void bootloader_copyfail(libxl__egc *egc, const char *which,
-        libxl__bootloader_state *bl, int ondisplay,
-        int rc, int onwrite, int errnoval)
-{
-    STATE_AO_GC(bl->ao);
-
-    if (errnoval==-1) {
-        /* POLLHUP */
-        if (!!ondisplay != !!onwrite) {
-            rc = 0;
-            bl->got_pollhup = 1;
-        } else {
-            LOGD(ERROR, bl->domid, "unexpected POLLHUP on %s", which);
-        }
-    } else if (!rc) {
-        LOGD(ERROR, bl->domid, "unexpected eof copying %s", which);
-        rc = ERROR_FAIL;
-    }
-
-    bootloader_stop(egc, bl, rc);
-}
-static void bootloader_keystrokes_copyfail(libxl__egc *egc,
-       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
-{
-    libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, keystrokes);
-    bootloader_copyfail(egc, "bootloader input", bl, 0, rc,onwrite,errnoval);
-}
-static void bootloader_display_copyfail(libxl__egc *egc,
-       libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
-{
-    libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display);
-    bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval);
-}
-
-static void bootloader_domaindeath(libxl__egc *egc,
-                                   libxl__domaindeathcheck *dc,
-                                   int rc)
-{
-    libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, deathcheck);
-    bootloader_stop(egc, bl, rc);
-}
-
-static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
-                                pid_t pid, int status)
-{
-    libxl__bootloader_state *bl = CONTAINER_OF(child, *bl, child);
-    STATE_AO_GC(bl->ao);
-    int rc;
-
-    libxl__datacopier_kill(&bl->keystrokes);
-    libxl__datacopier_kill(&bl->display);
-
-    if (status) {
-        if (bl->got_pollhup && WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)
-            LOGD(ERROR, bl->domid, "got POLLHUP, sent SIGTERM");
-        LOGD(ERROR, bl->domid,
-             "bootloader failed - consult logfile %s", bl->logfile);
-        libxl_report_child_exitstatus(CTX, XTL_ERROR, "bootloader",
-                                      pid, status);
-        rc = ERROR_FAIL;
-        goto out;
-    } else {
-        LOGD(DEBUG, bl->domid, "bootloader completed");
-    }
-
-    if (bl->rc) {
-        /* datacopier went wrong */
-        rc = bl->rc;
-        goto out;
-    }
-
-    rc = parse_bootloader_result(egc, bl);
-    if (rc) goto out;
-
-    rc = 0;
-    LOGD(DEBUG, bl->domid, "bootloader execution successful");
-
- out:
-    bootloader_callback(egc, bl, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_checkpoint_device.c b/tools/libxl/libxl_checkpoint_device.c
deleted file mode 100644 (file)
index f6395dc..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2014 FUJITSU LIMITED
- * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*----- setup() and teardown() -----*/
-
-/* callbacks */
-
-static void all_devices_setup_cb(libxl__egc *egc,
-                                 libxl__multidev *multidev,
-                                 int rc);
-static void device_setup_iterate(libxl__egc *egc,
-                                 libxl__ao_device *aodev);
-static void devices_teardown_cb(libxl__egc *egc,
-                                libxl__multidev *multidev,
-                                int rc);
-
-/* checkpoint device setup and teardown */
-
-static libxl__checkpoint_device* checkpoint_device_init(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds,
-                                        libxl__device_kind kind,
-                                        void *libxl_dev)
-{
-    libxl__checkpoint_device *dev = NULL;
-
-    STATE_AO_GC(cds->ao);
-    GCNEW(dev);
-    dev->backend_dev = libxl_dev;
-    dev->kind = kind;
-    dev->cds = cds;
-
-    return dev;
-}
-
-static void checkpoint_devices_setup(libxl__egc *egc,
-                                     libxl__checkpoint_devices_state *cds);
-
-void libxl__checkpoint_devices_setup(libxl__egc *egc,
-                                     libxl__checkpoint_devices_state *cds)
-{
-    int i;
-
-    STATE_AO_GC(cds->ao);
-
-    cds->num_devices = 0;
-    cds->num_nics = 0;
-    cds->num_disks = 0;
-
-    if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VIF))
-        cds->nics = libxl__device_list(gc, &libxl__nic_devtype, cds->domid,
-                                       &cds->num_nics);
-
-    if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VBD))
-        cds->disks = libxl__device_list(gc, &libxl__disk_devtype, cds->domid,
-                                        &cds->num_disks);
-
-    if (cds->num_nics == 0 && cds->num_disks == 0)
-        goto out;
-
-    GCNEW_ARRAY(cds->devs, cds->num_nics + cds->num_disks);
-
-    for (i = 0; i < cds->num_nics; i++) {
-        cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
-                                                LIBXL__DEVICE_KIND_VIF,
-                                                &cds->nics[i]);
-    }
-
-    for (i = 0; i < cds->num_disks; i++) {
-        cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
-                                                LIBXL__DEVICE_KIND_VBD,
-                                                &cds->disks[i]);
-    }
-
-    checkpoint_devices_setup(egc, cds);
-
-    return;
-
-out:
-    cds->callback(egc, cds, 0);
-}
-
-static void checkpoint_devices_setup(libxl__egc *egc,
-                                     libxl__checkpoint_devices_state *cds)
-{
-    int i, rc;
-
-    STATE_AO_GC(cds->ao);
-
-    libxl__multidev_begin(ao, &cds->multidev);
-    cds->multidev.callback = all_devices_setup_cb;
-    for (i = 0; i < cds->num_devices; i++) {
-        libxl__checkpoint_device *dev = cds->devs[i];
-        dev->ops_index = -1;
-        libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
-
-        dev->aodev.rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
-        dev->aodev.callback = device_setup_iterate;
-        device_setup_iterate(egc,&dev->aodev);
-    }
-
-    rc = 0;
-    libxl__multidev_prepared(egc, &cds->multidev, rc);
-}
-
-
-static void device_setup_iterate(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
-    EGC_GC;
-
-    if (aodev->rc != ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED &&
-        aodev->rc != ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH)
-        /* might be success or disaster */
-        goto out;
-
-    do {
-        dev->ops = dev->cds->ops[++dev->ops_index];
-        if (!dev->ops) {
-            libxl_device_nic * nic = NULL;
-            libxl_device_disk * disk = NULL;
-            uint32_t domid = INVALID_DOMID;
-            int devid;
-            if (dev->kind == LIBXL__DEVICE_KIND_VIF) {
-                nic = (libxl_device_nic *)dev->backend_dev;
-                domid = nic->backend_domid;
-                devid = nic->devid;
-            } else if (dev->kind == LIBXL__DEVICE_KIND_VBD) {
-                disk = (libxl_device_disk *)dev->backend_dev;
-                domid = disk->backend_domid;
-                devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
-            } else {
-                LOGD(ERROR, domid, "device kind not handled by checkpoint: %s",
-                     libxl__device_kind_to_string(dev->kind));
-                aodev->rc = ERROR_FAIL;
-                goto out;
-            }
-            LOGD(ERROR, domid, "device not handled by checkpoint"
-                 " (device=%s:%"PRId32"/%"PRId32")",
-                 libxl__device_kind_to_string(dev->kind),
-                 domid, devid);
-            aodev->rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
-            goto out;
-        }
-    } while (dev->ops->kind != dev->kind);
-
-    /* found the next ops_index to try */
-    assert(dev->aodev.callback == device_setup_iterate);
-    dev->ops->setup(egc,dev);
-    return;
-
- out:
-    libxl__multidev_one_callback(egc,aodev);
-}
-
-static void all_devices_setup_cb(libxl__egc *egc,
-                                 libxl__multidev *multidev,
-                                 int rc)
-{
-    STATE_AO_GC(multidev->ao);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds =
-                            CONTAINER_OF(multidev, *cds, multidev);
-
-    cds->callback(egc, cds, rc);
-}
-
-void libxl__checkpoint_devices_teardown(libxl__egc *egc,
-                                   libxl__checkpoint_devices_state *cds)
-{
-    int i;
-    libxl__checkpoint_device *dev;
-
-    STATE_AO_GC(cds->ao);
-
-    libxl__multidev_begin(ao, &cds->multidev);
-    cds->multidev.callback = devices_teardown_cb;
-    for (i = 0; i < cds->num_devices; i++) {
-        dev = cds->devs[i];
-        if (!dev->ops || !dev->matched)
-            continue;
-
-        libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
-        dev->ops->teardown(egc,dev);
-    }
-
-    libxl__multidev_prepared(egc, &cds->multidev, 0);
-}
-
-static void devices_teardown_cb(libxl__egc *egc,
-                                libxl__multidev *multidev,
-                                int rc)
-{
-    STATE_AO_GC(multidev->ao);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds =
-                            CONTAINER_OF(multidev, *cds, multidev);
-
-    /* clean nic */
-    libxl__device_list_free(&libxl__nic_devtype, cds->nics, cds->num_nics);
-    cds->nics = NULL;
-    cds->num_nics = 0;
-
-    /* clean disk */
-    libxl__device_list_free(&libxl__disk_devtype, cds->disks, cds->num_disks);
-    cds->disks = NULL;
-    cds->num_disks = 0;
-
-    cds->callback(egc, cds, rc);
-}
-
-/*----- checkpointing APIs -----*/
-
-/* callbacks */
-
-static void devices_checkpoint_cb(libxl__egc *egc,
-                                  libxl__multidev *multidev,
-                                  int rc);
-
-/* API implementations */
-
-#define define_checkpoint_api(api)                                      \
-void libxl__checkpoint_devices_##api(libxl__egc *egc,                   \
-                                libxl__checkpoint_devices_state *cds)   \
-{                                                                       \
-    int i;                                                              \
-    libxl__checkpoint_device *dev;                                      \
-                                                                        \
-    STATE_AO_GC(cds->ao);                                               \
-                                                                        \
-    libxl__multidev_begin(ao, &cds->multidev);                          \
-    cds->multidev.callback = devices_checkpoint_cb;                     \
-    for (i = 0; i < cds->num_devices; i++) {                            \
-        dev = cds->devs[i];                                             \
-        if (!dev->matched || !dev->ops->api)                            \
-            continue;                                                   \
-        libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);\
-        dev->ops->api(egc,dev);                                         \
-    }                                                                   \
-                                                                        \
-    libxl__multidev_prepared(egc, &cds->multidev, 0);                   \
-}
-
-define_checkpoint_api(postsuspend);
-
-define_checkpoint_api(preresume);
-
-define_checkpoint_api(commit);
-
-static void devices_checkpoint_cb(libxl__egc *egc,
-                                  libxl__multidev *multidev,
-                                  int rc)
-{
-    STATE_AO_GC(multidev->ao);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds =
-                            CONTAINER_OF(multidev, *cds, multidev);
-
-    cds->callback(egc, cds, rc);
-}
diff --git a/tools/libxl/libxl_colo.h b/tools/libxl/libxl_colo.h
deleted file mode 100644 (file)
index 6c01b55..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef LIBXL_COLO_H
-#define LIBXL_COLO_H
-
-#include "libxl_internal.h"
-
-/* Maximum time(5s) to wait for colo proxy checkpoit */
-#define COLO_PROXY_CHECKPOINT_TIMEOUT 5000000
-
-#define ASYNC_CALL(egc, ao, child, param, func, callback) do {          \
-    int pid = -1;                                                       \
-    STATE_AO_GC(ao);                                                    \
-                                                                        \
-    pid = libxl__ev_child_fork(gc, child, callback);                    \
-    if (pid == -1) {                                                    \
-        LOGD(ERROR, ao->domid, "unable to fork");                       \
-        goto out;                                                       \
-    }                                                                   \
-                                                                        \
-    if (!pid) {                                                         \
-        /* child */                                                     \
-        func(param);                                                    \
-        /* notreached */                                                \
-        abort();                                                        \
-    }                                                                   \
-                                                                        \
-    return;                                                             \
-out:                                                                    \
-    callback(egc, child, -1, 1);                                        \
-} while (0)
-
-enum {
-    LIBXL_COLO_SETUPED,
-    LIBXL_COLO_SUSPENDED,
-    LIBXL_COLO_RESUMED,
-};
-
-struct libxl__colo_device_nic {
-    int devid;
-    const char *vif;
-};
-
-struct libxl__colo_qdisk {
-    bool setuped;
-};
-
-struct libxl__colo_proxy_state {
-    /* set by caller of colo_proxy_setup */
-    struct libxl__ao *ao;
-
-    int sock_fd;
-    int index;
-    /*
-     * Private, True means use userspace colo proxy
-     *          False means use kernel colo proxy.
-     */
-    bool is_userspace_proxy;
-    const char *checkpoint_host;
-    const char *checkpoint_port;
-};
-
-struct libxl__colo_save_state {
-    int send_fd;
-    int recv_fd;
-    char *colo_proxy_script;
-
-    /* private */
-    libxl__stream_read_state srs;
-    void (*callback)(libxl__egc *, libxl__colo_save_state *, int);
-    bool svm_running;
-    bool paused;
-
-    /* private, used by qdisk block replication */
-    bool qdisk_used;
-    bool qdisk_setuped;
-
-    /* private, used by colo-proxy */
-    libxl__colo_proxy_state cps;
-    libxl__ev_child child;
-};
-
-
-typedef void libxl__colo_callback(struct libxl__egc *egc,
-                                  libxl__colo_restore_state *crs, int rc);
-
-struct libxl__colo_restore_state {
-    /* must set by caller of libxl__colo_(setup|teardown) */
-    struct libxl__ao *ao;
-    uint32_t domid;
-    int send_back_fd;
-    int recv_fd;
-    int hvm;
-    libxl__colo_callback *callback;
-    char *colo_proxy_script;
-
-    /* private, colo restore checkpoint state */
-    libxl__domain_create_cb *saved_cb;
-    void *crcs;
-
-    /* private, used by qdisk block replication */
-    bool qdisk_used;
-    bool qdisk_setuped;
-    const char *host;
-    const char *port;
-
-    /* private, used by colo-proxy */
-    libxl__colo_proxy_state cps;
-};
-
-int init_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
-
-void cleanup_subkind_qdisk(struct libxl__checkpoint_devices_state *cds);
-
-int init_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
-
-void cleanup_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds);
-
-extern void libxl__colo_restore_setup(struct libxl__egc *egc,
-                                      libxl__colo_restore_state *crs);
-extern void libxl__colo_restore_teardown(struct libxl__egc *egc, void *dcs_void,
-                                         int ret, int retval, int errnoval);
-extern void libxl__colo_save_setup(struct libxl__egc *egc,
-                                   struct libxl__colo_save_state *css);
-extern void libxl__colo_save_teardown(struct libxl__egc *egc,
-                                      struct libxl__colo_save_state *css,
-                                      int rc);
-extern int colo_proxy_setup(libxl__colo_proxy_state *cps);
-extern void colo_proxy_teardown(libxl__colo_proxy_state *cps);
-extern void colo_proxy_preresume(libxl__colo_proxy_state *cps);
-extern void colo_proxy_postresume(libxl__colo_proxy_state *cps);
-extern int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
-                                 unsigned int timeout_us);
-
-#endif
diff --git a/tools/libxl/libxl_colo_nic.c b/tools/libxl/libxl_colo_nic.c
deleted file mode 100644 (file)
index bf5c48f..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-enum {
-    primary,
-    secondary,
-};
-
-/* ========== init() and cleanup() ========== */
-
-int init_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
-{
-    return 0;
-}
-
-void cleanup_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
-{
-}
-
-/* ========== helper functions ========== */
-
-static void colo_save_setup_script_cb(libxl__egc *egc,
-                                     libxl__async_exec_state *aes,
-                                     int rc, int status);
-static void colo_save_teardown_script_cb(libxl__egc *egc,
-                                         libxl__async_exec_state *aes,
-                                         int rc, int status);
-
-/*
- * If the device has a vifname, then use that instead of
- * the vifX.Y format.
- * it must ONLY be used for remus because if driver domains
- * were in use it would constitute a security vulnerability.
- */
-static const char *get_vifname(libxl__checkpoint_device *dev,
-                               const libxl_device_nic *nic)
-{
-    const char *vifname = NULL;
-    const char *path;
-    int rc;
-
-    STATE_AO_GC(dev->cds->ao);
-
-    /* Convenience aliases */
-    const uint32_t domid = dev->cds->domid;
-
-    path = GCSPRINTF("%s/vifname",
-                     libxl__domain_device_backend_path(gc, 0,
-                     domid, nic->devid, LIBXL__DEVICE_KIND_VIF));
-    rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
-    if (!rc && !vifname) {
-        vifname = libxl__device_nic_devname(gc, domid,
-                                            nic->devid,
-                                            nic->nictype);
-    }
-
-    return vifname;
-}
-
-/*
- * the script needs the following env & args
- * $vifname
- * $forwarddev
- * $mode(primary/secondary)
- * $index
- * $bridge
- * setup/teardown as command line arg.
- */
-static void setup_async_exec(libxl__checkpoint_device *dev, char *op,
-                             libxl__colo_proxy_state *cps, int side,
-                             char *colo_proxy_script)
-{
-    int arraysize, nr = 0;
-    char **env = NULL, **args = NULL;
-    libxl__colo_device_nic *colo_nic = dev->concrete_data;
-    libxl__checkpoint_devices_state *cds = dev->cds;
-    libxl__async_exec_state *aes = &dev->aodev.aes;
-    const libxl_device_nic *nic = dev->backend_dev;
-
-    STATE_AO_GC(cds->ao);
-
-    /* Convenience aliases */
-    const char *const vif = colo_nic->vif;
-
-    arraysize = 11;
-    GCNEW_ARRAY(env, arraysize);
-    env[nr++] = "vifname";
-    env[nr++] = libxl__strdup(gc, vif);
-    env[nr++] = "forwarddev";
-    env[nr++] = libxl__strdup(gc, nic->coloft_forwarddev);
-    env[nr++] = "mode";
-    if (side == primary)
-        env[nr++] = "primary";
-    else
-        env[nr++] = "secondary";
-    env[nr++] = "index";
-    env[nr++] = GCSPRINTF("%d", cps->index);
-    env[nr++] = "bridge";
-    env[nr++] = libxl__strdup(gc, nic->bridge);
-    env[nr++] = NULL;
-    assert(nr == arraysize);
-
-    arraysize = 3; nr = 0;
-    GCNEW_ARRAY(args, arraysize);
-    args[nr++] = colo_proxy_script;
-    args[nr++] = op;
-    args[nr++] = NULL;
-    assert(nr == arraysize);
-
-    aes->ao = dev->cds->ao;
-    aes->what = GCSPRINTF("%s %s", args[0], args[1]);
-    aes->env = env;
-    aes->args = args;
-    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
-    aes->stdfds[0] = -1;
-    aes->stdfds[1] = -1;
-    aes->stdfds[2] = -1;
-
-    if (!strcmp(op, "teardown"))
-        aes->callback = colo_save_teardown_script_cb;
-    else
-        aes->callback = colo_save_setup_script_cb;
-}
-
-/* ========== setup() and teardown() ========== */
-
-static void colo_nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
-                           libxl__colo_proxy_state *cps, int side,
-                           char *colo_proxy_script)
-{
-    int rc;
-    libxl__colo_device_nic *colo_nic;
-    const libxl_device_nic *nic = dev->backend_dev;
-
-    STATE_AO_GC(dev->cds->ao);
-
-    /*
-     * thers's no subkind of nic devices, so nic ops is always matched
-     * with nic devices, we begin to setup the nic device
-     */
-    dev->matched = 1;
-
-    if (!nic->coloft_forwarddev) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    GCNEW(colo_nic);
-    dev->concrete_data = colo_nic;
-    colo_nic->devid = nic->devid;
-    colo_nic->vif = get_vifname(dev, nic);
-    if (!colo_nic->vif) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    setup_async_exec(dev, "setup", cps, side, colo_proxy_script);
-    rc = libxl__async_exec_start(&dev->aodev.aes);
-    if (rc)
-        goto out;
-
-    return;
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void colo_save_setup_script_cb(libxl__egc *egc,
-                                      libxl__async_exec_state *aes,
-                                      int rc, int status)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
-    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
-    libxl__colo_device_nic *colo_nic = dev->concrete_data;
-    libxl__checkpoint_devices_state *cds = dev->cds;
-    const char *out_path_base, *hotplug_error = NULL;
-
-    EGC_GC;
-
-    /* Convenience aliases */
-    const uint32_t domid = cds->domid;
-    const int devid = colo_nic->devid;
-    const char *const vif = colo_nic->vif;
-
-    if (status && !rc)
-        rc = ERROR_FAIL;
-    if (rc)
-        goto out;
-
-    out_path_base = GCSPRINTF("%s/colo_proxy/%d",
-                              libxl__xs_libxl_path(gc, domid), devid);
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/hotplug-error", out_path_base),
-                                &hotplug_error);
-    if (rc)
-        goto out;
-
-    if (hotplug_error) {
-        LOGD(ERROR, domid, "colo_proxy script %s setup failed for vif %s: %s",
-            aes->args[0], vif, hotplug_error);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-static void colo_nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
-                              libxl__colo_proxy_state *cps, int side,
-                              char *colo_proxy_script)
-{
-    int rc;
-    libxl__colo_device_nic *colo_nic = dev->concrete_data;
-
-    if (!colo_nic || !colo_nic->vif) {
-        /* colo nic has not yet been set up, just return */
-        rc = 0;
-        goto out;
-    }
-
-    setup_async_exec(dev, "teardown", cps, side, colo_proxy_script);
-
-    rc = libxl__async_exec_start(&dev->aodev.aes);
-    if (rc)
-        goto out;
-
-    return;
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void colo_save_teardown_script_cb(libxl__egc *egc,
-                                         libxl__async_exec_state *aes,
-                                         int rc, int status)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
-
-    if (status && !rc)
-        rc = ERROR_FAIL;
-    else
-        rc = 0;
-
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-/* ======== primary ======== */
-
-static void colo_nic_save_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    libxl__colo_save_state *css = dev->cds->concrete_data;
-
-    colo_nic_setup(egc, dev, &css->cps, primary, css->colo_proxy_script);
-}
-
-static void colo_nic_save_teardown(libxl__egc *egc,
-                                   libxl__checkpoint_device *dev)
-{
-    libxl__colo_save_state *css = dev->cds->concrete_data;
-
-    colo_nic_teardown(egc, dev, &css->cps, primary, css->colo_proxy_script);
-}
-
-const libxl__checkpoint_device_instance_ops colo_save_device_nic = {
-    .kind = LIBXL__DEVICE_KIND_VIF,
-    .setup = colo_nic_save_setup,
-    .teardown = colo_nic_save_teardown,
-};
-
-/* ======== secondary ======== */
-
-static void colo_nic_restore_setup(libxl__egc *egc,
-                                   libxl__checkpoint_device *dev)
-{
-    libxl__colo_restore_state *crs = dev->cds->concrete_data;
-
-    colo_nic_setup(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
-}
-
-static void colo_nic_restore_teardown(libxl__egc *egc,
-                                      libxl__checkpoint_device *dev)
-{
-    libxl__colo_restore_state *crs = dev->cds->concrete_data;
-
-    colo_nic_teardown(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
-}
-
-const libxl__checkpoint_device_instance_ops colo_restore_device_nic = {
-    .kind = LIBXL__DEVICE_KIND_VIF,
-    .setup = colo_nic_restore_setup,
-    .teardown = colo_nic_restore_teardown,
-};
diff --git a/tools/libxl/libxl_colo_proxy.c b/tools/libxl/libxl_colo_proxy.c
deleted file mode 100644 (file)
index 5475f7e..0000000
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <netlink/netlink.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-/* Consistent with the new COLO netlink channel in kernel side */
-#define NETLINK_COLO 28
-#define COLO_DEFAULT_WAIT_TIME 500000
-
-enum colo_netlink_op {
-    COLO_QUERY_CHECKPOINT = (NLMSG_MIN_TYPE + 1),
-    COLO_CHECKPOINT,
-    COLO_FAILOVER,
-    COLO_PROXY_INIT,
-    COLO_PROXY_RESET, /* UNUSED, will be used for continuous FT */
-};
-
-/* ========= colo-proxy: helper functions ========== */
-
-static int colo_proxy_send(libxl__colo_proxy_state *cps, uint8_t *buff,
-                           uint64_t size, int type)
-{
-    struct sockaddr_nl sa;
-    struct nlmsghdr msg;
-    struct iovec iov;
-    struct msghdr mh;
-    int ret;
-
-    STATE_AO_GC(cps->ao);
-
-    memset(&sa, 0, sizeof(sa));
-    sa.nl_family = AF_NETLINK;
-    sa.nl_pid = 0;
-    sa.nl_groups = 0;
-
-    msg.nlmsg_len = NLMSG_SPACE(0);
-    msg.nlmsg_flags = NLM_F_REQUEST;
-    if (type == COLO_PROXY_INIT)
-        msg.nlmsg_flags |= NLM_F_ACK;
-    msg.nlmsg_seq = 0;
-    msg.nlmsg_pid = cps->index;
-    msg.nlmsg_type = type;
-
-    iov.iov_base = &msg;
-    iov.iov_len = msg.nlmsg_len;
-
-    mh.msg_name = &sa;
-    mh.msg_namelen = sizeof(sa);
-    mh.msg_iov = &iov;
-    mh.msg_iovlen = 1;
-    mh.msg_control = NULL;
-    mh.msg_controllen = 0;
-    mh.msg_flags = 0;
-
-    ret = sendmsg(cps->sock_fd, &mh, 0);
-    if (ret <= 0) {
-        LOGD(ERROR, ao->domid, "can't send msg to kernel by netlink: %s",
-            strerror(errno));
-    }
-
-    return ret;
-}
-
-static int colo_userspace_proxy_send(libxl__colo_proxy_state *cps,
-                                     uint8_t *buff,
-                                     uint32_t size)
-{
-    int ret = 0;
-    uint32_t len = 0;
-
-    len = htonl(size);
-    ret = send(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
-    if (ret != sizeof(len)) {
-        goto err;
-    }
-
-    ret = send(cps->sock_fd, (uint8_t *)buff, size, 0);
-    if (ret != size) {
-        goto err;
-    }
-
-err:
-    return ret;
-}
-
-static int colo_userspace_proxy_recv(libxl__colo_proxy_state *cps,
-                                     char *buff,
-                                     unsigned int timeout_us)
-{
-    struct timeval tv;
-    int ret;
-    uint32_t len = 0;
-    uint32_t size = 0;
-
-    STATE_AO_GC(cps->ao);
-
-    if (timeout_us) {
-        tv.tv_sec = timeout_us / 1000000;
-        tv.tv_usec = timeout_us % 1000000;
-        ret = setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv,
-                         sizeof(tv));
-        if (ret < 0) {
-            LOGD(ERROR, ao->domid,
-                 "colo_userspace_proxy_recv setsockopt error: %s",
-                 strerror(errno));
-        }
-    }
-
-    ret = recv(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0);
-    if (ret < 0) {
-        goto err;
-    }
-
-    size = ntohl(len);
-    ret = recv(cps->sock_fd, buff, size, 0);
-
-err:
-    return ret;
-}
-
-/* error: return -1, otherwise return 0 */
-static int64_t colo_proxy_recv(libxl__colo_proxy_state *cps, uint8_t **buff,
-                               unsigned int timeout_us)
-{
-    struct sockaddr_nl sa;
-    struct iovec iov;
-    struct msghdr mh = {
-        .msg_name = &sa,
-        .msg_namelen = sizeof(sa),
-        .msg_iov = &iov,
-        .msg_iovlen = 1,
-    };
-    struct timeval tv;
-    uint32_t size = 16384;
-    int64_t len = 0;
-    int ret;
-
-    STATE_AO_GC(cps->ao);
-    uint8_t *tmp = libxl__malloc(NOGC, size);
-
-    if (timeout_us) {
-        tv.tv_sec = timeout_us / 1000000;
-        tv.tv_usec = timeout_us % 1000000;
-        setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
-    }
-
-    iov.iov_base = tmp;
-    iov.iov_len = size;
-next:
-    ret = recvmsg(cps->sock_fd, &mh, 0);
-    if (ret <= 0) {
-        if (errno != EAGAIN && errno != EWOULDBLOCK)
-            LOGED(ERROR, ao->domid, "can't recv msg from kernel by netlink");
-        goto err;
-    }
-
-    len += ret;
-    if (mh.msg_flags & MSG_TRUNC) {
-        size += 16384;
-        tmp = libxl__realloc(NOGC, tmp, size);
-        iov.iov_base = tmp + len;
-        iov.iov_len = size - len;
-        goto next;
-    }
-
-    *buff = tmp;
-    ret = len;
-    goto out;
-
-err:
-    free(tmp);
-    *buff = NULL;
-
-out:
-    if (timeout_us) {
-        tv.tv_sec = 0;
-        tv.tv_usec = 0;
-        setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
-    }
-    return ret;
-}
-
-/* ========= colo-proxy: setup and teardown ========== */
-
-int colo_proxy_setup(libxl__colo_proxy_state *cps)
-{
-    int skfd = 0;
-    struct sockaddr_nl sa;
-    struct nlmsghdr *h;
-    int i = 1;
-    int ret = ERROR_FAIL;
-    uint8_t *buff = NULL;
-    int64_t size;
-
-    STATE_AO_GC(cps->ao);
-
-    /* If enable userspace proxy mode, we don't need setup kernel proxy */
-    if (cps->is_userspace_proxy) {
-        struct sockaddr_in addr;
-        int port;
-        char recvbuff[1024];
-        const char sendbuf[] = "COLO_USERSPACE_PROXY_INIT";
-
-        memset(&addr, 0, sizeof(addr));
-        port = atoi(cps->checkpoint_port);
-        addr.sin_family = AF_INET;
-        addr.sin_port = htons(port);
-        addr.sin_addr.s_addr = inet_addr(cps->checkpoint_host);
-
-        skfd = socket(AF_INET, SOCK_STREAM, 0);
-        if (skfd < 0) {
-            LOGD(ERROR, ao->domid, "can not create a TCP socket: %s",
-                 strerror(errno));
-            goto out;
-        }
-
-        cps->sock_fd = skfd;
-
-        if (connect(skfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-            LOGD(ERROR, ao->domid, "connect error");
-            goto out;
-        }
-
-        ret = colo_userspace_proxy_send(cps, (uint8_t *)sendbuf, strlen(sendbuf));
-        if (ret < 0)
-            goto out;
-
-        ret = colo_userspace_proxy_recv(cps, recvbuff, COLO_DEFAULT_WAIT_TIME);
-        if (ret < 0) {
-            LOGD(ERROR, ao->domid, "Can't recv msg from qemu colo-compare: %s",
-                 strerror(errno));
-            goto out;
-        }
-
-        return 0;
-    }
-
-    skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_COLO);
-    if (skfd < 0) {
-        LOGD(ERROR, ao->domid, "can not create a netlink socket: %s", strerror(errno));
-        goto out;
-    }
-    cps->sock_fd = skfd;
-    memset(&sa, 0, sizeof(sa));
-    sa.nl_family = AF_NETLINK;
-    sa.nl_groups = 0;
-retry:
-    sa.nl_pid = i++;
-
-    if (i > 10) {
-        LOGD(ERROR, ao->domid, "netlink bind error");
-        goto out;
-    }
-
-    ret = bind(skfd, (struct sockaddr *)&sa, sizeof(sa));
-    if (ret < 0 && errno == EADDRINUSE) {
-        LOGD(ERROR, ao->domid, "colo index %d has already in used", sa.nl_pid);
-        goto retry;
-    } else if (ret < 0) {
-        LOGD(ERROR, ao->domid, "netlink bind error");
-        goto out;
-    }
-
-    cps->index = sa.nl_pid;
-    ret = colo_proxy_send(cps, NULL, 0, COLO_PROXY_INIT);
-    if (ret < 0)
-        goto out;
-
-    /* receive ack */
-    size = colo_proxy_recv(cps, &buff, 500000);
-    if (size < 0) {
-        LOGD(ERROR, ao->domid, "Can't recv msg from kernel by netlink: %s",
-             strerror(errno));
-        goto out;
-    }
-
-    if (size) {
-        h = (struct nlmsghdr *)buff;
-        if (h->nlmsg_type == NLMSG_ERROR) {
-            /* ack's type is NLMSG_ERROR */
-            struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
-
-            if (size - sizeof(*h) < sizeof(*err)) {
-                LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
-                goto out;
-            }
-
-            if (err->error) {
-                LOGD(ERROR, ao->domid, "NLMSG_ERROR contains error %d", err->error);
-                goto out;
-            }
-        }
-    }
-
-    ret = 0;
-
-out:
-    free(buff);
-    if (ret) {
-        close(cps->sock_fd);
-        cps->sock_fd = -1;
-    }
-    return ret;
-}
-
-void colo_proxy_teardown(libxl__colo_proxy_state *cps)
-{
-    /*
-     * If enable userspace proxy mode,
-     * we don't need teardown kernel proxy
-     */
-    if (cps->is_userspace_proxy)
-        return;
-
-    if (cps->sock_fd >= 0) {
-        close(cps->sock_fd);
-        cps->sock_fd = -1;
-    }
-}
-
-/* ========= colo-proxy: preresume, postresume and checkpoint ========== */
-
-void colo_proxy_preresume(libxl__colo_proxy_state *cps)
-{
-    /*
-     * If enable userspace proxy mode,
-     * we don't need preresume kernel proxy
-     */
-    if (cps->is_userspace_proxy) {
-        const char sendbuf[] = "COLO_CHECKPOINT";
-        colo_userspace_proxy_send(cps,
-                                  (uint8_t *)sendbuf,
-                                  strlen(sendbuf));
-        return;
-    }
-
-    colo_proxy_send(cps, NULL, 0, COLO_CHECKPOINT);
-    /* TODO: need to handle if the call fails... */
-}
-
-void colo_proxy_postresume(libxl__colo_proxy_state *cps)
-{
-    /* nothing to do... */
-}
-
-typedef struct colo_msg {
-    bool is_checkpoint;
-} colo_msg;
-
-/*
- * Return value:
- * -1: error
- *  0: no checkpoint event is received before timeout
- *  1: do checkpoint
- */
-int colo_proxy_checkpoint(libxl__colo_proxy_state *cps,
-                          unsigned int timeout_us)
-{
-    uint8_t *buff = NULL;
-    int64_t size;
-    struct nlmsghdr *h;
-    struct colo_msg *m;
-    int ret = -1;
-    char recvbuff[1024];
-
-    STATE_AO_GC(cps->ao);
-
-    /*
-     * Enable userspace proxy to periodical checkpoint mode,
-     * sleeping temporarily for colo userspace proxy mode.
-     * then we will use socket recv instead of this usleep.
-     * In other words, we use socket communicate with Qemu
-     * Proxy part(colo-compare), for example, notify checkpoint
-     * event.
-     */
-    if (cps->is_userspace_proxy) {
-        ret = colo_userspace_proxy_recv(cps, recvbuff, timeout_us);
-        if (ret <= 0) {
-            ret = 0;
-            goto out;
-        }
-
-        if (!strcmp(recvbuff, "DO_CHECKPOINT")) {
-            ret = 1;
-        } else {
-            LOGD(ERROR, ao->domid, "receive qemu colo-compare checkpoint error");
-            ret = 0;
-        }
-        goto out;
-    }
-
-    size = colo_proxy_recv(cps, &buff, timeout_us);
-
-    /* timeout, return no checkpoint message. */
-    if (size <= 0) {
-        ret = 0;
-        goto out;
-    }
-
-    h = (struct nlmsghdr *) buff;
-
-    if (h->nlmsg_type == NLMSG_ERROR) {
-        LOGD(ERROR, ao->domid, "receive NLMSG_ERROR");
-        goto out;
-    }
-
-    if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*m))) {
-        LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short");
-        goto out;
-    }
-
-    m = NLMSG_DATA(h);
-
-    ret = m->is_checkpoint ? 1 : 0;
-
-out:
-    free(buff);
-    return ret;
-}
diff --git a/tools/libxl/libxl_colo_qdisk.c b/tools/libxl/libxl_colo_qdisk.c
deleted file mode 100644 (file)
index 4c017ca..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/* ========== init() and cleanup() ========== */
-
-int init_subkind_qdisk(libxl__checkpoint_devices_state *cds)
-{
-    /*
-     * We don't know if we use qemu block replication, so
-     * we cannot start block replication here.
-     */
-    return 0;
-}
-
-void cleanup_subkind_qdisk(libxl__checkpoint_devices_state *cds)
-{
-}
-
-/* ========== setup() and teardown() ========== */
-
-static void colo_qdisk_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
-                             bool primary)
-{
-    const libxl_device_disk *disk = dev->backend_dev;
-    int ret, rc = 0;
-    libxl__colo_qdisk *colo_qdisk = NULL;
-    char port[32];
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = dev->cds;
-    const char *host = disk->colo_host;
-    const char *export_name = disk->colo_export;
-    const int domid = cds->domid;
-
-    STATE_AO_GC(dev->cds->ao);
-
-    if (disk->backend != LIBXL_DISK_BACKEND_QDISK ||
-        !libxl_defbool_val(disk->colo_enable) ||
-        !host || !export_name || (disk->colo_port <= 0) ||
-        !disk->active_disk || !disk->hidden_disk) {
-        rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
-        goto out;
-    }
-
-    dev->matched = true;
-
-    GCNEW(colo_qdisk);
-    dev->concrete_data = colo_qdisk;
-
-    if (primary) {
-        libxl__colo_save_state *css = cds->concrete_data;
-
-        css->qdisk_used = true;
-        /* NBD server is not ready, so we cannot start block replication now */
-        goto out;
-    } else {
-        libxl__colo_restore_state *crs = cds->concrete_data;
-        sprintf(port, "%d", disk->colo_port);
-
-        if (!crs->qdisk_used) {
-            /* start nbd server */
-            ret = libxl__qmp_nbd_server_start(gc, domid, host, port);
-            if (ret) {
-                rc = ERROR_FAIL;
-                goto out;
-            }
-            crs->host = host;
-            crs->port = port;
-        } else {
-            if (strcmp(crs->host, host) || strcmp(crs->port, port)) {
-                LOGD(ERROR, domid, "The host and port of all disks must be the same");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-        }
-
-        crs->qdisk_used = true;
-
-        ret = libxl__qmp_nbd_server_add(gc, domid, export_name);
-        if (ret)
-            rc = ERROR_FAIL;
-
-        colo_qdisk->setuped = true;
-    }
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void colo_qdisk_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
-                                bool primary)
-{
-    int ret, rc = 0;
-    const libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
-    const libxl_device_disk *disk = dev->backend_dev;
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = dev->cds;
-    const int domid = cds->domid;
-    const char *export_name = disk->colo_export;
-
-    EGC_GC;
-
-    if (primary) {
-        if (!colo_qdisk->setuped)
-            goto out;
-
-        /*
-         * There is no way to get the child name, but we know it is children.1
-         */
-        ret = libxl__qmp_x_blockdev_change(gc, domid, export_name,
-                                           "children.1", NULL);
-        if (ret)
-            rc = ERROR_FAIL;
-    } else {
-        libxl__colo_restore_state *crs = cds->concrete_data;
-
-        if (crs->qdisk_used) {
-            ret = libxl__qmp_nbd_server_stop(gc, domid);
-            if (ret)
-                rc = ERROR_FAIL;
-        }
-    }
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-/* ========== checkpointing APIs ========== */
-
-static void colo_qdisk_save_preresume(libxl__egc *egc,
-                                      libxl__checkpoint_device *dev)
-{
-    libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
-    const libxl_device_disk *disk = dev->backend_dev;
-    int ret, rc = 0;
-    char *node = NULL;
-    char *cmd = NULL;
-
-    /* Convenience aliases */
-    const int domid = dev->cds->domid;
-    const char *host = disk->colo_host;
-    int port = disk->colo_port;
-    const char *export_name = disk->colo_export;
-
-    EGC_GC;
-
-    if (colo_qdisk->setuped)
-        goto out;
-
-    /* qmp command doesn't support the driver "nbd" */
-    node = GCSPRINTF("colo_node%d",
-                     libxl__device_disk_dev_number(disk->vdev, NULL, NULL));
-    cmd = GCSPRINTF("drive_add -n buddy driver=replication,mode=primary,"
-                    "file.driver=nbd,file.host=%s,file.port=%d,"
-                    "file.export=%s,node-name=%s",
-                    host, port, export_name, node);
-    ret = libxl__qmp_hmp(gc, domid, cmd, NULL);
-    if (ret)
-        rc = ERROR_FAIL;
-
-    ret = libxl__qmp_x_blockdev_change(gc, domid, export_name, NULL, node);
-    if (ret)
-        rc = ERROR_FAIL;
-
-    colo_qdisk->setuped = true;
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-/* ======== primary ======== */
-
-static void colo_qdisk_save_setup(libxl__egc *egc,
-                                  libxl__checkpoint_device *dev)
-{
-    colo_qdisk_setup(egc, dev, true);
-}
-
-static void colo_qdisk_save_teardown(libxl__egc *egc,
-                                   libxl__checkpoint_device *dev)
-{
-    colo_qdisk_teardown(egc, dev, true);
-}
-
-const libxl__checkpoint_device_instance_ops colo_save_device_qdisk = {
-    .kind = LIBXL__DEVICE_KIND_VBD,
-    .setup = colo_qdisk_save_setup,
-    .teardown = colo_qdisk_save_teardown,
-    .preresume = colo_qdisk_save_preresume,
-};
-
-/* ======== secondary ======== */
-
-static void colo_qdisk_restore_setup(libxl__egc *egc,
-                                     libxl__checkpoint_device *dev)
-{
-    colo_qdisk_setup(egc, dev, false);
-}
-
-static void colo_qdisk_restore_teardown(libxl__egc *egc,
-                                      libxl__checkpoint_device *dev)
-{
-    colo_qdisk_teardown(egc, dev, false);
-}
-
-const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk = {
-    .kind = LIBXL__DEVICE_KIND_VBD,
-    .setup = colo_qdisk_restore_setup,
-    .teardown = colo_qdisk_restore_teardown,
-};
diff --git a/tools/libxl/libxl_colo_restore.c b/tools/libxl/libxl_colo_restore.c
deleted file mode 100644 (file)
index aa36567..0000000
+++ /dev/null
@@ -1,1093 +0,0 @@
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *         Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-#include "libxl_sr_stream_format.h"
-
-typedef struct libxl__colo_restore_checkpoint_state libxl__colo_restore_checkpoint_state;
-struct libxl__colo_restore_checkpoint_state {
-    libxl__domain_suspend_state dsps;
-    libxl__logdirty_switch lds;
-    libxl__colo_restore_state *crs;
-    libxl__stream_write_state sws;
-    int status;
-    bool preresume;
-    /* used for teardown */
-    int teardown_devices;
-    int saved_rc;
-    char *state_file;
-
-    void (*callback)(libxl__egc *,
-                     libxl__colo_restore_checkpoint_state *,
-                     int);
-};
-
-extern const libxl__checkpoint_device_instance_ops colo_restore_device_nic;
-extern const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk;
-
-static const libxl__checkpoint_device_instance_ops *colo_restore_ops[] = {
-    &colo_restore_device_nic,
-    &colo_restore_device_qdisk,
-    NULL,
-};
-
-/* ===================== colo: common functions ===================== */
-
-static void colo_enable_logdirty(libxl__colo_restore_state *crs, libxl__egc *egc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
-    /* Convenience aliases */
-    const uint32_t domid = crs->domid;
-    libxl__logdirty_switch *const lds = &crcs->lds;
-
-    EGC_GC;
-
-    /* we need to know which pages are dirty to restore the guest */
-    if (xc_shadow_control(CTX->xch, domid,
-                          XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
-                          NULL, 0, NULL, 0, NULL) < 0) {
-        LOGD(ERROR, domid, "cannot enable secondary vm's logdirty");
-        lds->callback(egc, lds, ERROR_FAIL);
-        return;
-    }
-
-    if (crs->hvm) {
-        libxl__domain_common_switch_qemu_logdirty(egc, domid, 1, lds);
-        return;
-    }
-
-    lds->callback(egc, lds, 0);
-}
-
-static void colo_disable_logdirty(libxl__colo_restore_state *crs,
-                                  libxl__egc *egc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
-    /* Convenience aliases */
-    const uint32_t domid = crs->domid;
-    libxl__logdirty_switch *const lds = &crcs->lds;
-
-    EGC_GC;
-
-    /* we need to know which pages are dirty to restore the guest */
-    if (xc_shadow_control(CTX->xch, domid, XEN_DOMCTL_SHADOW_OP_OFF,
-                          NULL, 0, NULL, 0, NULL) < 0)
-        LOGD(WARN, domid, "cannot disable secondary vm's logdirty");
-
-    if (crs->hvm) {
-        libxl__domain_common_switch_qemu_logdirty(egc, domid, 0, lds);
-        return;
-    }
-
-    lds->callback(egc, lds, 0);
-}
-
-static void colo_resume_vm(libxl__egc *egc,
-                           libxl__colo_restore_checkpoint_state *crcs,
-                           int restore_device_model)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-    int rc;
-
-    /* Convenience aliases */
-    libxl__colo_restore_state *const crs = crcs->crs;
-
-    EGC_GC;
-
-    if (!crs->saved_cb) {
-        /* TODO: sync mmu for hvm? */
-        if (restore_device_model) {
-            rc = libxl__qmp_restore(gc, crs->domid, crcs->state_file);
-            if (rc) {
-                LOGD(ERROR, crs->domid,
-                     "cannot restore device model for secondary vm");
-                crcs->callback(egc, crcs, rc);
-                return;
-            }
-        }
-        rc = libxl__domain_resume_deprecated(gc, crs->domid, 0);
-        if (rc)
-            LOGD(ERROR, crs->domid, "cannot resume secondary vm");
-
-        crcs->callback(egc, crcs, rc);
-        return;
-    }
-
-    libxl__xc_domain_restore_done(egc, dcs, 0, 0, 0);
-
-    return;
-}
-
-static int init_device_subkind(libxl__checkpoint_devices_state *cds)
-{
-    /* init device subkind-specific state in the libxl ctx */
-    int rc;
-    STATE_AO_GC(cds->ao);
-
-    rc = init_subkind_colo_nic(cds);
-    if (rc) goto out;
-
-    rc = init_subkind_qdisk(cds);
-    if (rc) {
-        cleanup_subkind_colo_nic(cds);
-        goto out;
-    }
-
-    rc = 0;
-out:
-    return rc;
-}
-
-static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
-{
-    /* cleanup device subkind-specific state in the libxl ctx */
-    STATE_AO_GC(cds->ao);
-
-    cleanup_subkind_colo_nic(cds);
-    cleanup_subkind_qdisk(cds);
-}
-
-/* ================ colo: setup restore environment ================ */
-
-static void libxl__colo_domain_create_cb(libxl__egc *egc,
-                                         libxl__domain_create_state *dcs,
-                                         int rc, uint32_t domid);
-
-static int init_dsps(libxl__domain_suspend_state *dsps)
-{
-    int rc = ERROR_FAIL;
-    libxl_domain_type type;
-
-    STATE_AO_GC(dsps->ao);
-
-    libxl__xswait_init(&dsps->pvcontrol);
-    libxl__ev_evtchn_init(&dsps->guest_evtchn);
-    libxl__ev_xswatch_init(&dsps->guest_watch);
-    libxl__ev_time_init(&dsps->guest_timeout);
-
-    type = libxl__domain_type(gc, dsps->domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID)
-        goto out;
-
-    dsps->type = type;
-
-    dsps->guest_evtchn.port = -1;
-    dsps->guest_evtchn_lockfd = -1;
-    dsps->guest_responded = 0;
-    dsps->dm_savefile = libxl__device_model_savefile(gc, dsps->domid);
-
-    /* Secondary vm is not created, so we cannot get evtchn port */
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-/*
- * checkpoint callbacks are called in the following order:
- * 1. resume
- * 2. wait checkpoint
- * 3. suspend
- * 4. checkpoint
- */
-static void libxl__colo_restore_domain_resume_callback(void *data);
-static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data);
-static void libxl__colo_restore_domain_suspend_callback(void *data);
-static void libxl__colo_restore_domain_checkpoint_callback(void *data);
-
-void libxl__colo_restore_setup(libxl__egc *egc,
-                               libxl__colo_restore_state *crs)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs;
-    int rc = ERROR_FAIL;
-
-    /* Convenience aliases */
-    libxl__srm_restore_autogen_callbacks *const callbacks =
-        &dcs->srs.shs.callbacks.restore.a;
-    const int domid = crs->domid;
-
-    STATE_AO_GC(crs->ao);
-
-    GCNEW(crcs);
-    crs->crcs = crcs;
-    crcs->crs = crs;
-    crs->qdisk_setuped = false;
-    crs->qdisk_used = false;
-    if (dcs->colo_proxy_script)
-        crs->colo_proxy_script = libxl__strdup(gc, dcs->colo_proxy_script);
-    else
-        crs->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
-                                           libxl__xen_script_dir_path());
-
-    /* setup dsps */
-    crcs->dsps.ao = ao;
-    crcs->dsps.domid = domid;
-    if (init_dsps(&crcs->dsps))
-        goto out;
-
-    callbacks->postcopy = libxl__colo_restore_domain_resume_callback;
-    callbacks->wait_checkpoint = libxl__colo_restore_domain_wait_checkpoint_callback;
-    callbacks->suspend = libxl__colo_restore_domain_suspend_callback;
-    callbacks->checkpoint = libxl__colo_restore_domain_checkpoint_callback;
-
-    /*
-     * Secondary vm is running in colo mode, so we need to call
-     * libxl__xc_domain_restore_done() to create secondary vm.
-     * But we will exit in domain_create_cb(). So replace the
-     * callback here.
-     */
-    crs->saved_cb = dcs->callback;
-    dcs->callback = libxl__colo_domain_create_cb;
-    crcs->state_file = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
-    crcs->status = LIBXL_COLO_SETUPED;
-
-    libxl__logdirty_init(&crcs->lds);
-    crcs->lds.ao = ao;
-
-    crcs->sws.fd = crs->send_back_fd;
-    crcs->sws.ao = ao;
-    crcs->sws.back_channel = true;
-
-    dcs->cds.concrete_data = crs;
-
-    libxl__stream_write_start(egc, &crcs->sws);
-
-    rc = 0;
-
-out:
-    crs->callback(egc, crs, rc);
-    return;
-}
-
-static void libxl__colo_domain_create_cb(libxl__egc *egc,
-                                         libxl__domain_create_state *dcs,
-                                         int rc, uint32_t domid)
-{
-    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
-    crcs->callback(egc, crcs, rc);
-}
-
-/* ================ colo: teardown restore environment ================ */
-
-static void colo_restore_teardown_devices_done(libxl__egc *egc,
-    libxl__checkpoint_devices_state *cds, int rc);
-static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs);
-static void do_failover_done(libxl__egc *egc,
-                             libxl__colo_restore_checkpoint_state* crcs,
-                             int rc);
-static void colo_disable_logdirty_done(libxl__egc *egc,
-                                       libxl__logdirty_switch *lds,
-                                       int rc);
-static void libxl__colo_restore_teardown_done(libxl__egc *egc,
-                                              libxl__colo_restore_state *crs,
-                                              int rc);
-
-void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
-                                  int ret, int retval, int errnoval)
-{
-    libxl__domain_create_state *dcs = dcs_void;
-    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-    int rc = 1;
-
-    /* convenience aliases */
-    libxl__colo_restore_state *const crs = &dcs->crs;
-    EGC_GC;
-
-    if (ret == 0 && retval == 0)
-        rc = 0;
-
-    LOGD(INFO, crs->domid, "%s", rc ? "colo fails" : "failover");
-
-    libxl__stream_write_abort(egc, &crcs->sws, 1);
-    if (crs->saved_cb) {
-        /* crcs->status is LIBXL_COLO_SETUPED */
-        dcs->srs.completion_callback = NULL;
-    }
-    libxl__xc_domain_restore_done(egc, dcs, ret, retval, errnoval);
-
-    if (crs->qdisk_setuped) {
-        libxl__qmp_stop_replication(gc, crs->domid, false);
-        crs->qdisk_setuped = false;
-    }
-
-    crcs->saved_rc = rc;
-    if (!crcs->teardown_devices) {
-        colo_restore_teardown_devices_done(egc, &dcs->cds, 0);
-        return;
-    }
-
-    dcs->cds.callback = colo_restore_teardown_devices_done;
-    libxl__checkpoint_devices_teardown(egc, &dcs->cds);
-}
-
-static void colo_restore_teardown_devices_done(libxl__egc *egc,
-    libxl__checkpoint_devices_state *cds, int rc)
-{
-    libxl__colo_restore_state *crs = cds->concrete_data;
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-
-    EGC_GC;
-
-    if (rc)
-        LOGD(ERROR, cds->domid, "COLO: failed to teardown device for guest,"
-            " rc %d", rc);
-
-    if (crcs->teardown_devices)
-        cleanup_device_subkind(cds);
-
-    colo_proxy_teardown(&crs->cps);
-
-    rc = crcs->saved_rc;
-    if (!rc) {
-        crcs->callback = do_failover_done;
-        do_failover(egc, crs);
-        return;
-    }
-
-    libxl__colo_restore_teardown_done(egc, crs, rc);
-}
-
-static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs)
-{
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
-    /* Convenience aliases */
-    const int status = crcs->status;
-    libxl__logdirty_switch *const lds = &crcs->lds;
-
-    EGC_GC;
-
-    switch(status) {
-    case LIBXL_COLO_SETUPED:
-        /*
-         * We will come here only when reading emulator xenstore data or
-         * emulator context fails, and libxl__xc_domain_restore_done()
-         * is not called. In this case, the migration is not finished,
-         * so we cannot do failover.
-         */
-        LOGD(ERROR, crs->domid, "migration fails");
-        crcs->callback(egc, crcs, ERROR_FAIL);
-        return;
-    case LIBXL_COLO_SUSPENDED:
-    case LIBXL_COLO_RESUMED:
-        /* disable logdirty first */
-        lds->callback = colo_disable_logdirty_done;
-        colo_disable_logdirty(crs, egc);
-        return;
-    default:
-        LOGD(ERROR, crs->domid, "invalid status: %d", status);
-        crcs->callback(egc, crcs, ERROR_FAIL);
-    }
-}
-
-static void do_failover_done(libxl__egc *egc,
-                             libxl__colo_restore_checkpoint_state* crcs,
-                             int rc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
-    /* Convenience aliases */
-    libxl__colo_restore_state *const crs = crcs->crs;
-
-    EGC_GC;
-
-    if (rc)
-        LOGD(ERROR, crs->domid, "cannot do failover");
-
-    libxl__colo_restore_teardown_done(egc, crs, rc);
-}
-
-static void colo_disable_logdirty_done(libxl__egc *egc,
-                                       libxl__logdirty_switch *lds,
-                                       int rc)
-{
-    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
-
-    EGC_GC;
-
-    if (rc)
-        LOGD(WARN, crcs->crs->domid, "cannot disable logdirty");
-
-    if (crcs->status == LIBXL_COLO_SUSPENDED) {
-        /*
-         * failover when reading state from master, so no need to
-         * call libxl__qmp_restore().
-         */
-        colo_resume_vm(egc, crcs, 0);
-        return;
-    }
-
-    /* If we cannot disable logdirty, we still can do failover */
-    crcs->callback(egc, crcs, 0);
-}
-
-static void libxl__colo_restore_teardown_done(libxl__egc *egc,
-                                              libxl__colo_restore_state *crs,
-                                              int rc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    EGC_GC;
-
-    /* convenience aliases */
-    const int domid = crs->domid;
-    const libxl_ctx *const ctx = libxl__gc_owner(gc);
-    xc_interface *const xch = ctx->xch;
-
-    if (!rc)
-        /* failover, no need to destroy the secondary vm */
-        goto out;
-
-    xc_domain_destroy(xch, domid);
-
-out:
-    if (crs->saved_cb) {
-        dcs->callback = crs->saved_cb;
-        crs->saved_cb = NULL;
-    }
-
-    dcs->callback(egc, dcs, rc, crs->domid);
-}
-
-static void colo_common_write_stream_done(libxl__egc *egc,
-                                          libxl__stream_write_state *stream,
-                                          int rc);
-static void colo_common_read_stream_done(libxl__egc *egc,
-                                         libxl__stream_read_state *stream,
-                                         int rc);
-
-/* ======================== colo: checkpoint ======================= */
-
-/*
- * Do the following things when resuming secondary vm:
- *  1. read emulator xenstore data
- *  2. read emulator context
- *  3. REC_TYPE_CHECKPOINT_END
- */
-static void libxl__colo_restore_domain_checkpoint_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
-    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
-    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
-    crcs->callback = NULL;
-    dcs->srs.checkpoint_callback = colo_common_read_stream_done;
-    libxl__stream_read_start_checkpoint(shs->egc, &dcs->srs);
-}
-
-/* ===================== colo: resume secondary vm ===================== */
-
-/*
- * Do the following things when resuming secondary vm the first time:
- *  1. resume secondary vm
- *  2. enable log dirty
- *  3. setup checkpoint devices
- *  4. write CHECKPOINT_SVM_READY
- *  5. unpause secondary vm
- *  6. write CHECKPOINT_SVM_RESUMED
- *
- * Do the following things when resuming secondary vm:
- *  1. write CHECKPOINT_SVM_READY
- *  2. resume secondary vm
- *  3. write CHECKPOINT_SVM_RESUMED
- */
-static void colo_send_svm_ready(libxl__egc *egc,
-                                libxl__colo_restore_checkpoint_state *crcs);
-static void colo_send_svm_ready_done(libxl__egc *egc,
-                                     libxl__colo_restore_checkpoint_state *crcs,
-                                     int rc);
-static void colo_restore_preresume_cb(libxl__egc *egc,
-                                      libxl__checkpoint_devices_state *cds,
-                                      int rc);
-static void colo_restore_resume_vm(libxl__egc *egc,
-                                   libxl__colo_restore_checkpoint_state *crcs);
-static void colo_resume_vm_done(libxl__egc *egc,
-                                libxl__colo_restore_checkpoint_state *crcs,
-                                int rc);
-static void colo_write_svm_resumed(libxl__egc *egc,
-                                   libxl__colo_restore_checkpoint_state *crcs);
-static void colo_enable_logdirty_done(libxl__egc *egc,
-                                      libxl__logdirty_switch *lds,
-                                      int retval);
-static void colo_reenable_logdirty(libxl__egc *egc,
-                                   libxl__logdirty_switch *lds,
-                                   int rc);
-static void colo_reenable_logdirty_done(libxl__egc *egc,
-                                        libxl__logdirty_switch *lds,
-                                        int rc);
-static void colo_setup_checkpoint_devices(libxl__egc *egc,
-                                          libxl__colo_restore_state *crs);
-static void colo_restore_setup_cds_done(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds,
-                                        int rc);
-static void colo_unpause_svm(libxl__egc *egc,
-                             libxl__colo_restore_checkpoint_state *crcs);
-
-static void libxl__colo_restore_domain_resume_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
-    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
-    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
-    if (crcs->teardown_devices)
-        colo_send_svm_ready(shs->egc, crcs);
-    else
-        colo_restore_resume_vm(shs->egc, crcs);
-}
-
-static void colo_send_svm_ready(libxl__egc *egc,
-                               libxl__colo_restore_checkpoint_state *crcs)
-{
-    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_READY };
-
-    crcs->callback = colo_send_svm_ready_done;
-    crcs->sws.checkpoint_callback = colo_common_write_stream_done;
-    libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
-}
-
-static void colo_send_svm_ready_done(libxl__egc *egc,
-                                     libxl__colo_restore_checkpoint_state *crcs,
-                                     int rc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *cds = &dcs->cds;
-
-    if (!crcs->preresume) {
-        crcs->preresume = true;
-        colo_unpause_svm(egc, crcs);
-        return;
-    }
-
-    cds->callback = colo_restore_preresume_cb;
-    libxl__checkpoint_devices_preresume(egc, cds);
-}
-
-static void colo_restore_preresume_cb(libxl__egc *egc,
-                                      libxl__checkpoint_devices_state *cds,
-                                      int rc)
-{
-    libxl__colo_restore_state *crs = cds->concrete_data;
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
-    /* Convenience aliases */
-    libxl__save_helper_state *const shs = &dcs->srs.shs;
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, crs->domid, "preresume fails");
-        goto out;
-    }
-
-    if (crs->qdisk_setuped) {
-        if (libxl__qmp_colo_do_checkpoint(gc, crs->domid)) {
-            LOGD(ERROR, crs->domid, "doing checkpoint fails");
-            goto out;
-        }
-    }
-
-    if (!crs->cps.is_userspace_proxy)
-        colo_proxy_preresume(&crs->cps);
-
-    colo_restore_resume_vm(egc, crcs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_restore_resume_vm(libxl__egc *egc,
-                                   libxl__colo_restore_checkpoint_state *crcs)
-{
-
-    crcs->callback = colo_resume_vm_done;
-    colo_resume_vm(egc, crcs, 1);
-}
-
-static void colo_resume_vm_done(libxl__egc *egc,
-                                libxl__colo_restore_checkpoint_state *crcs,
-                                int rc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
-    /* Convenience aliases */
-    libxl__colo_restore_state *const crs = crcs->crs;
-    libxl__logdirty_switch *const lds = &crcs->lds;
-    libxl__save_helper_state *const shs = &dcs->srs.shs;
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, crs->domid, "cannot resume secondary vm");
-        goto out;
-    }
-
-    crcs->status = LIBXL_COLO_RESUMED;
-
-    colo_proxy_postresume(&crs->cps);
-
-    /* avoid calling stream->completion_callback() more than once */
-    if (crs->saved_cb) {
-        dcs->callback = crs->saved_cb;
-        crs->saved_cb = NULL;
-
-        dcs->srs.completion_callback = NULL;
-
-        lds->callback = colo_enable_logdirty_done;
-        colo_enable_logdirty(crs, egc);
-        return;
-    }
-
-    colo_write_svm_resumed(egc, crcs);
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_write_svm_resumed(libxl__egc *egc,
-                                   libxl__colo_restore_checkpoint_state *crcs)
-{
-    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_RESUMED };
-
-    crcs->callback = NULL;
-    crcs->sws.checkpoint_callback = colo_common_write_stream_done;
-    libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
-}
-
-static void colo_enable_logdirty_done(libxl__egc *egc,
-                                      libxl__logdirty_switch *lds,
-                                      int rc)
-{
-    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
-
-    /* Convenience aliases */
-    libxl__colo_restore_state *const crs = crcs->crs;
-
-    EGC_GC;
-
-    if (rc) {
-        /*
-         * log-dirty already enabled? There's no test op,
-         * so attempt to disable then reenable it
-         */
-        lds->callback = colo_reenable_logdirty;
-        colo_disable_logdirty(crs, egc);
-        return;
-    }
-
-    colo_setup_checkpoint_devices(egc, crs);
-}
-
-static void colo_reenable_logdirty(libxl__egc *egc,
-                                   libxl__logdirty_switch *lds,
-                                   int rc)
-{
-    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
-    /* Convenience aliases */
-    libxl__colo_restore_state *const crs = crcs->crs;
-    libxl__save_helper_state *const shs = &dcs->srs.shs;
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, crs->domid, "cannot enable logdirty");
-        goto out;
-    }
-
-    lds->callback = colo_reenable_logdirty_done;
-    colo_enable_logdirty(crs, egc);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_reenable_logdirty_done(libxl__egc *egc,
-                                        libxl__logdirty_switch *lds,
-                                        int rc)
-{
-    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds);
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-
-    /* Convenience aliases */
-    libxl__save_helper_state *const shs = &dcs->srs.shs;
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, crcs->crs->domid, "cannot enable logdirty");
-        goto out;
-    }
-
-    colo_setup_checkpoint_devices(egc, crcs->crs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-/*
- * We cannot setup checkpoint devices in libxl__colo_restore_setup(),
- * because the guest is not ready.
- */
-static void colo_setup_checkpoint_devices(libxl__egc *egc,
-                                          libxl__colo_restore_state *crs)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *cds = &dcs->cds;
-    libxl__save_helper_state *const shs = &dcs->srs.shs;
-
-    STATE_AO_GC(crs->ao);
-
-    if (crs->cps.is_userspace_proxy)
-        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
-    else
-        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
-                                 (1 << LIBXL__DEVICE_KIND_VBD);
-
-    cds->callback = colo_restore_setup_cds_done;
-    cds->ao = ao;
-    cds->domid = crs->domid;
-    cds->ops = colo_restore_ops;
-
-    crs->cps.ao = ao;
-    if (!crs->cps.is_userspace_proxy) {
-        if (colo_proxy_setup(&crs->cps)) {
-            LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
-            goto out;
-        }
-    }
-
-    if (init_device_subkind(cds))
-        goto out;
-
-    crcs->teardown_devices = 1;
-
-    libxl__checkpoint_devices_setup(egc, cds);
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_restore_setup_cds_done(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds,
-                                        int rc)
-{
-    libxl__colo_restore_state *crs = cds->concrete_data;
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
-    /* Convenience aliases */
-    libxl__save_helper_state *const shs = &dcs->srs.shs;
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, cds->domid, "COLO: failed to setup device for guest");
-        goto out;
-    }
-
-    if (crs->qdisk_used && !crs->qdisk_setuped) {
-        if (libxl__qmp_start_replication(gc, crs->domid, false)) {
-            LOGD(ERROR, cds->domid, "starting replication fails");
-            goto out;
-        }
-        crs->qdisk_setuped = true;
-    }
-
-    colo_send_svm_ready(egc, crcs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-static void colo_unpause_svm(libxl__egc *egc,
-                             libxl__colo_restore_checkpoint_state *crcs)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-    int rc;
-
-    /* Convenience aliases */
-    const uint32_t domid = crcs->crs->domid;
-    libxl__save_helper_state *const shs = &dcs->srs.shs;
-
-    EGC_GC;
-
-    /* We have enabled secondary vm's logdirty, so we can unpause it now */
-    rc = libxl__domain_unpause_deprecated(gc, domid);
-    if (rc) {
-        LOGD(ERROR, domid, "cannot unpause secondary vm");
-        goto out;
-    }
-
-    colo_write_svm_resumed(egc, crcs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0);
-}
-
-/* ===================== colo: wait new checkpoint ===================== */
-
-static void colo_restore_commit_cb(libxl__egc *egc,
-                                   libxl__checkpoint_devices_state *cds,
-                                   int rc);
-static void colo_stream_read_done(libxl__egc *egc,
-                                  libxl__colo_restore_checkpoint_state *crcs,
-                                  int real_size);
-
-static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
-    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *cds = &dcs->cds;
-
-    cds->callback = colo_restore_commit_cb;
-    libxl__checkpoint_devices_commit(shs->egc, cds);
-}
-
-static void colo_restore_commit_cb(libxl__egc *egc,
-                                   libxl__checkpoint_devices_state *cds,
-                                   int rc)
-{
-    libxl__colo_restore_state *crs = cds->concrete_data;
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, crs->domid, "commit fails");
-        goto out;
-    }
-
-    crcs->callback = colo_stream_read_done;
-    dcs->srs.checkpoint_callback = colo_common_read_stream_done;
-    libxl__stream_read_checkpoint_state(egc, &dcs->srs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
-}
-
-static void colo_stream_read_done(libxl__egc *egc,
-                                  libxl__colo_restore_checkpoint_state *crcs,
-                                  int id)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-    int ok = 0;
-
-    EGC_GC;
-
-    if (id != CHECKPOINT_NEW) {
-        LOGD(ERROR, crcs->crs->domid, "invalid section: %d", id);
-        goto out;
-    }
-
-    ok = 1;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
-}
-
-/* ===================== colo: suspend secondary vm ===================== */
-
-/*
- * Do the following things when resuming secondary vm:
- *  1. suspend secondary vm
- *  2. send CHECKPOINT_SVM_SUSPENDED
- */
-static void colo_suspend_vm_done(libxl__egc *egc,
-                                 libxl__domain_suspend_state *dsps,
-                                 int ok);
-static void colo_restore_postsuspend_cb(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds,
-                                        int rc);
-
-static void libxl__colo_restore_domain_suspend_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs);
-    libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs);
-    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-
-    STATE_AO_GC(dcs->ao);
-
-    /* Convenience aliases */
-    libxl__domain_suspend_state *const dsps = &crcs->dsps;
-
-    /* suspend secondary vm */
-    dsps->callback_common_done = colo_suspend_vm_done;
-
-    libxl__domain_suspend(shs->egc, dsps);
-}
-
-static void colo_suspend_vm_done(libxl__egc *egc,
-                                 libxl__domain_suspend_state *dsps,
-                                 int rc)
-{
-    libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(dsps, *crcs, dsps);
-    libxl__colo_restore_state *crs = crcs->crs;
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *cds = &dcs->cds;
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, crs->domid, "cannot suspend secondary vm");
-        goto out;
-    }
-
-    crcs->status = LIBXL_COLO_SUSPENDED;
-
-    if (libxl__qmp_query_xen_replication_status(gc, crs->domid)) {
-        LOGD(ERROR, crs->domid, "replication error occurs when secondary vm is running");
-        goto out;
-    }
-
-    cds->callback = colo_restore_postsuspend_cb;
-    libxl__checkpoint_devices_postsuspend(egc, cds);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0);
-}
-
-static void colo_restore_postsuspend_cb(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds,
-                                        int rc)
-{
-    libxl__colo_restore_state *crs = cds->concrete_data;
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-    libxl__colo_restore_checkpoint_state *crcs = crs->crcs;
-    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_SUSPENDED };
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, crs->domid, "postsuspend fails");
-        goto out;
-    }
-
-    crcs->callback = NULL;
-    crcs->sws.checkpoint_callback = colo_common_write_stream_done;
-    libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, !rc);
-}
-
-/* ===================== colo: common callback ===================== */
-
-static void colo_common_write_stream_done(libxl__egc *egc,
-                                          libxl__stream_write_state *stream,
-                                          int rc)
-{
-    libxl__colo_restore_checkpoint_state *crcs =
-        CONTAINER_OF(stream, *crcs, sws);
-    libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs);
-    int ok;
-
-    EGC_GC;
-
-    if (rc < 0) {
-        /* TODO: it may be a internal error, but we don't know */
-        LOGD(ERROR, crcs->crs->domid, "sending data fails");
-        ok = 2;
-        goto out;
-    }
-
-    if (!crcs->callback) {
-        /* Everythins is OK */
-        ok = 1;
-        goto out;
-    }
-
-    crcs->callback(egc, crcs, 0);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
-}
-
-static void colo_common_read_stream_done(libxl__egc *egc,
-                                         libxl__stream_read_state *stream,
-                                         int rc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(stream, *dcs, srs);
-    libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs;
-    int ok;
-
-    EGC_GC;
-
-    if (rc < 0) {
-        /* TODO: it may be a internal error, but we don't know */
-        LOGD(ERROR, crcs->crs->domid, "reading data fails");
-        ok = 2;
-        goto out;
-    }
-
-    if (!crcs->callback) {
-        /* Everythins is OK */
-        ok = 1;
-        goto out;
-    }
-
-    /* rc contains the id */
-    crcs->callback(egc, crcs, rc);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok);
-}
diff --git a/tools/libxl/libxl_colo_save.c b/tools/libxl/libxl_colo_save.c
deleted file mode 100644 (file)
index b47f038..0000000
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * Copyright (C) 2016 FUJITSU LIMITED
- * Author: Wen Congyang <wency@cn.fujitsu.com>
- *         Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-extern const libxl__checkpoint_device_instance_ops colo_save_device_nic;
-extern const libxl__checkpoint_device_instance_ops colo_save_device_qdisk;
-
-static const libxl__checkpoint_device_instance_ops *colo_ops[] = {
-    &colo_save_device_nic,
-    &colo_save_device_qdisk,
-    NULL,
-};
-
-/* ================= helper functions ================= */
-
-static int init_device_subkind(libxl__checkpoint_devices_state *cds)
-{
-    /* init device subkind-specific state in the libxl ctx */
-    int rc;
-    STATE_AO_GC(cds->ao);
-
-    rc = init_subkind_colo_nic(cds);
-    if (rc) goto out;
-
-    rc = init_subkind_qdisk(cds);
-    if (rc) {
-        cleanup_subkind_colo_nic(cds);
-        goto out;
-    }
-
-    rc = 0;
-out:
-    return rc;
-}
-
-static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
-{
-    /* cleanup device subkind-specific state in the libxl ctx */
-    STATE_AO_GC(cds->ao);
-
-    cleanup_subkind_colo_nic(cds);
-    cleanup_subkind_qdisk(cds);
-}
-
-/* ================= colo: setup save environment ================= */
-
-static void colo_save_setup_done(libxl__egc *egc,
-                                 libxl__checkpoint_devices_state *cds,
-                                 int rc);
-static void colo_save_setup_failed(libxl__egc *egc,
-                                   libxl__checkpoint_devices_state *cds,
-                                   int rc);
-/*
- * checkpoint callbacks are called in the following order:
- * 1. suspend
- * 2. checkpoint
- * 3. resume
- * 4. wait checkpoint
- */
-static void libxl__colo_save_domain_suspend_callback(void *data);
-static void libxl__colo_save_domain_checkpoint_callback(void *data);
-static void libxl__colo_save_domain_resume_callback(void *data);
-static void libxl__colo_save_domain_wait_checkpoint_callback(void *data);
-
-void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-    libxl__srm_save_autogen_callbacks *const callbacks =
-        &dss->sws.shs.callbacks.save.a;
-
-    STATE_AO_GC(dss->ao);
-
-    if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
-        LOGD(ERROR, dss->domid, "COLO only supports hvm now");
-        goto out;
-    }
-
-    css->send_fd = dss->fd;
-    css->recv_fd = dss->recv_fd;
-    css->svm_running = false;
-    css->paused = true;
-    css->qdisk_setuped = false;
-    css->qdisk_used = false;
-    libxl__ev_child_init(&css->child);
-    css->cps.is_userspace_proxy =
-        libxl_defbool_val(dss->remus->userspace_colo_proxy);
-
-    if (dss->remus->netbufscript)
-        css->colo_proxy_script = libxl__strdup(gc, dss->remus->netbufscript);
-    else
-        css->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup",
-                                           libxl__xen_script_dir_path());
-
-    cds->ops = colo_ops;
-    cds->callback = colo_save_setup_done;
-    cds->ao = ao;
-    cds->domid = dss->domid;
-    cds->concrete_data = css;
-
-    /* If enable userspace proxy mode, we don't need VIF */
-    if (css->cps.is_userspace_proxy) {
-        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD);
-
-        /* Use this args we can connect to qemu colo-compare */
-        cds->nics = libxl__device_list(gc, &libxl__nic_devtype,
-                                       cds->domid, &cds->num_nics);
-        if (cds->num_nics > 0) {
-            css->cps.checkpoint_host = cds->nics[0].colo_checkpoint_host;
-            css->cps.checkpoint_port = cds->nics[0].colo_checkpoint_port;
-        }
-    } else {
-        cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) |
-                                 (1 << LIBXL__DEVICE_KIND_VBD);
-    }
-
-    css->srs.ao = ao;
-    css->srs.fd = css->recv_fd;
-    css->srs.back_channel = true;
-    libxl__stream_read_start(egc, &css->srs);
-    css->cps.ao = ao;
-    if (colo_proxy_setup(&css->cps)) {
-        LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest");
-        goto out;
-    }
-
-    if (init_device_subkind(cds))
-        goto out;
-
-    callbacks->suspend = libxl__colo_save_domain_suspend_callback;
-    callbacks->checkpoint = libxl__colo_save_domain_checkpoint_callback;
-    callbacks->postcopy = libxl__colo_save_domain_resume_callback;
-    callbacks->wait_checkpoint = libxl__colo_save_domain_wait_checkpoint_callback;
-
-    libxl__checkpoint_devices_setup(egc, &dss->cds);
-
-    return;
-
-out:
-    dss->callback(egc, dss, ERROR_FAIL);
-}
-
-static void colo_save_setup_done(libxl__egc *egc,
-                                 libxl__checkpoint_devices_state *cds,
-                                 int rc)
-{
-    libxl__colo_save_state *css = cds->concrete_data;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-    EGC_GC;
-
-    if (!rc) {
-        libxl__domain_save(egc, dss);
-        return;
-    }
-
-    LOGD(ERROR, dss->domid, "COLO: failed to setup device for guest");
-    cds->callback = colo_save_setup_failed;
-    libxl__checkpoint_devices_teardown(egc, cds);
-}
-
-static void colo_save_setup_failed(libxl__egc *egc,
-                                   libxl__checkpoint_devices_state *cds,
-                                   int rc)
-{
-    libxl__colo_save_state *css = cds->concrete_data;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-    STATE_AO_GC(cds->ao);
-
-    if (rc)
-        LOGD(ERROR, cds->domid,
-             "COLO: failed to teardown device after setup failed"
-             " for guest, rc %d", rc);
-
-    cleanup_device_subkind(cds);
-    dss->callback(egc, dss, rc);
-}
-
-/* ================= colo: teardown save environment ================= */
-
-static void colo_teardown_done(libxl__egc *egc,
-                               libxl__checkpoint_devices_state *cds,
-                               int rc);
-
-void libxl__colo_save_teardown(libxl__egc *egc,
-                               libxl__colo_save_state *css,
-                               int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    LOGD(WARN, dss->domid,
-         "COLO: Domain suspend terminated with rc %d,"
-         " teardown COLO devices...", rc);
-
-    libxl__stream_read_abort(egc, &css->srs, 1);
-
-    if (css->qdisk_setuped) {
-        libxl__qmp_stop_replication(gc, dss->domid, true);
-        css->qdisk_setuped = false;
-    }
-
-    dss->cds.callback = colo_teardown_done;
-    libxl__checkpoint_devices_teardown(egc, &dss->cds);
-    return;
-}
-
-static void colo_teardown_done(libxl__egc *egc,
-                               libxl__checkpoint_devices_state *cds,
-                               int rc)
-{
-    libxl__colo_save_state *css = cds->concrete_data;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    cleanup_device_subkind(cds);
-    colo_proxy_teardown(&css->cps);
-    dss->callback(egc, dss, rc);
-}
-
-static void colo_common_write_stream_done(libxl__egc *egc,
-                                          libxl__stream_write_state *stream,
-                                          int rc);
-static void colo_common_read_stream_done(libxl__egc *egc,
-                                         libxl__stream_read_state *stream,
-                                         int rc);
-
-/* ===================== colo: suspend primary vm ===================== */
-
-static void colo_read_svm_suspended_done(libxl__egc *egc,
-                                         libxl__colo_save_state *css,
-                                         int id);
-/*
- * Do the following things when suspending primary vm:
- * 1. suspend primary vm
- * 2. do postsuspend
- * 3. read CHECKPOINT_SVM_SUSPENDED
- * 4. read secondary vm's dirty pages
- */
-static void colo_suspend_primary_vm_done(libxl__egc *egc,
-                                         libxl__domain_suspend_state *dsps,
-                                         int ok);
-static void colo_postsuspend_cb(libxl__egc *egc,
-                                libxl__checkpoint_devices_state *cds,
-                                int rc);
-
-static void libxl__colo_save_domain_suspend_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__egc *egc = shs->egc;
-    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
-    libxl__domain_save_state *dss = sws->dss;
-
-    /* Convenience aliases */
-    libxl__domain_suspend_state *dsps = &dss->dsps;
-
-    dsps->callback_common_done = colo_suspend_primary_vm_done;
-    libxl__domain_suspend(egc, dsps);
-}
-
-static void colo_suspend_primary_vm_done(libxl__egc *egc,
-                                         libxl__domain_suspend_state *dsps,
-                                         int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, dss->domid, "cannot suspend primary vm");
-        goto out;
-    }
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-
-    cds->callback = colo_postsuspend_cb;
-    libxl__checkpoint_devices_postsuspend(egc, cds);
-    return;
-
-out:
-    dss->rc = rc;
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void colo_postsuspend_cb(libxl__egc *egc,
-                                libxl__checkpoint_devices_state *cds,
-                                int rc)
-{
-    libxl__colo_save_state *css = cds->concrete_data;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, dss->domid, "postsuspend fails");
-        goto out;
-    }
-
-    if (!css->svm_running) {
-        rc = 0;
-        goto out;
-    }
-
-    /*
-     * read CHECKPOINT_SVM_SUSPENDED
-     */
-    css->callback = colo_read_svm_suspended_done;
-    css->srs.checkpoint_callback = colo_common_read_stream_done;
-    libxl__stream_read_checkpoint_state(egc, &css->srs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void colo_read_svm_suspended_done(libxl__egc *egc,
-                                         libxl__colo_save_state *css,
-                                         int id)
-{
-    int ok = 0;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    if (id != CHECKPOINT_SVM_SUSPENDED) {
-        LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
-            CHECKPOINT_SVM_SUSPENDED);
-        goto out;
-    }
-
-    if (!css->paused &&
-        libxl__qmp_query_xen_replication_status(gc, dss->domid)) {
-        LOGD(ERROR, dss->domid,
-             "replication error occurs when primary vm is running");
-        goto out;
-    }
-
-    ok = 1;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
-
-/* ===================== colo: send tailbuf ========================== */
-
-static void libxl__colo_save_domain_checkpoint_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
-    libxl__domain_save_state *dss = sws->dss;
-
-    /* Convenience aliases */
-    libxl__colo_save_state *const css = &dss->css;
-
-    /* write emulator xenstore data, emulator context, and checkpoint end */
-    css->callback = NULL;
-    dss->sws.checkpoint_callback = colo_common_write_stream_done;
-    libxl__stream_write_start_checkpoint(shs->egc, &dss->sws);
-}
-
-/* ===================== colo: resume primary vm ===================== */
-
-/*
- * Do the following things when resuming primary vm:
- *  1. read CHECKPOINT_SVM_READY
- *  2. do preresume
- *  3. resume primary vm
- *  4. read CHECKPOINT_SVM_RESUMED
- */
-static void colo_read_svm_ready_done(libxl__egc *egc,
-                                     libxl__colo_save_state *css,
-                                     int id);
-static void colo_preresume_cb(libxl__egc *egc,
-                              libxl__checkpoint_devices_state *cds,
-                              int rc);
-static void colo_read_svm_resumed_done(libxl__egc *egc,
-                                       libxl__colo_save_state *css,
-                                       int id);
-
-static void libxl__colo_save_domain_resume_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__egc *egc = shs->egc;
-    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
-    libxl__domain_save_state *dss = sws->dss;
-
-    /* Convenience aliases */
-    libxl__colo_save_state *const css = &dss->css;
-
-    EGC_GC;
-
-    /* read CHECKPOINT_SVM_READY */
-    css->callback = colo_read_svm_ready_done;
-    css->srs.checkpoint_callback = colo_common_read_stream_done;
-    libxl__stream_read_checkpoint_state(egc, &css->srs);
-}
-
-static void colo_read_svm_ready_done(libxl__egc *egc,
-                                     libxl__colo_save_state *css,
-                                     int id)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    if (id != CHECKPOINT_SVM_READY) {
-        LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
-            CHECKPOINT_SVM_READY);
-        goto out;
-    }
-
-    colo_proxy_preresume(&css->cps);
-
-    css->svm_running = true;
-    dss->cds.callback = colo_preresume_cb;
-    libxl__checkpoint_devices_preresume(egc, &dss->cds);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void colo_preresume_cb(libxl__egc *egc,
-                              libxl__checkpoint_devices_state *cds,
-                              int rc)
-{
-    libxl__colo_save_state *css = cds->concrete_data;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, dss->domid, "preresume fails");
-        goto out;
-    }
-
-    if (css->qdisk_used && !css->qdisk_setuped) {
-        if (libxl__qmp_start_replication(gc, dss->domid, true)) {
-            LOGD(ERROR, dss->domid, "starting replication fails");
-            goto out;
-        }
-        css->qdisk_setuped = true;
-    }
-
-    if (!css->paused) {
-        if (libxl__qmp_colo_do_checkpoint(gc, dss->domid)) {
-            LOGD(ERROR, dss->domid, "doing checkpoint fails");
-            goto out;
-        }
-    }
-
-    /* Resumes the domain and the device model */
-    if (libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1)) {
-        LOGD(ERROR, dss->domid, "cannot resume primary vm");
-        goto out;
-    }
-
-    /*
-     * The guest should be paused before doing colo because there is
-     * no disk migration.
-     */
-    if (css->paused) {
-        rc = libxl__domain_unpause_deprecated(gc, dss->domid);
-        if (rc) {
-            LOGD(ERROR, dss->domid, "cannot unpause primary vm");
-            goto out;
-        }
-        css->paused = false;
-    }
-
-    /* read CHECKPOINT_SVM_RESUMED */
-    css->callback = colo_read_svm_resumed_done;
-    css->srs.checkpoint_callback = colo_common_read_stream_done;
-    libxl__stream_read_checkpoint_state(egc, &css->srs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void colo_read_svm_resumed_done(libxl__egc *egc,
-                                       libxl__colo_save_state *css,
-                                       int id)
-{
-    int ok = 0;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    if (id != CHECKPOINT_SVM_RESUMED) {
-        LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id,
-            CHECKPOINT_SVM_RESUMED);
-        goto out;
-    }
-
-    colo_proxy_postresume(&css->cps);
-
-    ok = 1;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
-
-/* ===================== colo: wait new checkpoint ===================== */
-
-static void colo_start_new_checkpoint(libxl__egc *egc,
-                                      libxl__checkpoint_devices_state *cds,
-                                      int rc);
-static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css);
-static void colo_proxy_async_call_done(libxl__egc *egc,
-                                       libxl__ev_child *child,
-                                       int pid,
-                                       int status);
-
-static void colo_proxy_wait_for_checkpoint(libxl__egc *egc,
-                                           libxl__colo_save_state *css)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    ASYNC_CALL(egc, dss->cds.ao, &css->child, css,
-               colo_proxy_async_wait_for_checkpoint,
-               colo_proxy_async_call_done);
-}
-
-static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css)
-{
-    int req;
-
-    req = colo_proxy_checkpoint(&css->cps, COLO_PROXY_CHECKPOINT_TIMEOUT);
-    if (req < 0) {
-        /* some error happens */
-        _exit(1);
-    } else {
-        /* req == 0: no checkpoint is needed, do a checkpoint every 5s */
-        /* req > 0: net packets is not consistent, we need to start a
-         * checkpoint
-         */
-        _exit(0);
-    }
-}
-
-static void colo_proxy_async_call_done(libxl__egc *egc,
-                                       libxl__ev_child *child,
-                                       int pid,
-                                       int status)
-{
-    libxl__colo_save_state *css = CONTAINER_OF(child, *css, child);
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    if (status) {
-        LOGD(ERROR, dss->domid, "failed to wait for new checkpoint");
-        colo_start_new_checkpoint(egc, &dss->cds, ERROR_FAIL);
-        return;
-    }
-
-    colo_start_new_checkpoint(egc, &dss->cds, 0);
-}
-
-/*
- * Do the following things:
- * 1. do commit
- * 2. wait for a new checkpoint
- * 3. write CHECKPOINT_NEW
- */
-static void colo_device_commit_cb(libxl__egc *egc,
-                                  libxl__checkpoint_devices_state *cds,
-                                  int rc);
-
-static void libxl__colo_save_domain_wait_checkpoint_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs);
-    libxl__domain_save_state *dss = sws->dss;
-    libxl__egc *egc = dss->sws.shs.egc;
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-
-    cds->callback = colo_device_commit_cb;
-    libxl__checkpoint_devices_commit(egc, cds);
-}
-
-static void colo_device_commit_cb(libxl__egc *egc,
-                                  libxl__checkpoint_devices_state *cds,
-                                  int rc)
-{
-    libxl__colo_save_state *css = cds->concrete_data;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, dss->domid, "commit fails");
-        goto out;
-    }
-
-    colo_proxy_wait_for_checkpoint(egc, css);
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void colo_start_new_checkpoint(libxl__egc *egc,
-                                      libxl__checkpoint_devices_state *cds,
-                                      int rc)
-{
-    libxl__colo_save_state *css = cds->concrete_data;
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-    libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_NEW };
-
-    if (rc)
-        goto out;
-
-    /* write CHECKPOINT_NEW */
-    css->callback = NULL;
-    dss->sws.checkpoint_callback = colo_common_write_stream_done;
-    libxl__stream_write_checkpoint_state(egc, &dss->sws, &srcs);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-/* ===================== colo: common callback ===================== */
-
-static void colo_common_write_stream_done(libxl__egc *egc,
-                                          libxl__stream_write_state *stream,
-                                          int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(stream, *dss, sws);
-    int ok;
-
-    /* Convenience aliases */
-    libxl__colo_save_state *const css = &dss->css;
-
-    EGC_GC;
-
-    if (rc < 0) {
-        /* TODO: it may be a internal error, but we don't know */
-        LOGD(ERROR, dss->domid, "sending data fails");
-        ok = 0;
-        goto out;
-    }
-
-    if (!css->callback) {
-        /* Everythins is OK */
-        ok = 1;
-        goto out;
-    }
-
-    css->callback(egc, css, 0);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
-
-static void colo_common_read_stream_done(libxl__egc *egc,
-                                         libxl__stream_read_state *stream,
-                                         int rc)
-{
-    libxl__colo_save_state *css = CONTAINER_OF(stream, *css, srs);
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-    int ok;
-
-    EGC_GC;
-
-    if (rc < 0) {
-        /* TODO: it may be a internal error, but we don't know */
-        LOGD(ERROR, dss->domid, "reading data fails");
-        ok = 0;
-        goto out;
-    }
-
-    if (!css->callback) {
-        /* Everythins is OK */
-        ok = 1;
-        goto out;
-    }
-
-    /* rc contains the id */
-    css->callback(egc, css, rc);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok);
-}
diff --git a/tools/libxl/libxl_console.c b/tools/libxl/libxl_console.c
deleted file mode 100644 (file)
index 047d23d..0000000
+++ /dev/null
@@ -1,802 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
-                            libxl_console_type type, char **tty_path)
-{
-    int rc;
-    char *dom_path;
-
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    switch (type) {
-    case LIBXL_CONSOLE_TYPE_SERIAL:
-        *tty_path = GCSPRINTF("%s/serial/%d/tty", dom_path, cons_num);
-        rc = 0;
-        break;
-    case LIBXL_CONSOLE_TYPE_PV:
-        if (cons_num == 0)
-            *tty_path = GCSPRINTF("%s/console/tty", dom_path);
-        else
-            *tty_path = GCSPRINTF("%s/tty",
-                                  libxl__domain_device_frontend_path(gc, domid,
-                                  cons_num, LIBXL__DEVICE_KIND_CONSOLE));
-        rc = 0;
-        break;
-    default:
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-out:
-    return rc;
-}
-
-int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
-                       libxl_console_type type, int notify_fd)
-{
-    GC_INIT(ctx);
-    char *p = GCSPRINTF("%s/xenconsole", libxl__private_bindir_path());
-    char *domid_s = GCSPRINTF("%d", domid);
-    char *cons_num_s = GCSPRINTF("%d", cons_num);
-    char *notify_fd_s;
-    char *cons_type_s;
-
-    switch (type) {
-    case LIBXL_CONSOLE_TYPE_PV:
-        cons_type_s = "pv";
-        break;
-    case LIBXL_CONSOLE_TYPE_SERIAL:
-        cons_type_s = "serial";
-        break;
-    case LIBXL_CONSOLE_TYPE_VUART:
-        cons_type_s = "vuart";
-        break;
-    default:
-        goto out;
-    }
-
-    if (notify_fd != -1) {
-        notify_fd_s = GCSPRINTF("%d", notify_fd);
-        execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
-              "--start-notify-fd", notify_fd_s, (void *)NULL);
-    } else {
-        execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s,
-              (void *)NULL);
-    }
-
-out:
-    GC_FREE;
-    return ERROR_FAIL;
-}
-
-int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
-                          libxl_console_type type, char **path)
-{
-    GC_INIT(ctx);
-    char *tty_path;
-    char *tty;
-    int rc;
-
-    rc = libxl__console_tty_path(gc, domid, cons_num, type, &tty_path);
-    if (rc) {
-        LOGD(ERROR, domid, "Failed to get tty path\n");
-        goto out;
-    }
-
-    tty = libxl__xs_read(gc, XBT_NULL, tty_path);
-    if (!tty || tty[0] == '\0') {
-       LOGED(ERROR, domid, "Unable to read console tty path `%s'",
-             tty_path);
-       rc = ERROR_FAIL;
-       goto out;
-    }
-
-    *path = libxl__strdup(NOGC, tty);
-    rc = 0;
-out:
-    GC_FREE;
-    return rc;
-}
-
-static int libxl__primary_console_find(libxl_ctx *ctx, uint32_t domid_vm,
-                                       uint32_t *domid, int *cons_num,
-                                       libxl_console_type *type)
-{
-    GC_INIT(ctx);
-    uint32_t stubdomid = libxl_get_stubdom_id(ctx, domid_vm);
-    int rc;
-
-    if (stubdomid) {
-        *domid = stubdomid;
-        *cons_num = STUBDOM_CONSOLE_SERIAL;
-        *type = LIBXL_CONSOLE_TYPE_PV;
-    } else {
-        switch (libxl__domain_type(gc, domid_vm)) {
-        case LIBXL_DOMAIN_TYPE_HVM:
-            *domid = domid_vm;
-            *cons_num = 0;
-            *type = LIBXL_CONSOLE_TYPE_SERIAL;
-            break;
-        case LIBXL_DOMAIN_TYPE_PVH:
-        case LIBXL_DOMAIN_TYPE_PV:
-            *domid = domid_vm;
-            *cons_num = 0;
-            *type = LIBXL_CONSOLE_TYPE_PV;
-            break;
-        case LIBXL_DOMAIN_TYPE_INVALID:
-            rc = ERROR_INVAL;
-            goto out;
-        default: abort();
-        }
-    }
-
-    rc = 0;
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm, int notify_fd)
-{
-    uint32_t domid;
-    int cons_num;
-    libxl_console_type type;
-    int rc;
-
-    rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
-    if ( rc ) return rc;
-    return libxl_console_exec(ctx, domid, cons_num, type, notify_fd);
-}
-
-int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm,
-                                  char **path)
-{
-    uint32_t domid;
-    int cons_num;
-    libxl_console_type type;
-    int rc;
-
-    rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
-    if ( rc ) return rc;
-    return libxl_console_get_tty(ctx, domid, cons_num, type, path);
-}
-
-int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass)
-{
-    GC_INIT(ctx);
-    const char *vnc_port;
-    const char *vnc_listen = NULL, *vnc_pass = NULL;
-    int port = 0, autopass_fd = -1;
-    char *vnc_bin, *args[] = {
-        "vncviewer",
-        NULL, /* hostname:display */
-        NULL, /* -autopass */
-        NULL,
-    };
-
-    vnc_port = libxl__xs_read(gc, XBT_NULL,
-                            GCSPRINTF(
-                            "/local/domain/%d/console/vnc-port", domid));
-    if (!vnc_port) {
-        LOGD(ERROR, domid, "Cannot get vnc-port");
-        goto x_fail;
-    }
-
-    port = atoi(vnc_port) - 5900;
-
-    vnc_listen = libxl__xs_read(gc, XBT_NULL,
-                                GCSPRINTF("/local/domain/%d/console/vnc-listen",
-                                          domid));
-
-    if ( autopass )
-        vnc_pass = libxl__xs_read(gc, XBT_NULL,
-                                  GCSPRINTF("/local/domain/%d/console/vnc-pass",
-                                            domid));
-
-    if ( NULL == vnc_listen )
-        vnc_listen = "localhost";
-
-    if ( (vnc_bin = getenv("VNCVIEWER")) )
-        args[0] = vnc_bin;
-
-    args[1] = GCSPRINTF("%s:%d", vnc_listen, port);
-
-    if ( vnc_pass ) {
-        char tmpname[] = "/tmp/vncautopass.XXXXXX";
-        autopass_fd = mkstemp(tmpname);
-        if ( autopass_fd < 0 ) {
-            LOGED(ERROR, domid, "mkstemp %s failed", tmpname);
-            goto x_fail;
-        }
-
-        if ( unlink(tmpname) ) {
-            /* should never happen */
-            LOGED(ERROR, domid, "unlink %s failed", tmpname);
-            goto x_fail;
-        }
-
-        if ( libxl_write_exactly(ctx, autopass_fd, vnc_pass, strlen(vnc_pass),
-                                    tmpname, "vnc password") )
-            goto x_fail;
-
-        if ( lseek(autopass_fd, SEEK_SET, 0) ) {
-            LOGED(ERROR, domid, "rewind %s (autopass) failed", tmpname);
-            goto x_fail;
-        }
-
-        args[2] = "-autopass";
-    }
-
-    libxl__exec(gc, autopass_fd, -1, -1, args[0], args, NULL);
-
- x_fail:
-    GC_FREE;
-    return ERROR_FAIL;
-}
-
-int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
-                              libxl__device_console *console,
-                              libxl__domain_build_state *state,
-                              libxl__device *device)
-{
-    flexarray_t *front, *ro_front;
-    flexarray_t *back;
-    int rc;
-
-    if (console->devid && state) {
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    if (!console->devid && (console->name || console->path)) {
-        LOGD(ERROR, domid, "Primary console has invalid configuration");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    front = flexarray_make(gc, 16, 1);
-    ro_front = flexarray_make(gc, 16, 1);
-    back = flexarray_make(gc, 16, 1);
-
-    device->backend_devid = console->devid;
-    device->backend_domid = console->backend_domid;
-    device->backend_kind = LIBXL__DEVICE_KIND_CONSOLE;
-    device->devid = console->devid;
-    device->domid = domid;
-    device->kind = LIBXL__DEVICE_KIND_CONSOLE;
-
-    flexarray_append(back, "frontend-id");
-    flexarray_append(back, GCSPRINTF("%d", domid));
-    flexarray_append(back, "online");
-    flexarray_append(back, "1");
-    flexarray_append(back, "state");
-    flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
-    flexarray_append(back, "protocol");
-    flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
-
-    if (console->name) {
-        flexarray_append(ro_front, "name");
-        flexarray_append(ro_front, console->name);
-        flexarray_append(back, "name");
-        flexarray_append(back, console->name);
-    }
-    if (console->connection) {
-        flexarray_append(back, "connection");
-        flexarray_append(back, console->connection);
-    }
-    if (console->path) {
-        flexarray_append(back, "path");
-        flexarray_append(back, console->path);
-    }
-
-    flexarray_append(front, "backend-id");
-    flexarray_append(front, GCSPRINTF("%d", console->backend_domid));
-
-    flexarray_append(ro_front, "limit");
-    flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
-    flexarray_append(ro_front, "type");
-    if (console->consback == LIBXL__CONSOLE_BACKEND_XENCONSOLED)
-        flexarray_append(ro_front, "xenconsoled");
-    else
-        flexarray_append(ro_front, "ioemu");
-    flexarray_append(ro_front, "output");
-    flexarray_append(ro_front, console->output);
-    flexarray_append(ro_front, "tty");
-    if (state && state->console_tty)
-        flexarray_append(ro_front, state->console_tty);
-    else
-        flexarray_append(ro_front, "");
-
-    if (state) {
-        flexarray_append(ro_front, "port");
-        flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->console_port));
-        flexarray_append(ro_front, "ring-ref");
-        flexarray_append(ro_front, GCSPRINTF("%lu", state->console_mfn));
-    } else {
-        flexarray_append(front, "state");
-        flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
-        flexarray_append(front, "protocol");
-        flexarray_append(front, LIBXL_XENCONSOLE_PROTOCOL);
-    }
-    libxl__device_generic_add(gc, XBT_NULL, device,
-                              libxl__xs_kvs_of_flexarray(gc, back),
-                              libxl__xs_kvs_of_flexarray(gc, front),
-                              libxl__xs_kvs_of_flexarray(gc, ro_front));
-    rc = 0;
-out:
-    return rc;
-}
-
-int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
-                            libxl__device_console *console,
-                            libxl__domain_build_state *state)
-{
-    libxl__device device;
-    flexarray_t *ro_front;
-    flexarray_t *back;
-    int rc;
-
-    ro_front = flexarray_make(gc, 16, 1);
-    back = flexarray_make(gc, 16, 1);
-
-    device.backend_devid = console->devid;
-    device.backend_domid = console->backend_domid;
-    device.backend_kind = LIBXL__DEVICE_KIND_VUART;
-    device.devid = console->devid;
-    device.domid = domid;
-    device.kind = LIBXL__DEVICE_KIND_VUART;
-
-    flexarray_append(back, "frontend-id");
-    flexarray_append(back, GCSPRINTF("%d", domid));
-    flexarray_append(back, "online");
-    flexarray_append(back, "1");
-    flexarray_append(back, "state");
-    flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
-    flexarray_append(back, "protocol");
-    flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
-
-    flexarray_append(ro_front, "port");
-    flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->vuart_port));
-    flexarray_append(ro_front, "ring-ref");
-    flexarray_append(ro_front, GCSPRINTF("%"PRIu_xen_pfn, state->vuart_gfn));
-    flexarray_append(ro_front, "limit");
-    flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
-    flexarray_append(ro_front, "type");
-    flexarray_append(ro_front, "xenconsoled");
-
-    rc = libxl__device_generic_add(gc, XBT_NULL, &device,
-                                   libxl__xs_kvs_of_flexarray(gc, back),
-                                   NULL,
-                                   libxl__xs_kvs_of_flexarray(gc, ro_front));
-    return rc;
-}
-
-int libxl__init_console_from_channel(libxl__gc *gc,
-                                     libxl__device_console *console,
-                                     int dev_num,
-                                     libxl_device_channel *channel)
-{
-    int rc;
-
-    libxl__device_console_init(console);
-
-    /* Perform validation first, allocate second. */
-
-    if (channel->devid == -1)
-        channel->devid = dev_num;
-
-    if (!channel->name) {
-        LOG(ERROR, "channel %d has no name", channel->devid);
-        return ERROR_INVAL;
-    }
-
-    if (channel->backend_domname) {
-        rc = libxl_domain_qualifier_to_domid(CTX, channel->backend_domname,
-                                             &channel->backend_domid);
-        if (rc < 0) return rc;
-    }
-
-    /* The xenstore 'output' node tells the backend what to connect the console
-       to. If the channel has "connection = pty" then the "output" node will be
-       set to "pty". If the channel has "connection = socket" then the "output"
-       node will be set to "chardev:libxl-channel%d". This tells the qemu
-       backend to proxy data between the console ring and the character device
-       with id "libxl-channel%d". These character devices are currently defined
-       on the qemu command-line via "-chardev" options in libxl_dm.c */
-
-    switch (channel->connection) {
-        case LIBXL_CHANNEL_CONNECTION_UNKNOWN:
-            LOG(ERROR, "channel %d has no defined connection; "
-                "to where should it be connected?", channel->devid);
-            return ERROR_INVAL;
-        case LIBXL_CHANNEL_CONNECTION_PTY:
-            console->connection = libxl__strdup(NOGC, "pty");
-            console->output = libxl__sprintf(NOGC, "pty");
-            break;
-        case LIBXL_CHANNEL_CONNECTION_SOCKET:
-            if (!channel->u.socket.path) {
-                LOG(ERROR, "channel %d has no path", channel->devid);
-                return ERROR_INVAL;
-            }
-            console->connection = libxl__strdup(NOGC, "socket");
-            console->path = libxl__strdup(NOGC, channel->u.socket.path);
-            console->output = libxl__sprintf(NOGC, "chardev:libxl-channel%d",
-                                             channel->devid);
-            break;
-        default:
-            /* We've forgotten to add the clause */
-            LOG(ERROR, "%s: missing implementation for channel connection %d",
-                __func__, channel->connection);
-            abort();
-    }
-
-    console->devid = channel->devid;
-    console->consback = LIBXL__CONSOLE_BACKEND_IOEMU;
-    console->backend_domid = channel->backend_domid;
-    console->name = libxl__strdup(NOGC, channel->name);
-
-    return 0;
-}
-
-static int libxl__device_channel_from_xenstore(libxl__gc *gc,
-                                            const char *libxl_path,
-                                            libxl_device_channel *channel)
-{
-    const char *tmp;
-    int rc;
-
-    libxl_device_channel_init(channel);
-
-    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
-                                GCSPRINTF("%s/name", libxl_path),
-                                (const char **)(&channel->name));
-    if (rc) goto out;
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/connection", libxl_path), &tmp);
-    if (rc) goto out;
-    if (!strcmp(tmp, "pty")) {
-        channel->connection = LIBXL_CHANNEL_CONNECTION_PTY;
-    } else if (!strcmp(tmp, "socket")) {
-        channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
-        rc = libxl__xs_read_checked(NOGC, XBT_NULL,
-                                    GCSPRINTF("%s/path", libxl_path),
-                                    (const char **)(&channel->u.socket.path));
-        if (rc) goto out;
-    } else {
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    rc = 0;
- out:
-    return rc;
-}
-
-static int libxl__append_channel_list(libxl__gc *gc,
-                                              uint32_t domid,
-                                              libxl_device_channel **channels,
-                                              int *nchannels)
-{
-    char *libxl_dir_path = NULL;
-    char **dir = NULL;
-    unsigned int n = 0, devid = 0;
-    libxl_device_channel *next = NULL;
-    int rc = 0, i;
-
-    libxl_dir_path = GCSPRINTF("%s/device/%s",
-                               libxl__xs_libxl_path(gc, domid),
-                               libxl__device_kind_to_string(
-                               LIBXL__DEVICE_KIND_CONSOLE));
-    dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n);
-    if (!dir || !n)
-      goto out;
-
-    for (i = 0; i < n; i++) {
-        const char *libxl_path, *name;
-        libxl_device_channel *tmp;
-
-        libxl_path = GCSPRINTF("%s/%s", libxl_dir_path, dir[i]);
-        name = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/name", libxl_path));
-        /* 'channels' are consoles with names, so ignore all consoles
-           without names */
-        if (!name) continue;
-        tmp = realloc(*channels,
-                      sizeof(libxl_device_channel) * (*nchannels + devid + 1));
-        if (!tmp) {
-          rc = ERROR_NOMEM;
-          goto out;
-        }
-        *channels = tmp;
-        next = *channels + *nchannels + devid;
-        rc = libxl__device_channel_from_xenstore(gc, libxl_path, next);
-        if (rc) goto out;
-        next->devid = devid;
-        devid++;
-    }
-    *nchannels += devid;
-    return 0;
-
- out:
-    return rc;
-}
-
-libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
-                                                uint32_t domid,
-                                                int *num)
-{
-    GC_INIT(ctx);
-    libxl_device_channel *channels = NULL;
-    int rc;
-
-    *num = 0;
-
-    rc = libxl__append_channel_list(gc, domid, &channels, num);
-    if (rc) goto out_err;
-
-    GC_FREE;
-    return channels;
-
-out_err:
-    LOGD(ERROR, domid, "Unable to list channels");
-    while (*num) {
-        (*num)--;
-        libxl_device_channel_dispose(&channels[*num]);
-    }
-    free(channels);
-    return NULL;
-}
-
-int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
-                                 const libxl_device_channel *channel,
-                                 libxl_channelinfo *channelinfo)
-{
-    GC_INIT(ctx);
-    char *fe_path, *libxl_path;
-    char *val;
-    int rc;
-
-    channelinfo->devid = channel->devid;
-
-    fe_path = libxl__domain_device_frontend_path(gc, domid,
-                                                 channelinfo->devid + 1,
-                                                 LIBXL__DEVICE_KIND_CONSOLE);
-    libxl_path = libxl__domain_device_libxl_path(gc, domid,
-                                                 channelinfo->devid + 1,
-                                                 LIBXL__DEVICE_KIND_CONSOLE);
-
-    channelinfo->backend = xs_read(ctx->xsh, XBT_NULL,
-                                   GCSPRINTF("%s/backend", libxl_path), NULL);
-    if (!channelinfo->backend) {
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    rc = libxl__backendpath_parse_domid(gc, channelinfo->backend,
-                                        &channelinfo->backend_id);
-    if (rc) goto out;
-
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
-    channelinfo->state = val ? strtoul(val, NULL, 10) : -1;
-    channelinfo->frontend = libxl__strdup(NOGC, fe_path);
-    channelinfo->frontend_id = domid;
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
-    channelinfo->rref = val ? strtoul(val, NULL, 10) : -1;
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/port", fe_path));
-    channelinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
-
-    channelinfo->connection = channel->connection;
-    switch (channel->connection) {
-         case LIBXL_CHANNEL_CONNECTION_PTY:
-             val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tty", fe_path));
-             /*
-              * It is obviously very wrong for this value to be in the
-              * frontend.  But in XSA-175 we don't want to re-engineer
-              * this because other xenconsole code elsewhere (some
-              * even out of tree, perhaps) expects this node to be
-              * here.
-              *
-              * FE/pty is readonly for the guest.  It always exists if
-              * FE does because libxl__device_console_add
-              * unconditionally creates it and nothing deletes it.
-              *
-              * The guest can delete the whole FE (which it has write
-              * privilege on) but the containing directories
-              * /local/GUEST[/device[/console]] are also RO for the
-              * guest.  So if the guest deletes FE it cannot recreate
-              * it.
-              *
-              * Therefore the guest cannot cause FE/pty to contain bad
-              * data, although it can cause it to not exist.
-              */
-             if (!val) val = "/NO-SUCH-PATH";
-             channelinfo->u.pty.path = strdup(val);
-             break;
-         default:
-             break;
-    }
-    rc = 0;
- out:
-    GC_FREE;
-    return rc;
-}
-
-static int libxl__device_vfb_setdefault(libxl__gc *gc, uint32_t domid,
-                                        libxl_device_vfb *vfb, bool hotplug)
-{
-    int rc;
-
-    libxl_defbool_setdefault(&vfb->vnc.enable, true);
-    if (libxl_defbool_val(vfb->vnc.enable)) {
-        if (!vfb->vnc.listen) {
-            vfb->vnc.listen = strdup("127.0.0.1");
-            if (!vfb->vnc.listen) return ERROR_NOMEM;
-        }
-
-        libxl_defbool_setdefault(&vfb->vnc.findunused, true);
-    } else {
-        libxl_defbool_setdefault(&vfb->vnc.findunused, false);
-    }
-
-    libxl_defbool_setdefault(&vfb->sdl.enable, false);
-    libxl_defbool_setdefault(&vfb->sdl.opengl, false);
-
-    rc = libxl__resolve_domid(gc, vfb->backend_domname, &vfb->backend_domid);
-    return rc;
-}
-
-int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
-                         const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int rc;
-
-    rc = libxl__device_add(gc, domid, &libxl__vfb_devtype, vfb);
-    if (rc) {
-        LOGD(ERROR, domid, "Unable to add vfb device");
-        goto out;
-    }
-
-out:
-    libxl__ao_complete(egc, ao, rc);
-    return AO_INPROGRESS;
-}
-
-static int libxl__set_xenstore_vfb(libxl__gc *gc, uint32_t domid,
-                                   libxl_device_vfb *vfb,
-                                  flexarray_t *back, flexarray_t *front,
-                                  flexarray_t *ro_front)
-{
-    flexarray_append_pair(back, "vnc",
-                          libxl_defbool_val(vfb->vnc.enable) ? "1" : "0");
-    flexarray_append_pair(back, "vnclisten", vfb->vnc.listen);
-    flexarray_append_pair(back, "vncpasswd", vfb->vnc.passwd);
-    flexarray_append_pair(back, "vncdisplay",
-                          GCSPRINTF("%d", vfb->vnc.display));
-    flexarray_append_pair(back, "vncunused",
-                          libxl_defbool_val(vfb->vnc.findunused) ? "1" : "0");
-    flexarray_append_pair(back, "sdl",
-                          libxl_defbool_val(vfb->sdl.enable) ? "1" : "0");
-    flexarray_append_pair(back, "opengl",
-                          libxl_defbool_val(vfb->sdl.opengl) ? "1" : "0");
-    if (vfb->sdl.xauthority) {
-        flexarray_append_pair(back, "xauthority", vfb->sdl.xauthority);
-    }
-    if (vfb->sdl.display) {
-        flexarray_append_pair(back, "display", vfb->sdl.display);
-    }
-
-    return 0;
-}
-
-/* The following functions are defined:
- * libxl_device_vfb_remove
- * libxl_device_vfb_destroy
- */
-
-/* channel/console hotunplug is not implemented. There are 2 possibilities:
- * 1. add support for secondary consoles to xenconsoled
- * 2. dynamically add/remove qemu chardevs via qmp messages. */
-
-#define libxl__add_vfbs NULL
-#define libxl_device_vfb_list NULL
-#define libxl_device_vfb_compare NULL
-
-static LIBXL_DEFINE_UPDATE_DEVID(vfb)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(vfb)
-
-/* vfb */
-LIBXL_DEFINE_DEVICE_REMOVE(vfb)
-
-DEFINE_DEVICE_TYPE_STRUCT(vfb, VFB,
-    .skip_attach = 1,
-    .set_xenstore_config = (device_set_xenstore_config_fn_t)
-                           libxl__set_xenstore_vfb,
-);
-
-libxl_xen_console_reader *
-    libxl_xen_console_read_start(libxl_ctx *ctx, int clear)
-{
-    GC_INIT(ctx);
-    libxl_xen_console_reader *cr;
-    unsigned int size = 16384;
-
-    cr = libxl__zalloc(NOGC, sizeof(libxl_xen_console_reader));
-    cr->buffer = libxl__zalloc(NOGC, size);
-    cr->size = size;
-    cr->count = size;
-    cr->clear = clear;
-    cr->incremental = 1;
-
-    GC_FREE;
-    return cr;
-}
-
-/* return values:                                          *line_r
- *   1          success, whole line obtained from buffer    non-0
- *   0          no more lines available right now           0
- *   negative   error code ERROR_*                          0
- * On success *line_r is updated to point to a nul-terminated
- * string which is valid until the next call on the same console
- * reader.  The libxl caller may overwrite parts of the string
- * if it wishes. */
-int libxl_xen_console_read_line(libxl_ctx *ctx,
-                                libxl_xen_console_reader *cr,
-                                char **line_r)
-{
-    int ret;
-    GC_INIT(ctx);
-
-    memset(cr->buffer, 0, cr->size);
-    ret = xc_readconsolering(ctx->xch, cr->buffer, &cr->count,
-                             cr->clear, cr->incremental, &cr->index);
-    if (ret < 0) {
-        LOGE(ERROR, "reading console ring buffer");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    if (!ret) {
-        if (cr->count) {
-            *line_r = cr->buffer;
-            ret = 1;
-        } else {
-            *line_r = NULL;
-            ret = 0;
-        }
-    }
-
-    GC_FREE;
-    return ret;
-}
-
-void libxl_xen_console_read_finish(libxl_ctx *ctx,
-                                   libxl_xen_console_reader *cr)
-{
-    free(cr->buffer);
-    free(cr);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_convert_callout.c b/tools/libxl/libxl_convert_callout.c
deleted file mode 100644 (file)
index 5e5678b..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2014      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/*
- * Infrastructure for converting a legacy migration stream into a
- * libxl v2 stream.
- *
- * This is done by fork()ing the python conversion script, which takes
- * in a legacy stream, and puts out a suitably-formatted v2 stream.
- */
-
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
-                          pid_t pid, int status);
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc);
-static void helper_done(libxl__egc *egc,
-                        libxl__conversion_helper_state *chs);
-
-/*----- Entrypoints -----*/
-
-void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
-{
-    assert(chs->ao);
-
-    chs->v2_carefd = NULL;
-    chs->rc = 0;
-    libxl__ao_abortable_init(&chs->abrt);
-    libxl__ev_child_init(&chs->child);
-}
-
-int libxl__convert_legacy_stream(libxl__egc *egc,
-                                 libxl__conversion_helper_state *chs)
-{
-    STATE_AO_GC(chs->ao);
-    libxl__carefd *child_in = NULL, *child_out = NULL;
-    int rc = 0;
-
-    chs->abrt.ao = chs->ao;
-    chs->abrt.callback = helper_stop;
-    rc = libxl__ao_abortable_register(&chs->abrt);
-    if (rc) goto err;
-
-    libxl__carefd_begin();
-    int fds[2];
-    if (libxl_pipe(CTX, fds)) {
-        rc = ERROR_FAIL;
-        libxl__carefd_unlock();
-        goto err;
-    }
-    child_out = libxl__carefd_record(CTX, fds[0]);
-    child_in  = libxl__carefd_record(CTX, fds[1]);
-    libxl__carefd_unlock();
-
-    pid_t pid = libxl__ev_child_fork(gc, &chs->child, helper_exited);
-    if (!pid) {
-        char * const args[] =
-        {
-            getenv("LIBXL_CONVERT_HELPER") ?:
-                LIBEXEC_BIN "/convert-legacy-stream",
-            "--in",     GCSPRINTF("%d", chs->legacy_fd),
-            "--out",    GCSPRINTF("%d", fds[1]),
-            /*
-             * The width calculation is an assumption for the common
-             * case.  The conversion script needs to know the width of
-             * the toolstack which saved the legacy stream.
-             *
-             * In the overwhelming majority of cases, the width of the
-             * saving toolstack will be the same as our current
-             * width.  To avoid extending the libxl API with a
-             * parameter intended to disappear shortly, this option
-             * has not been exposed to the caller.
-             *
-             * If more complicated conversion is required, the
-             * conversion script can be instantiated manually, which
-             * will bypass all of this conversion logic.
-             */
-            "--width",  sizeof(unsigned long) == 8 ? "64" : "32",
-
-            "--guest",  chs->hvm ? "hvm" : "pv",
-            "--format", "libxl",
-            /* "--verbose", */
-            NULL,
-        };
-
-        libxl_fd_set_cloexec(CTX, chs->legacy_fd, 0);
-        libxl_fd_set_cloexec(CTX, libxl__carefd_fd(child_in), 0);
-
-        libxl__exec(gc,
-                    -1, -1, -1,
-                    args[0], args, NULL);
-    }
-
-    libxl__carefd_close(child_in);
-    chs->v2_carefd = child_out;
-
-    assert(!rc);
-    return rc;
-
- err:
-    libxl__ao_abortable_deregister(&chs->abrt);
-    assert(rc);
-    return rc;
-}
-
-void libxl__conversion_helper_abort(libxl__egc *egc,
-                                    libxl__conversion_helper_state *chs,
-                                    int rc)
-{
-    STATE_AO_GC(chs->ao);
-    assert(rc);
-
-    if (libxl__conversion_helper_inuse(chs)) {
-
-        if (!chs->rc)
-            chs->rc = rc;
-
-        libxl__kill(gc, chs->child.pid, SIGTERM, "conversion helper");
-    }
-}
-
-/*----- State handling -----*/
-
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
-{
-    libxl__conversion_helper_state *chs = CONTAINER_OF(abrt, *chs, abrt);
-    STATE_AO_GC(chs->ao);
-
-    libxl__conversion_helper_abort(egc, chs, rc);
-}
-
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
-                          pid_t pid, int status)
-{
-    libxl__conversion_helper_state *chs = CONTAINER_OF(ch, *chs, child);
-    STATE_AO_GC(chs->ao);
-
-    if (status) {
-        libxl_report_child_exitstatus(
-            CTX, chs->rc ? XTL_DEBUG : XTL_ERROR,
-            "conversion helper", pid, status);
-
-        if (!chs->rc)
-            chs->rc = ERROR_FAIL;
-    }
-
-    helper_done(egc, chs);
-}
-
-static void helper_done(libxl__egc *egc,
-                        libxl__conversion_helper_state *chs)
-{
-    STATE_AO_GC(chs->ao);
-
-    assert(!libxl__conversion_helper_inuse(chs));
-
-    libxl__ao_abortable_deregister(&chs->abrt);
-
-    chs->completion_callback(egc, chs, chs->rc);
-}
diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c
deleted file mode 100644 (file)
index 08e85dc..0000000
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
-{
-    return !libxl_cpuid_policy_list_length(pl);
-}
-
-void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
-{
-    int i, j;
-    libxl_cpuid_policy_list cpuid_list = *p_cpuid_list;
-
-    if (cpuid_list == NULL)
-        return;
-    for (i = 0; cpuid_list[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
-        for (j = 0; j < 4; j++)
-            if (cpuid_list[i].policy[j] != NULL) {
-                free(cpuid_list[i].policy[j]);
-                cpuid_list[i].policy[j] = NULL;
-            }
-    }
-    free(cpuid_list);
-    *p_cpuid_list = NULL;
-    return;
-}
-
-#define CPUID_REG_INV 0
-#define CPUID_REG_EAX 1
-#define CPUID_REG_EBX 2
-#define CPUID_REG_ECX 3
-#define CPUID_REG_EDX 4
-
-/* mapping CPUID features to names
- * holds a "name" for each feature, specified by the "leaf" number (and an
- * optional "subleaf" in ECX), the "reg"ister (EAX-EDX) used and a number of
- * bits starting with "bit" and being "length" bits long.
- * Used for the static structure describing all features.
- */
-struct cpuid_flags {
-    char* name;
-    uint32_t leaf;
-    uint32_t subleaf;
-    int reg;
-    int bit;
-    int length;
-};
-
-/* go through the dynamic array finding the entry for a specified leaf.
- * if no entry exists, allocate one and return that.
- */
-static libxl_cpuid_policy_list cpuid_find_match(libxl_cpuid_policy_list *list,
-                                          uint32_t leaf, uint32_t subleaf)
-{
-    int i = 0;
-
-    if (*list != NULL) {
-        for (i = 0; (*list)[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
-            if ((*list)[i].input[0] == leaf && (*list)[i].input[1] == subleaf)
-                return *list + i;
-        }
-    }
-    *list = realloc(*list, sizeof((*list)[0]) * (i + 2));
-    (*list)[i].input[0] = leaf;
-    (*list)[i].input[1] = subleaf;
-    memset((*list)[i].policy, 0, 4 * sizeof(char*));
-    (*list)[i + 1].input[0] = XEN_CPUID_INPUT_UNUSED;
-    return *list + i;
-}
-
-/* parse a single key=value pair and translate it into the libxc
- * used interface using 32-characters strings for each register.
- * Will overwrite earlier entries and thus can be called multiple
- * times.
- */
-int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
-{
-#define NA XEN_CPUID_INPUT_UNUSED
-    static const struct cpuid_flags cpuid_flags[] = {
-        {"maxleaf",      0x00000000, NA, CPUID_REG_EAX,  0, 32},
-      /* the following two entries are subject to tweaking later in the code */
-        {"stepping",     0x00000001, NA, CPUID_REG_EAX,  0,  4},
-        {"model",        0x00000001, NA, CPUID_REG_EAX,  4,  8},
-        {"family",       0x00000001, NA, CPUID_REG_EAX,  8,  8},
-
-        {"brandid",      0x00000001, NA, CPUID_REG_EBX,  0,  8},
-        {"clflush",      0x00000001, NA, CPUID_REG_EBX,  8,  8},
-        {"proccount",    0x00000001, NA, CPUID_REG_EBX, 16,  8},
-        {"localapicid",  0x00000001, NA, CPUID_REG_EBX, 24,  8},
-
-        {"sse3",         0x00000001, NA, CPUID_REG_ECX,  0,  1},
-        {"pclmulqdq",    0x00000001, NA, CPUID_REG_ECX,  1,  1},
-        {"dtes64",       0x00000001, NA, CPUID_REG_ECX,  2,  1},
-        {"monitor",      0x00000001, NA, CPUID_REG_ECX,  3,  1},
-        {"dscpl",        0x00000001, NA, CPUID_REG_ECX,  4,  1},
-        {"vmx",          0x00000001, NA, CPUID_REG_ECX,  5,  1},
-        {"smx",          0x00000001, NA, CPUID_REG_ECX,  6,  1},
-        {"est",          0x00000001, NA, CPUID_REG_ECX,  7,  1},
-        {"tm2",          0x00000001, NA, CPUID_REG_ECX,  8,  1},
-        {"ssse3",        0x00000001, NA, CPUID_REG_ECX,  9,  1},
-        {"cntxid",       0x00000001, NA, CPUID_REG_ECX, 10,  1},
-        {"fma",          0x00000001, NA, CPUID_REG_ECX, 12,  1},
-        {"cmpxchg16",    0x00000001, NA, CPUID_REG_ECX, 13,  1},
-        {"xtpr",         0x00000001, NA, CPUID_REG_ECX, 14,  1},
-        {"pdcm",         0x00000001, NA, CPUID_REG_ECX, 15,  1},
-        {"pcid",         0x00000001, NA, CPUID_REG_ECX, 17,  1},
-        {"dca",          0x00000001, NA, CPUID_REG_ECX, 18,  1},
-        /* Linux uses sse4_{1,2}.  Keep sse4.{1,2} for compatibility */
-        {"sse4_1",       0x00000001, NA, CPUID_REG_ECX, 19,  1},
-        {"sse4.1",       0x00000001, NA, CPUID_REG_ECX, 19,  1},
-        {"sse4_2",       0x00000001, NA, CPUID_REG_ECX, 20,  1},
-        {"sse4.2",       0x00000001, NA, CPUID_REG_ECX, 20,  1},
-        {"x2apic",       0x00000001, NA, CPUID_REG_ECX, 21,  1},
-        {"movbe",        0x00000001, NA, CPUID_REG_ECX, 22,  1},
-        {"popcnt",       0x00000001, NA, CPUID_REG_ECX, 23,  1},
-        {"tsc-deadline", 0x00000001, NA, CPUID_REG_ECX, 24,  1},
-        {"aes",          0x00000001, NA, CPUID_REG_ECX, 25,  1},
-        {"xsave",        0x00000001, NA, CPUID_REG_ECX, 26,  1},
-        {"osxsave",      0x00000001, NA, CPUID_REG_ECX, 27,  1},
-        {"avx",          0x00000001, NA, CPUID_REG_ECX, 28,  1},
-        {"f16c",         0x00000001, NA, CPUID_REG_ECX, 29,  1},
-        {"rdrand",       0x00000001, NA, CPUID_REG_ECX, 30,  1},
-        {"hypervisor",   0x00000001, NA, CPUID_REG_ECX, 31,  1},
-
-        {"fpu",          0x00000001, NA, CPUID_REG_EDX,  0,  1},
-        {"vme",          0x00000001, NA, CPUID_REG_EDX,  1,  1},
-        {"de",           0x00000001, NA, CPUID_REG_EDX,  2,  1},
-        {"pse",          0x00000001, NA, CPUID_REG_EDX,  3,  1},
-        {"tsc",          0x00000001, NA, CPUID_REG_EDX,  4,  1},
-        {"msr",          0x00000001, NA, CPUID_REG_EDX,  5,  1},
-        {"pae",          0x00000001, NA, CPUID_REG_EDX,  6,  1},
-        {"mce",          0x00000001, NA, CPUID_REG_EDX,  7,  1},
-        {"cmpxchg8",     0x00000001, NA, CPUID_REG_EDX,  8,  1},
-        {"apic",         0x00000001, NA, CPUID_REG_EDX,  9,  1},
-        {"sysenter",     0x00000001, NA, CPUID_REG_EDX, 11,  1},
-        {"mtrr",         0x00000001, NA, CPUID_REG_EDX, 12,  1},
-        {"pge",          0x00000001, NA, CPUID_REG_EDX, 13,  1},
-        {"mca",          0x00000001, NA, CPUID_REG_EDX, 14,  1},
-        {"cmov",         0x00000001, NA, CPUID_REG_EDX, 15,  1},
-        {"pat",          0x00000001, NA, CPUID_REG_EDX, 16,  1},
-        {"pse36",        0x00000001, NA, CPUID_REG_EDX, 17,  1},
-        {"psn",          0x00000001, NA, CPUID_REG_EDX, 18,  1},
-        {"clfsh",        0x00000001, NA, CPUID_REG_EDX, 19,  1},
-        {"ds",           0x00000001, NA, CPUID_REG_EDX, 21,  1},
-        {"acpi",         0x00000001, NA, CPUID_REG_EDX, 22,  1},
-        {"mmx",          0x00000001, NA, CPUID_REG_EDX, 23,  1},
-        {"fxsr",         0x00000001, NA, CPUID_REG_EDX, 24,  1},
-        {"sse",          0x00000001, NA, CPUID_REG_EDX, 25,  1},
-        {"sse2",         0x00000001, NA, CPUID_REG_EDX, 26,  1},
-        {"ss",           0x00000001, NA, CPUID_REG_EDX, 27,  1},
-        {"htt",          0x00000001, NA, CPUID_REG_EDX, 28,  1},
-        {"tm",           0x00000001, NA, CPUID_REG_EDX, 29,  1},
-        {"ia64",         0x00000001, NA, CPUID_REG_EDX, 30,  1},
-        {"pbe",          0x00000001, NA, CPUID_REG_EDX, 31,  1},
-
-        {"arat",         0x00000006, NA, CPUID_REG_EAX,  2,  1},
-
-        {"fsgsbase",     0x00000007,  0, CPUID_REG_EBX,  0,  1},
-        {"tsc_adjust",   0x00000007,  0, CPUID_REG_EBX,  1,  1},
-        {"bmi1",         0x00000007,  0, CPUID_REG_EBX,  3,  1},
-        {"hle",          0x00000007,  0, CPUID_REG_EBX,  4,  1},
-        {"avx2",         0x00000007,  0, CPUID_REG_EBX,  5,  1},
-        {"smep",         0x00000007,  0, CPUID_REG_EBX,  7,  1},
-        {"bmi2",         0x00000007,  0, CPUID_REG_EBX,  8,  1},
-        {"erms",         0x00000007,  0, CPUID_REG_EBX,  9,  1},
-        {"invpcid",      0x00000007,  0, CPUID_REG_EBX, 10,  1},
-        {"rtm",          0x00000007,  0, CPUID_REG_EBX, 11,  1},
-        {"cmt",          0x00000007,  0, CPUID_REG_EBX, 12,  1},
-        {"mpx",          0x00000007,  0, CPUID_REG_EBX, 14,  1},
-        {"avx512f",      0x00000007,  0, CPUID_REG_EBX, 16,  1},
-        {"avx512dq",     0x00000007,  0, CPUID_REG_EBX, 17,  1},
-        {"rdseed",       0x00000007,  0, CPUID_REG_EBX, 18,  1},
-        {"adx",          0x00000007,  0, CPUID_REG_EBX, 19,  1},
-        {"smap",         0x00000007,  0, CPUID_REG_EBX, 20,  1},
-        {"avx512-ifma",  0x00000007,  0, CPUID_REG_EBX, 21,  1},
-        {"clflushopt",   0x00000007,  0, CPUID_REG_EBX, 23,  1},
-        {"clwb",         0x00000007,  0, CPUID_REG_EBX, 24,  1},
-        {"avx512pf",     0x00000007,  0, CPUID_REG_EBX, 26,  1},
-        {"avx512er",     0x00000007,  0, CPUID_REG_EBX, 27,  1},
-        {"avx512cd",     0x00000007,  0, CPUID_REG_EBX, 28,  1},
-        {"sha",          0x00000007,  0, CPUID_REG_EBX, 29,  1},
-        {"avx512bw",     0x00000007,  0, CPUID_REG_EBX, 30,  1},
-        {"avx512vl",     0x00000007,  0, CPUID_REG_EBX, 31,  1},
-
-        {"prefetchwt1",  0x00000007,  0, CPUID_REG_ECX,  0,  1},
-        {"avx512-vbmi",  0x00000007,  0, CPUID_REG_ECX,  1,  1},
-        {"umip",         0x00000007,  0, CPUID_REG_ECX,  2,  1},
-        {"pku",          0x00000007,  0, CPUID_REG_ECX,  3,  1},
-        {"ospke",        0x00000007,  0, CPUID_REG_ECX,  4,  1},
-        {"avx512-vbmi2", 0x00000007,  0, CPUID_REG_ECX,  6,  1},
-        {"cet-ss",       0x00000007,  0, CPUID_REG_ECX,  7,  1},
-        {"gfni",         0x00000007,  0, CPUID_REG_ECX,  8,  1},
-        {"vaes",         0x00000007,  0, CPUID_REG_ECX,  9,  1},
-        {"vpclmulqdq",   0x00000007,  0, CPUID_REG_ECX, 10,  1},
-        {"avx512-vnni",  0x00000007,  0, CPUID_REG_ECX, 11,  1},
-        {"avx512-bitalg",0x00000007,  0, CPUID_REG_ECX, 12,  1},
-        {"avx512-vpopcntdq",0x00000007,0,CPUID_REG_ECX, 14,  1},
-        {"tsxldtrk",     0x00000007,  0, CPUID_REG_ECX, 16,  1},
-        {"rdpid",        0x00000007,  0, CPUID_REG_ECX, 22,  1},
-        {"cldemote",     0x00000007,  0, CPUID_REG_ECX, 25,  1},
-
-        {"avx512-4vnniw",0x00000007,  0, CPUID_REG_EDX,  2,  1},
-        {"avx512-4fmaps",0x00000007,  0, CPUID_REG_EDX,  3,  1},
-        {"avx512-vp2intersect",0x00000007,0,CPUID_REG_EDX,8, 1},
-        {"srbds-ctrl",   0x00000007,  0, CPUID_REG_EDX,  9,  1},
-        {"md-clear",     0x00000007,  0, CPUID_REG_EDX, 10,  1},
-        {"serialize",    0x00000007,  0, CPUID_REG_EDX, 14,  1},
-        {"cet-ibt",      0x00000007,  0, CPUID_REG_EDX, 20,  1},
-        {"ibrsb",        0x00000007,  0, CPUID_REG_EDX, 26,  1},
-        {"stibp",        0x00000007,  0, CPUID_REG_EDX, 27,  1},
-        {"l1d-flush",    0x00000007,  0, CPUID_REG_EDX, 28,  1},
-        {"arch-caps",    0x00000007,  0, CPUID_REG_EDX, 29,  1},
-        {"core-caps",    0x00000007,  0, CPUID_REG_EDX, 30,  1},
-        {"ssbd",         0x00000007,  0, CPUID_REG_EDX, 31,  1},
-
-        {"avx512-bf16",  0x00000007,  1, CPUID_REG_EAX,  5,  1},
-
-        {"lahfsahf",     0x80000001, NA, CPUID_REG_ECX,  0,  1},
-        {"cmplegacy",    0x80000001, NA, CPUID_REG_ECX,  1,  1},
-        {"svm",          0x80000001, NA, CPUID_REG_ECX,  2,  1},
-        {"extapic",      0x80000001, NA, CPUID_REG_ECX,  3,  1},
-        {"altmovcr8",    0x80000001, NA, CPUID_REG_ECX,  4,  1},
-        {"abm",          0x80000001, NA, CPUID_REG_ECX,  5,  1},
-        {"sse4a",        0x80000001, NA, CPUID_REG_ECX,  6,  1},
-        {"misalignsse",  0x80000001, NA, CPUID_REG_ECX,  7,  1},
-        {"3dnowprefetch",0x80000001, NA, CPUID_REG_ECX,  8,  1},
-        {"osvw",         0x80000001, NA, CPUID_REG_ECX,  9,  1},
-        {"ibs",          0x80000001, NA, CPUID_REG_ECX, 10,  1},
-        {"xop",          0x80000001, NA, CPUID_REG_ECX, 11,  1},
-        {"skinit",       0x80000001, NA, CPUID_REG_ECX, 12,  1},
-        {"wdt",          0x80000001, NA, CPUID_REG_ECX, 13,  1},
-        {"lwp",          0x80000001, NA, CPUID_REG_ECX, 15,  1},
-        {"fma4",         0x80000001, NA, CPUID_REG_ECX, 16,  1},
-        {"nodeid",       0x80000001, NA, CPUID_REG_ECX, 19,  1},
-        {"tbm",          0x80000001, NA, CPUID_REG_ECX, 21,  1},
-        {"topoext",      0x80000001, NA, CPUID_REG_ECX, 22,  1},
-        {"perfctr_core", 0x80000001, NA, CPUID_REG_ECX, 23,  1},
-        {"perfctr_nb",   0x80000001, NA, CPUID_REG_ECX, 24,  1},
-
-        {"syscall",      0x80000001, NA, CPUID_REG_EDX, 11,  1},
-        {"nx",           0x80000001, NA, CPUID_REG_EDX, 20,  1},
-        {"mmxext",       0x80000001, NA, CPUID_REG_EDX, 22,  1},
-        {"ffxsr",        0x80000001, NA, CPUID_REG_EDX, 25,  1},
-        {"page1gb",      0x80000001, NA, CPUID_REG_EDX, 26,  1},
-        {"rdtscp",       0x80000001, NA, CPUID_REG_EDX, 27,  1},
-        {"lm",           0x80000001, NA, CPUID_REG_EDX, 29,  1},
-        {"3dnowext",     0x80000001, NA, CPUID_REG_EDX, 30,  1},
-        {"3dnow",        0x80000001, NA, CPUID_REG_EDX, 31,  1},
-
-        {"procpkg",      0x00000004,  0, CPUID_REG_EAX, 26,  6},
-
-        {"invtsc",       0x80000007, NA, CPUID_REG_EDX,  8,  1},
-
-        {"clzero",       0x80000008, NA, CPUID_REG_EBX,  0,  1},
-        {"rstr-fp-err-ptrs", 0x80000008, NA, CPUID_REG_EBX, 2, 1},
-        {"wbnoinvd",     0x80000008, NA, CPUID_REG_EBX,  9,  1},
-        {"ibpb",         0x80000008, NA, CPUID_REG_EBX, 12,  1},
-        {"ppin",         0x80000008, NA, CPUID_REG_EBX, 23,  1},
-
-        {"nc",           0x80000008, NA, CPUID_REG_ECX,  0,  8},
-        {"apicidsize",   0x80000008, NA, CPUID_REG_ECX, 12,  4},
-
-        {"svm_npt",      0x8000000a, NA, CPUID_REG_EDX,  0,  1},
-        {"svm_lbrv",     0x8000000a, NA, CPUID_REG_EDX,  1,  1},
-        {"svm_nrips",    0x8000000a, NA, CPUID_REG_EDX,  3,  1},
-        {"svm_tscrate",  0x8000000a, NA, CPUID_REG_EDX,  4,  1},
-        {"svm_vmcbclean",0x8000000a, NA, CPUID_REG_EDX,  5,  1},
-        {"svm_decode",   0x8000000a, NA, CPUID_REG_EDX,  7,  1},
-        {"svm_pausefilt",0x8000000a, NA, CPUID_REG_EDX, 10,  1},
-
-        {"maxhvleaf",    0x40000000, NA, CPUID_REG_EAX,  0,  8},
-
-        {NULL, 0, NA, CPUID_REG_INV, 0, 0}
-    };
-#undef NA
-    char *sep, *val, *endptr;
-    int i;
-    const struct cpuid_flags *flag;
-    struct xc_xend_cpuid *entry;
-    unsigned long num;
-    char flags[33], *resstr;
-
-    sep = strchr(str, '=');
-    if (sep == NULL) {
-        return 1;
-    } else {
-        val = sep + 1;
-    }
-    for (flag = cpuid_flags; flag->name != NULL; flag++) {
-        if(!strncmp(str, flag->name, sep - str) && flag->name[sep - str] == 0)
-            break;
-    }
-    if (flag->name == NULL) {
-        return 2;
-    }
-    entry = cpuid_find_match(cpuid, flag->leaf, flag->subleaf);
-    resstr = entry->policy[flag->reg - 1];
-    num = strtoull(val, &endptr, 0);
-    flags[flag->length] = 0;
-    if (endptr != val) {
-        /* if this was a valid number, write the binary form into the string */
-        for (i = 0; i < flag->length; i++) {
-            flags[flag->length - 1 - i] = "01"[!!(num & (1 << i))];
-        }
-    } else {
-        switch(val[0]) {
-        case 'x': case 'k': case 's':
-            memset(flags, val[0], flag->length);
-            break;
-        default:
-            return 3;
-        }
-    }
-
-    if (resstr == NULL) {
-        resstr = strdup("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-    }
-
-    /* the family and model entry is potentially split up across
-     * two fields in Fn0000_0001_EAX, so handle them here separately.
-     */
-    if (!strncmp(str, "family", sep - str)) {
-        if (num < 16) {
-            memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
-            memcpy(resstr + (32 - 8) - 20, "00000000", 8);
-        } else {
-            num -= 15;
-            memcpy(resstr + (32 - 4) - flag->bit, "1111", 4);
-            for (i = 0; i < 7; i++) {
-                flags[7 - i] = "01"[num & 1];
-                num >>= 1;
-            }
-            memcpy(resstr + (32 - 8) - 20, flags, 8);
-        }
-    } else if (!strncmp(str, "model", sep - str)) {
-        memcpy(resstr + (32 - 4) - 16, flags, 4);
-        memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
-    } else {
-        memcpy(resstr + (32 - flag->length) - flag->bit, flags,
-               flag->length);
-    }
-    entry->policy[flag->reg - 1] = resstr;
-
-    return 0;
-}
-
-/* parse a single list item from the legacy Python xend syntax, where
- * the strings for each register were directly exposed to the user.
- * Used for maintaining compatibility with older config files
- */
-int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
-                                  const char* str)
-{
-    char *endptr;
-    unsigned long value;
-    uint32_t leaf, subleaf = XEN_CPUID_INPUT_UNUSED;
-    struct xc_xend_cpuid *entry;
-
-    /* parse the leaf number */
-    value = strtoul(str, &endptr, 0);
-    if (str == endptr) {
-        return 1;
-    }
-    leaf = value;
-    /* check for an optional subleaf number */
-    if (*endptr == ',') {
-        str = endptr + 1;
-        value = strtoul(str, &endptr, 0);
-        if (str == endptr) {
-            return 2;
-        }
-        subleaf = value;
-    }
-    if (*endptr != ':') {
-        return 3;
-    }
-    str = endptr + 1;
-    entry = cpuid_find_match(cpuid, leaf, subleaf);
-    for (str = endptr + 1; *str != 0;) {
-        if (str[0] != 'e' || str[2] != 'x') {
-            return 4;
-        }
-        value = str[1] - 'a';
-        endptr = strchr(str, '=');
-        if (value > 3 || endptr == NULL) {
-            return 4;
-        }
-        str = endptr + 1;
-        endptr = strchr(str, ',');
-        if (endptr == NULL) {
-            endptr = strchr(str, 0);
-        }
-        if (endptr - str != 32) {
-            return 5;
-        }
-        entry->policy[value] = calloc(32 + 1, 1);
-        strncpy(entry->policy[value], str, 32);
-        entry->policy[value][32] = 0;
-        if (*endptr == 0) {
-            break;
-        }
-        for (str = endptr + 1; *str == ' ' || *str == '\n'; str++);
-    }
-    return 0;
-}
-
-void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
-                         libxl_domain_build_info *info)
-{
-    bool pae = true;
-    bool itsc;
-    bool nested_virt = libxl_defbool_val(info->nested_hvm);
-
-    /*
-     * For PV guests, PAE is Xen-controlled (it is the 'p' that differentiates
-     * the xen-3.0-x86_32 and xen-3.0-x86_32p ABIs).  It is mandatory as Xen
-     * is 64bit only these days.
-     *
-     * For PVH guests, there is no top-level PAE control in the domain config,
-     * so is treated as always available.
-     *
-     * HVM guests get a top-level choice of whether PAE is available.
-     */
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM)
-        pae = libxl_defbool_val(info->u.hvm.pae);
-
-    /*
-     * Advertising Invariant TSC to a guest means that the TSC frequency won't
-     * change at any point in the future.
-     *
-     * We do not have enough information about potential migration
-     * destinations to know whether advertising ITSC is safe, but if the guest
-     * isn't going to migrate, then the current hardware is all that matters.
-     *
-     * Alternatively, an internal property of vTSC is that the values read are
-     * invariant.  Advertise ITSC when we know the domain will have emualted
-     * TSC everywhere it goes.
-     */
-    itsc = (libxl_defbool_val(info->disable_migrate) ||
-            info->tsc_mode == LIBXL_TSC_MODE_ALWAYS_EMULATE);
-
-    xc_cpuid_apply_policy(ctx->xch, domid, restore, NULL, 0,
-                          pae, itsc, nested_virt, info->cpuid);
-}
-
-static const char *input_names[2] = { "leaf", "subleaf" };
-static const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" };
-/*
- * Aiming for:
- * [
- *     { 'leaf':    'val-eax',
- *       'subleaf': 'val-ecx',
- *       'eax':     'filter',
- *       'ebx':     'filter',
- *       'ecx':     'filter',
- *       'edx':     'filter' },
- *     { 'leaf':    'val-eax', ..., 'eax': 'filter', ... },
- *     ... etc ...
- * ]
- */
-
-yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
-                                libxl_cpuid_policy_list *pcpuid)
-{
-    libxl_cpuid_policy_list cpuid = *pcpuid;
-    yajl_gen_status s;
-    int i, j;
-
-    s = yajl_gen_array_open(hand);
-    if (s != yajl_gen_status_ok) goto out;
-
-    if (cpuid == NULL) goto empty;
-
-    for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
-        s = yajl_gen_map_open(hand);
-        if (s != yajl_gen_status_ok) goto out;
-
-        for (j = 0; j < 2; j++) {
-            if (cpuid[i].input[j] != XEN_CPUID_INPUT_UNUSED) {
-                s = libxl__yajl_gen_asciiz(hand, input_names[j]);
-                if (s != yajl_gen_status_ok) goto out;
-                s = yajl_gen_integer(hand, cpuid[i].input[j]);
-                if (s != yajl_gen_status_ok) goto out;
-            }
-        }
-
-        for (j = 0; j < 4; j++) {
-            if (cpuid[i].policy[j] != NULL) {
-                s = libxl__yajl_gen_asciiz(hand, policy_names[j]);
-                if (s != yajl_gen_status_ok) goto out;
-                s = yajl_gen_string(hand,
-                               (const unsigned char *)cpuid[i].policy[j], 32);
-                if (s != yajl_gen_status_ok) goto out;
-            }
-        }
-        s = yajl_gen_map_close(hand);
-        if (s != yajl_gen_status_ok) goto out;
-    }
-
-empty:
-    s = yajl_gen_array_close(hand);
-out:
-    return s;
-}
-
-int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
-                                        const libxl__json_object *o,
-                                        libxl_cpuid_policy_list *p)
-{
-    int i, size;
-    libxl_cpuid_policy_list l;
-    flexarray_t *array;
-
-    if (!libxl__json_object_is_array(o))
-        return ERROR_FAIL;
-
-    array = libxl__json_object_get_array(o);
-    if (!array->count)
-        return 0;
-
-    size = array->count;
-    /* need one extra slot as sentinel */
-    l = *p = libxl__calloc(NOGC, size + 1, sizeof(libxl_cpuid_policy));
-
-    l[size].input[0] = XEN_CPUID_INPUT_UNUSED;
-    l[size].input[1] = XEN_CPUID_INPUT_UNUSED;
-
-    for (i = 0; i < size; i++) {
-        const libxl__json_object *t;
-        int j;
-
-        if (flexarray_get(array, i, (void**)&t) != 0)
-            return ERROR_FAIL;
-
-        if (!libxl__json_object_is_map(t))
-            return ERROR_FAIL;
-
-        for (j = 0; j < ARRAY_SIZE(l[0].input); j++) {
-            const libxl__json_object *r;
-
-            r = libxl__json_map_get(input_names[j], t, JSON_INTEGER);
-            if (!r)
-                l[i].input[j] = XEN_CPUID_INPUT_UNUSED;
-            else
-                l[i].input[j] = libxl__json_object_get_integer(r);
-        }
-
-        for (j = 0; j < ARRAY_SIZE(l[0].policy); j++) {
-            const libxl__json_object *r;
-
-            r = libxl__json_map_get(policy_names[j], t, JSON_STRING);
-            if (!r)
-                l[i].policy[j] = NULL;
-            else
-                l[i].policy[j] =
-                    libxl__strdup(NOGC, libxl__json_object_get_string(r));
-        }
-    }
-
-    return 0;
-}
-
-int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *pl)
-{
-    int i = 0;
-    libxl_cpuid_policy_list l = *pl;
-
-    if (l) {
-        while (l[i].input[0] != XEN_CPUID_INPUT_UNUSED)
-            i++;
-    }
-
-    return i;
-}
-
-void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
-                                  libxl_cpuid_policy_list *dst,
-                                  const libxl_cpuid_policy_list *src)
-{
-    GC_INIT(ctx);
-    int i, j, len;
-
-    if (*src == NULL) {
-        *dst = NULL;
-        goto out;
-    }
-
-    len = libxl_cpuid_policy_list_length(src);
-    /* one extra slot for sentinel */
-    *dst = libxl__calloc(NOGC, len + 1, sizeof(libxl_cpuid_policy));
-    (*dst)[len].input[0] = XEN_CPUID_INPUT_UNUSED;
-    (*dst)[len].input[1] = XEN_CPUID_INPUT_UNUSED;
-
-    for (i = 0; i < len; i++) {
-        for (j = 0; j < 2; j++)
-            (*dst)[i].input[j] = (*src)[i].input[j];
-        for (j = 0; j < 4; j++)
-            if ((*src)[i].policy[j])
-                (*dst)[i].policy[j] =
-                    libxl__strdup(NOGC, (*src)[i].policy[j]);
-            else
-                (*dst)[i].policy[j] = NULL;
-    }
-
-out:
-    GC_FREE;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_cpupool.c b/tools/libxl/libxl_cpupool.c
deleted file mode 100644 (file)
index 85b0688..0000000
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/* Returns:
- *   0 - success
- *   ERROR_FAIL + errno == ENOENT - no entry found
- *   ERROR_$FOO + errno != ENOENT - other failure
- */
-static int cpupool_info(libxl__gc *gc,
-                        libxl_cpupoolinfo *info,
-                        uint32_t poolid,
-                        bool exact /* exactly poolid or >= poolid */)
-{
-    xc_cpupoolinfo_t *xcinfo;
-    int rc = ERROR_FAIL;
-
-    xcinfo = xc_cpupool_getinfo(CTX->xch, poolid);
-    if (xcinfo == NULL)
-    {
-        if (exact || errno != ENOENT)
-            LOGE(ERROR, "failed to get info for cpupool%d", poolid);
-        return ERROR_FAIL;
-    }
-
-    if (exact && xcinfo->cpupool_id != poolid)
-    {
-        LOG(ERROR, "got info for cpupool%d, wanted cpupool%d\n",
-            xcinfo->cpupool_id, poolid);
-        goto out;
-    }
-
-    info->poolid = xcinfo->cpupool_id;
-    info->pool_name = libxl_cpupoolid_to_name(CTX, info->poolid);
-    if (!info->pool_name) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    info->sched = xcinfo->sched_id;
-    info->n_dom = xcinfo->n_dom;
-    rc = libxl_cpu_bitmap_alloc(CTX, &info->cpumap, 0);
-    if (rc)
-        goto out;
-
-    memcpy(info->cpumap.map, xcinfo->cpumap, info->cpumap.size);
-
-    rc = 0;
-out:
-    xc_cpupool_infofree(CTX->xch, xcinfo);
-    return rc;
-}
-
-int libxl_cpupool_info(libxl_ctx *ctx,
-                       libxl_cpupoolinfo *info, uint32_t poolid)
-{
-    GC_INIT(ctx);
-    int rc = cpupool_info(gc, info, poolid, true);
-    GC_FREE;
-    return rc;
-}
-
-libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx *ctx, int *nb_pool_out)
-{
-    GC_INIT(ctx);
-    libxl_cpupoolinfo info, *ptr;
-
-    int i;
-    uint32_t poolid;
-
-    ptr = NULL;
-
-    poolid = 0;
-    for (i = 0;; i++) {
-        libxl_cpupoolinfo_init(&info);
-        if (cpupool_info(gc, &info, poolid, false)) {
-            libxl_cpupoolinfo_dispose(&info);
-            if (errno != ENOENT) goto out;
-            break;
-        }
-
-        ptr = libxl__realloc(NOGC, ptr, (i+1) * sizeof(libxl_cpupoolinfo));
-        ptr[i] = info;
-        poolid = info.poolid + 1;
-        /* Don't dispose of info because it will be returned to caller */
-    }
-
-    *nb_pool_out = i;
-
-    GC_FREE;
-    return ptr;
-
-out:
-    libxl_cpupoolinfo_list_free(ptr, i);
-    *nb_pool_out = 0;
-    GC_FREE;
-    return NULL;
-}
-
-int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap)
-{
-    int ncpus;
-
-    ncpus = libxl_get_max_cpus(ctx);
-    if (ncpus < 0)
-        return ncpus;
-
-    cpumap->map = xc_cpupool_freeinfo(ctx->xch);
-    if (cpumap->map == NULL)
-        return ERROR_FAIL;
-
-    cpumap->size = (ncpus + 7) / 8;
-
-    return 0;
-}
-
-int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
-                         libxl_scheduler sched,
-                         libxl_bitmap cpumap, libxl_uuid *uuid,
-                         uint32_t *poolid)
-{
-    GC_INIT(ctx);
-    int rc;
-    int i;
-    xs_transaction_t t;
-    char *uuid_string;
-    uint32_t xcpoolid;
-
-    /* Accept '0' as 'any poolid' for backwards compatibility */
-    if ( *poolid == LIBXL_CPUPOOL_POOLID_ANY
-         || *poolid == 0 )
-        xcpoolid = XC_CPUPOOL_POOLID_ANY;
-    else
-        xcpoolid = *poolid;
-
-    uuid_string = libxl__uuid2string(gc, *uuid);
-    if (!uuid_string) {
-        GC_FREE;
-        return ERROR_NOMEM;
-    }
-
-    rc = xc_cpupool_create(ctx->xch, &xcpoolid, sched);
-    if (rc) {
-        LOGEV(ERROR, rc, "Could not create cpupool");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    *poolid = xcpoolid;
-
-    libxl_for_each_bit(i, cpumap)
-        if (libxl_bitmap_test(&cpumap, i)) {
-            rc = xc_cpupool_addcpu(ctx->xch, *poolid, i);
-            if (rc) {
-                LOGEV(ERROR, rc, "Error moving cpu to cpupool");
-                libxl_cpupool_destroy(ctx, *poolid);
-                GC_FREE;
-                return ERROR_FAIL;
-            }
-        }
-
-    for (;;) {
-        t = xs_transaction_start(ctx->xsh);
-
-        xs_mkdir(ctx->xsh, t, GCSPRINTF("/local/pool/%d", *poolid));
-        libxl__xs_printf(gc, t,
-                         GCSPRINTF("/local/pool/%d/uuid", *poolid),
-                         "%s", uuid_string);
-        libxl__xs_printf(gc, t,
-                         GCSPRINTF("/local/pool/%d/name", *poolid),
-                         "%s", name);
-
-        if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN)) {
-            GC_FREE;
-            return 0;
-        }
-    }
-}
-
-int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid)
-{
-    GC_INIT(ctx);
-    int rc, i;
-    xc_cpupoolinfo_t *info;
-    xs_transaction_t t;
-    libxl_bitmap cpumap;
-
-    info = xc_cpupool_getinfo(ctx->xch, poolid);
-    if (info == NULL) {
-        GC_FREE;
-        return ERROR_NOMEM;
-    }
-
-    rc = ERROR_INVAL;
-    if ((info->cpupool_id != poolid) || (info->n_dom))
-        goto out;
-
-    rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, 0);
-    if (rc)
-        goto out;
-
-    memcpy(cpumap.map, info->cpumap, cpumap.size);
-    libxl_for_each_bit(i, cpumap)
-        if (libxl_bitmap_test(&cpumap, i)) {
-            rc = xc_cpupool_removecpu(ctx->xch, poolid, i);
-            if (rc) {
-                LOGEV(ERROR, rc, "Error removing cpu from cpupool");
-                rc = ERROR_FAIL;
-                goto out1;
-            }
-        }
-
-    rc = xc_cpupool_destroy(ctx->xch, poolid);
-    if (rc) {
-        LOGEV(ERROR, rc, "Could not destroy cpupool");
-        rc = ERROR_FAIL;
-        goto out1;
-    }
-
-    for (;;) {
-        t = xs_transaction_start(ctx->xsh);
-
-        xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF("/local/pool/%d", poolid));
-
-        if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN))
-            break;
-    }
-
-    rc = 0;
-
-out1:
-    libxl_bitmap_dispose(&cpumap);
-out:
-    xc_cpupool_infofree(ctx->xch, info);
-    GC_FREE;
-
-    return rc;
-}
-
-int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid)
-{
-    GC_INIT(ctx);
-    xs_transaction_t t;
-    xc_cpupoolinfo_t *info;
-    int rc;
-
-    info = xc_cpupool_getinfo(ctx->xch, poolid);
-    if (info == NULL) {
-        GC_FREE;
-        return ERROR_NOMEM;
-    }
-
-    rc = ERROR_INVAL;
-    if (info->cpupool_id != poolid)
-        goto out;
-
-    rc = 0;
-
-    for (;;) {
-        t = xs_transaction_start(ctx->xsh);
-
-        libxl__xs_printf(gc, t,
-                         GCSPRINTF("/local/pool/%d/name", poolid),
-                         "%s", name);
-
-        if (xs_transaction_end(ctx->xsh, t, 0))
-            break;
-
-        if (errno == EAGAIN)
-            continue;
-
-        rc = ERROR_FAIL;
-        break;
-    }
-
-out:
-    xc_cpupool_infofree(ctx->xch, info);
-    GC_FREE;
-
-    return rc;
-}
-
-int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu)
-{
-    GC_INIT(ctx);
-    int rc = 0;
-
-    rc = xc_cpupool_addcpu(ctx->xch, poolid, cpu);
-    if (rc) {
-        LOGE(ERROR, "Error moving cpu %d to cpupool", cpu);
-        rc = ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
-                                const libxl_bitmap *cpumap)
-{
-    int c, ncpus = 0, rc = 0;
-
-    libxl_for_each_set_bit(c, *cpumap) {
-        if (!libxl_cpupool_cpuadd(ctx, poolid, c))
-            ncpus++;
-    }
-
-    if (ncpus != libxl_bitmap_count_set(cpumap))
-        rc = ERROR_FAIL;
-
-    return rc;
-}
-
-int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
-{
-    int rc = 0;
-    int cpu, nr;
-    libxl_bitmap freemap;
-    libxl_cputopology *topology;
-
-    if (libxl_get_freecpus(ctx, &freemap)) {
-        return ERROR_FAIL;
-    }
-
-    topology = libxl_get_cpu_topology(ctx, &nr);
-    if (!topology) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    *cpus = 0;
-    for (cpu = 0; cpu < nr; cpu++) {
-        if (libxl_bitmap_test(&freemap, cpu) && (topology[cpu].node == node) &&
-            !libxl_cpupool_cpuadd(ctx, poolid, cpu)) {
-                (*cpus)++;
-        }
-        libxl_cputopology_dispose(&topology[cpu]);
-    }
-
-    free(topology);
-out:
-    libxl_bitmap_dispose(&freemap);
-    return rc;
-}
-
-int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu)
-{
-    GC_INIT(ctx);
-    int rc = 0;
-
-    rc = xc_cpupool_removecpu(ctx->xch, poolid, cpu);
-    if (rc) {
-        LOGE(ERROR, "Error removing cpu %d from cpupool", cpu);
-        rc = ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
-                                   const libxl_bitmap *cpumap)
-{
-    int c, ncpus = 0, rc = 0;
-
-    libxl_for_each_set_bit(c, *cpumap) {
-        if (!libxl_cpupool_cpuremove(ctx, poolid, c))
-            ncpus++;
-    }
-
-    if (ncpus != libxl_bitmap_count_set(cpumap))
-        rc = ERROR_FAIL;
-
-    return rc;
-}
-
-int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
-{
-    int ret = 0;
-    int n_pools;
-    int p;
-    int cpu, nr_cpus;
-    libxl_cputopology *topology;
-    libxl_cpupoolinfo *poolinfo;
-
-    poolinfo = libxl_list_cpupool(ctx, &n_pools);
-    if (!poolinfo) {
-        return ERROR_NOMEM;
-    }
-
-    topology = libxl_get_cpu_topology(ctx, &nr_cpus);
-    if (!topology) {
-        ret = ERROR_FAIL;
-        goto out;
-    }
-
-    *cpus = 0;
-    for (p = 0; p < n_pools; p++) {
-        if (poolinfo[p].poolid == poolid) {
-            for (cpu = 0; cpu < nr_cpus; cpu++) {
-                if ((topology[cpu].node == node) &&
-                    libxl_bitmap_test(&poolinfo[p].cpumap, cpu) &&
-                    !libxl_cpupool_cpuremove(ctx, poolid, cpu)) {
-                        (*cpus)++;
-                }
-            }
-        }
-    }
-
-    libxl_cputopology_list_free(topology, nr_cpus);
-
-out:
-    libxl_cpupoolinfo_list_free(poolinfo, n_pools);
-
-    return ret;
-}
-
-int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    rc = xc_cpupool_movedomain(ctx->xch, poolid, domid);
-    if (rc) {
-        LOGEVD(ERROR, rc, domid, "Error moving domain to cpupool");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
deleted file mode 100644 (file)
index 1031b75..0000000
+++ /dev/null
@@ -1,2310 +0,0 @@
-/*
- * Copyright (C) 2010      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- * Author Gianni Tedesco <gianni.tedesco@citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-#include <xenguest.h>
-#include <xen/hvm/hvm_info_table.h>
-#include <xen/hvm/e820.h>
-
-#include <xen-xsm/flask/flask.h>
-
-int libxl__domain_create_info_setdefault(libxl__gc *gc,
-                                         libxl_domain_create_info *c_info,
-                                         const libxl_physinfo *info)
-{
-    if (!c_info->type) {
-        LOG(ERROR, "domain type unspecified");
-        return ERROR_INVAL;
-    }
-
-    libxl__arch_domain_create_info_setdefault(gc, c_info);
-
-    if (c_info->type != LIBXL_DOMAIN_TYPE_PV) {
-        if (info->cap_hap) {
-            libxl_defbool_setdefault(&c_info->hap, true);
-        } else if (info->cap_shadow) {
-            libxl_defbool_setdefault(&c_info->hap, false);
-        } else {
-            LOG(ERROR, "neither hap nor shadow paging available");
-            return ERROR_INVAL;
-        }
-
-        libxl_defbool_setdefault(&c_info->oos, true);
-    }
-
-    libxl_defbool_setdefault(&c_info->run_hotplug_scripts, true);
-    libxl_defbool_setdefault(&c_info->driver_domain, false);
-
-    if (!c_info->ssidref)
-        c_info->ssidref = SECINITSID_DOMU;
-
-    libxl_defbool_setdefault(&c_info->xend_suspend_evtchn_compat, false);
-
-    return 0;
-}
-
-void libxl__rdm_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info)
-{
-    if (b_info->u.hvm.rdm.policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
-        b_info->u.hvm.rdm.policy = LIBXL_RDM_RESERVE_POLICY_RELAXED;
-
-    if (b_info->u.hvm.rdm_mem_boundary_memkb == LIBXL_MEMKB_DEFAULT)
-        b_info->u.hvm.rdm_mem_boundary_memkb =
-                            LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT;
-}
-
-int libxl__domain_build_info_setdefault(libxl__gc *gc,
-                                        libxl_domain_build_info *b_info)
-{
-    int i, rc;
-
-    if (b_info->type != LIBXL_DOMAIN_TYPE_HVM &&
-        b_info->type != LIBXL_DOMAIN_TYPE_PV &&
-        b_info->type != LIBXL_DOMAIN_TYPE_PVH) {
-        LOG(ERROR, "invalid domain type");
-        return ERROR_INVAL;
-    }
-
-    /* Copy deprecated options to it's new position. */
-    rc = libxl__domain_build_info_copy_deprecated(CTX, b_info);
-    if (rc) {
-        LOG(ERROR, "Unable to copy deprecated fields");
-        return rc;
-    }
-
-    libxl_defbool_setdefault(&b_info->device_model_stubdomain, false);
-
-    if (libxl_defbool_val(b_info->device_model_stubdomain) &&
-        !b_info->device_model_ssidref)
-        b_info->device_model_ssidref = SECINITSID_DOMDM;
-
-    if (!b_info->device_model_version) {
-        if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
-            if (libxl_defbool_val(b_info->device_model_stubdomain)) {
-                b_info->device_model_version =
-                    LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
-            } else {
-                b_info->device_model_version = libxl__default_device_model(gc);
-            }
-        } else {
-            b_info->device_model_version =
-                LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-        }
-        if (b_info->device_model_version
-                == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
-            const char *dm;
-
-            dm = libxl__domain_device_model(gc, b_info);
-            rc = access(dm, X_OK);
-            if (rc < 0) {
-                /* qemu-xen unavailable, use qemu-xen-traditional */
-                if (errno == ENOENT) {
-                    LOGE(INFO, "qemu-xen is unavailable"
-                         ", using qemu-xen-traditional instead");
-                    b_info->device_model_version =
-                        LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
-                } else {
-                    LOGE(ERROR, "qemu-xen access error");
-                    return ERROR_FAIL;
-                }
-            }
-        }
-    }
-
-    if (b_info->blkdev_start == NULL)
-        b_info->blkdev_start = libxl__strdup(NOGC, "xvda");
-
-    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        if (!b_info->u.hvm.bios)
-            switch (b_info->device_model_version) {
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-                b_info->u.hvm.bios = LIBXL_BIOS_TYPE_ROMBIOS; break;
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-                b_info->u.hvm.bios = LIBXL_BIOS_TYPE_SEABIOS; break;
-            default:
-                LOG(ERROR, "unknown device model version");
-                return ERROR_INVAL;
-            }
-
-        /* Enforce BIOS<->Device Model version relationship */
-        switch (b_info->device_model_version) {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            if (b_info->u.hvm.bios != LIBXL_BIOS_TYPE_ROMBIOS) {
-                LOG(ERROR, "qemu-xen-traditional requires bios=rombios.");
-                return ERROR_INVAL;
-            }
-            break;
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            if (b_info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS) {
-                LOG(ERROR, "qemu-xen does not support bios=rombios.");
-                return ERROR_INVAL;
-            }
-            break;
-        default:abort();
-        }
-
-        /* Check HVM direct boot parameters, we should honour ->ramdisk and
-         * ->cmdline iff ->kernel is set.
-         */
-        if (!b_info->kernel && (b_info->ramdisk || b_info->cmdline)) {
-            LOG(ERROR, "direct boot parameters specified but kernel missing");
-            return ERROR_INVAL;
-        }
-    }
-
-    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
-        libxl_defbool_val(b_info->device_model_stubdomain)) {
-        if (!b_info->stubdomain_kernel) {
-            switch (b_info->device_model_version) {
-                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-                    b_info->stubdomain_kernel =
-                        libxl__abs_path(NOGC, "ioemu-stubdom.gz", libxl__xenfirmwaredir_path());
-                    break;
-                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-                    b_info->stubdomain_kernel =
-                        libxl__abs_path(NOGC,
-                                "qemu-stubdom-linux-kernel",
-                                libxl__xenfirmwaredir_path());
-                    break;
-                default:
-                    abort();
-            }
-        }
-        if (!b_info->stubdomain_ramdisk) {
-            switch (b_info->device_model_version) {
-                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-                    break;
-                case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-                    b_info->stubdomain_ramdisk =
-                        libxl__abs_path(NOGC,
-                                "qemu-stubdom-linux-rootfs",
-                                libxl__xenfirmwaredir_path());
-                    break;
-                default:
-                    abort();
-            }
-        }
-    }
-
-    if (!b_info->max_vcpus)
-        b_info->max_vcpus = 1;
-    if (!b_info->avail_vcpus.size) {
-        if (libxl_cpu_bitmap_alloc(CTX, &b_info->avail_vcpus, 1)) {
-            LOG(ERROR, "unable to allocate avail_vcpus bitmap");
-            return ERROR_FAIL;
-        }
-        libxl_bitmap_set(&b_info->avail_vcpus, 0);
-    } else if (b_info->avail_vcpus.size > HVM_MAX_VCPUS) {
-        LOG(ERROR, "avail_vcpus bitmap contains too many VCPUS");
-        return ERROR_FAIL;
-    }
-
-    /* In libxl internals, we want to deal with vcpu_hard_affinity only! */
-    if (b_info->cpumap.size && !b_info->num_vcpu_hard_affinity) {
-        b_info->vcpu_hard_affinity = libxl__calloc(gc, b_info->max_vcpus,
-                                                   sizeof(libxl_bitmap));
-        for (i = 0; i < b_info->max_vcpus; i++) {
-            if (libxl_cpu_bitmap_alloc(CTX, &b_info->vcpu_hard_affinity[i], 0)) {
-                LOG(ERROR, "failed to allocate vcpu hard affinity bitmap");
-                return ERROR_FAIL;
-            }
-            libxl_bitmap_copy(CTX, &b_info->vcpu_hard_affinity[i],
-                              &b_info->cpumap);
-        }
-        b_info->num_vcpu_hard_affinity = b_info->max_vcpus;
-    }
-
-    libxl_defbool_setdefault(&b_info->numa_placement, true);
-
-    if (b_info->max_memkb == LIBXL_MEMKB_DEFAULT)
-        b_info->max_memkb = 32 * 1024;
-    if (b_info->target_memkb == LIBXL_MEMKB_DEFAULT)
-        b_info->target_memkb = b_info->max_memkb;
-
-    if (b_info->stubdomain_memkb == LIBXL_MEMKB_DEFAULT) {
-        if (libxl_defbool_val(b_info->device_model_stubdomain)) {
-            if (libxl__stubdomain_is_linux(b_info))
-                b_info->stubdomain_memkb = LIBXL_LINUX_STUBDOM_MEM * 1024;
-            else
-                b_info->stubdomain_memkb = 28 * 1024; // MiniOS
-        } else {
-            b_info->stubdomain_memkb = 0; // no stubdomain
-        }
-    }
-
-    libxl_defbool_setdefault(&b_info->claim_mode, false);
-
-    libxl_defbool_setdefault(&b_info->localtime, false);
-
-    libxl_defbool_setdefault(&b_info->disable_migrate, false);
-
-    for (i = 0 ; i < b_info->num_iomem; i++)
-        if (b_info->iomem[i].gfn == LIBXL_INVALID_GFN)
-            b_info->iomem[i].gfn = b_info->iomem[i].start;
-
-    if (!b_info->event_channels)
-        b_info->event_channels = 1023;
-
-    libxl__arch_domain_build_info_setdefault(gc, b_info);
-    libxl_defbool_setdefault(&b_info->dm_restrict, false);
-
-    if (b_info->iommu_memkb == LIBXL_MEMKB_DEFAULT)
-        /* Normally defaulted in libxl__domain_create_info_setdefault */
-        b_info->iommu_memkb = 0;
-
-    switch (b_info->type) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
-            /* Normally defaulted in libxl__domain_create_info_setdefault */
-            b_info->shadow_memkb = 0;
-        if (b_info->u.hvm.mmio_hole_memkb == LIBXL_MEMKB_DEFAULT)
-            b_info->u.hvm.mmio_hole_memkb = 0;
-
-        if (b_info->u.hvm.vga.kind == LIBXL_VGA_INTERFACE_TYPE_UNKNOWN) {
-            b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
-        }
-
-        if (!b_info->u.hvm.hdtype)
-            b_info->u.hvm.hdtype = LIBXL_HDTYPE_IDE;
-
-        switch (b_info->device_model_version) {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            switch (b_info->u.hvm.vga.kind) {
-            case LIBXL_VGA_INTERFACE_TYPE_NONE:
-                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
-                    b_info->video_memkb = 0;
-                break;
-            case LIBXL_VGA_INTERFACE_TYPE_QXL:
-                LOG(ERROR,"qemu upstream required for qxl vga");
-                return ERROR_INVAL;
-                break;
-            case LIBXL_VGA_INTERFACE_TYPE_STD:
-                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
-                    b_info->video_memkb = 8 * 1024;
-                if (b_info->video_memkb < 8 * 1024) {
-                    LOG(ERROR, "videoram must be at least 8 MB for STDVGA on QEMU_XEN_TRADITIONAL");
-                    return ERROR_INVAL;
-                }
-                break;
-            case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
-            default:
-                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
-                    b_info->video_memkb = 4 * 1024;
-                if (b_info->video_memkb != 4 * 1024)
-                    LOG(WARN, "ignoring videoram other than 4 MB for CIRRUS on QEMU_XEN_TRADITIONAL");
-                break;
-            }
-            break;
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-        default:
-            switch (b_info->u.hvm.vga.kind) {
-            case LIBXL_VGA_INTERFACE_TYPE_NONE:
-                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
-                    b_info->video_memkb = 0;
-                break;
-            case LIBXL_VGA_INTERFACE_TYPE_QXL:
-                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) {
-                    b_info->video_memkb = (128 * 1024);
-                } else if (b_info->video_memkb < (128 * 1024)) {
-                    LOG(ERROR,
-                        "128 Mib videoram is the minimum for qxl default");
-                    return ERROR_INVAL;
-                }
-                break;
-            case LIBXL_VGA_INTERFACE_TYPE_STD:
-                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
-                    b_info->video_memkb = 16 * 1024;
-                if (b_info->video_memkb < 16 * 1024) {
-                    LOG(ERROR, "videoram must be at least 16 MB for STDVGA on QEMU_XEN");
-                    return ERROR_INVAL;
-                }
-                break;
-            case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
-            default:
-                if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
-                    b_info->video_memkb = 8 * 1024;
-                if (b_info->video_memkb < 8 * 1024) {
-                    LOG(ERROR, "videoram must be at least 8 MB for CIRRUS on QEMU_XEN");
-                    return ERROR_INVAL;
-                }
-                break;
-            }
-            break;
-        }
-
-        libxl_defbool_setdefault(&b_info->u.hvm.pae,                true);
-        libxl_defbool_setdefault(&b_info->u.hvm.acpi,               true);
-        libxl_defbool_setdefault(&b_info->u.hvm.acpi_s3,            true);
-        libxl_defbool_setdefault(&b_info->u.hvm.acpi_s4,            true);
-        libxl_defbool_setdefault(&b_info->u.hvm.acpi_laptop_slate,  false);
-        libxl_defbool_setdefault(&b_info->u.hvm.nx,                 true);
-        libxl_defbool_setdefault(&b_info->u.hvm.viridian,           false);
-        libxl_defbool_setdefault(&b_info->u.hvm.hpet,               true);
-        libxl_defbool_setdefault(&b_info->u.hvm.vpt_align,          true);
-        libxl_defbool_setdefault(&b_info->u.hvm.altp2m,             false);
-        libxl_defbool_setdefault(&b_info->u.hvm.usb,                false);
-        libxl_defbool_setdefault(&b_info->u.hvm.vkb_device,         true);
-        libxl_defbool_setdefault(&b_info->u.hvm.xen_platform_pci,   true);
-
-        libxl_defbool_setdefault(&b_info->u.hvm.spice.enable, false);
-        if (!libxl_defbool_val(b_info->u.hvm.spice.enable) &&
-            (b_info->u.hvm.spice.usbredirection > 0) ){
-            b_info->u.hvm.spice.usbredirection = 0;
-            LOG(WARN, "spice disabled, disabling usbredirection");
-        }
-
-        if (!b_info->u.hvm.usbversion &&
-            (b_info->u.hvm.spice.usbredirection > 0) )
-            b_info->u.hvm.usbversion = 2;
-
-        if ((b_info->u.hvm.usbversion || b_info->u.hvm.spice.usbredirection) &&
-            ( libxl_defbool_val(b_info->u.hvm.usb)
-            || b_info->u.hvm.usbdevice_list
-            || b_info->u.hvm.usbdevice) ){
-            LOG(ERROR,"usbversion and/or usbredirection cannot be "
-            "enabled with usb and/or usbdevice parameters.");
-            return ERROR_INVAL;
-        }
-
-        if (!b_info->u.hvm.boot)
-            b_info->u.hvm.boot = libxl__strdup(NOGC, "cda");
-
-        libxl_defbool_setdefault(&b_info->u.hvm.vnc.enable, true);
-        if (libxl_defbool_val(b_info->u.hvm.vnc.enable)) {
-            libxl_defbool_setdefault(&b_info->u.hvm.vnc.findunused, true);
-            if (!b_info->u.hvm.vnc.listen)
-                b_info->u.hvm.vnc.listen = libxl__strdup(NOGC, "127.0.0.1");
-        }
-
-        libxl_defbool_setdefault(&b_info->u.hvm.sdl.enable, false);
-        if (libxl_defbool_val(b_info->u.hvm.sdl.enable)) {
-            libxl_defbool_setdefault(&b_info->u.hvm.sdl.opengl, false);
-        }
-
-        if (libxl_defbool_val(b_info->u.hvm.spice.enable)) {
-            libxl_defbool_setdefault(&b_info->u.hvm.spice.disable_ticketing,
-                                     false);
-            libxl_defbool_setdefault(&b_info->u.hvm.spice.agent_mouse, true);
-            libxl_defbool_setdefault(&b_info->u.hvm.spice.vdagent, false);
-            libxl_defbool_setdefault(&b_info->u.hvm.spice.clipboard_sharing,
-                                     false);
-        }
-
-        libxl_defbool_setdefault(&b_info->u.hvm.nographic, false);
-
-        libxl_defbool_setdefault(&b_info->u.hvm.gfx_passthru, false);
-
-        libxl__rdm_setdefault(gc, b_info);
-        break;
-    case LIBXL_DOMAIN_TYPE_PV:
-        libxl_defbool_setdefault(&b_info->u.pv.e820_host, false);
-        if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
-            b_info->video_memkb = 0;
-        if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
-            /* Normally defaulted in libxl__domain_create_info_setdefault */
-            b_info->shadow_memkb = 0;
-        if (b_info->u.pv.slack_memkb == LIBXL_MEMKB_DEFAULT)
-            b_info->u.pv.slack_memkb = 0;
-        break;
-    case LIBXL_DOMAIN_TYPE_PVH:
-        libxl_defbool_setdefault(&b_info->u.pvh.pvshim, false);
-        if (libxl_defbool_val(b_info->u.pvh.pvshim)) {
-            if (!b_info->u.pvh.pvshim_path)
-                b_info->u.pvh.pvshim_path =
-                    libxl__sprintf(NOGC, "%s/%s",
-                                   libxl__xenfirmwaredir_path(),
-                                   PVSHIM_BASENAME);
-            if (!b_info->u.pvh.pvshim_cmdline)
-                b_info->u.pvh.pvshim_cmdline =
-                    libxl__strdup(NOGC, PVSHIM_CMDLINE);
-        }
-
-        break;
-    default:
-        LOG(ERROR, "invalid domain type %s in create info",
-            libxl_domain_type_to_string(b_info->type));
-        return ERROR_INVAL;
-    }
-
-    /* Configuration fields shared between PVH and HVM. */
-    if (b_info->type != LIBXL_DOMAIN_TYPE_PV) {
-        if (libxl__timer_mode_is_default(&b_info->timer_mode))
-            b_info->timer_mode = LIBXL_TIMER_MODE_NO_DELAY_FOR_MISSED_TICKS;
-
-        libxl_defbool_setdefault(&b_info->apic,                     true);
-        libxl_defbool_setdefault(&b_info->nested_hvm,               false);
-    }
-
-    return 0;
-}
-
-static void init_console_info(libxl__gc *gc,
-                             libxl__device_console *console,
-                             int dev_num)
-{
-    libxl__device_console_init(console);
-    console->devid = dev_num;
-    console->consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
-    console->output = libxl__strdup(NOGC, "pty");
-    /* console->{name,connection,path} are NULL on normal consoles.
-       Only 'channels' when mapped to consoles have a string name. */
-}
-
-int libxl__domain_build(libxl__gc *gc,
-                        libxl_domain_config *d_config,
-                        uint32_t domid,
-                        libxl__domain_build_state *state)
-{
-    libxl_domain_build_info *const info = &d_config->b_info;
-    char **vments = NULL, **localents = NULL;
-    struct timeval start_time;
-    int i, ret;
-
-    ret = libxl__build_pre(gc, domid, d_config, state);
-    if (ret)
-        goto out;
-
-    gettimeofday(&start_time, NULL);
-
-    switch (info->type) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        ret = libxl__build_hvm(gc, domid, d_config, state);
-        if (ret)
-            goto out;
-
-        vments = libxl__calloc(gc, 7, sizeof(char *));
-        vments[0] = "rtc/timeoffset";
-        vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
-        vments[2] = "image/ostype";
-        vments[3] = "hvm";
-        vments[4] = "start_time";
-        vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
-
-        localents = libxl__calloc(gc, 13, sizeof(char *));
-        i = 0;
-        localents[i++] = "platform/acpi";
-        localents[i++] = libxl__acpi_defbool_val(info) ? "1" : "0";
-        localents[i++] = "platform/acpi_s3";
-        localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s3) ? "1" : "0";
-        localents[i++] = "platform/acpi_s4";
-        localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s4) ? "1" : "0";
-        localents[i++] = "platform/acpi_laptop_slate";
-        localents[i++] = libxl_defbool_val(info->u.hvm.acpi_laptop_slate) ? "1" : "0";
-        if (info->u.hvm.mmio_hole_memkb) {
-            uint64_t max_ram_below_4g =
-                (1ULL << 32) - (info->u.hvm.mmio_hole_memkb << 10);
-
-            if (max_ram_below_4g <= HVM_BELOW_4G_MMIO_START) {
-                localents[i++] = "platform/mmio_hole_size";
-                localents[i++] =
-                    GCSPRINTF("%"PRIu64,
-                                   info->u.hvm.mmio_hole_memkb << 10);
-            }
-        }
-        localents[i++] = "platform/device-model";
-        localents[i++] = (char *)libxl_device_model_version_to_string(info->device_model_version);
-
-        break;
-    case LIBXL_DOMAIN_TYPE_PV:
-        ret = libxl__build_pv(gc, domid, d_config, state);
-        if (ret)
-            goto out;
-
-        vments = libxl__calloc(gc, 11, sizeof(char *));
-        i = 0;
-        vments[i++] = "image/ostype";
-        vments[i++] = "linux";
-        vments[i++] = "image/kernel";
-        vments[i++] = (char *) state->pv_kernel.path;
-        vments[i++] = "start_time";
-        vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
-        if (state->pv_ramdisk.path) {
-            vments[i++] = "image/ramdisk";
-            vments[i++] = (char *) state->pv_ramdisk.path;
-        }
-        if (state->pv_cmdline) {
-            vments[i++] = "image/cmdline";
-            vments[i++] = (char *) state->pv_cmdline;
-        }
-
-        break;
-    case LIBXL_DOMAIN_TYPE_PVH:
-        state->shim_path = info->u.pvh.pvshim_path;
-        state->shim_cmdline = GCSPRINTF("%s%s%s",
-                    info->u.pvh.pvshim_cmdline,
-                    info->u.pvh.pvshim_extra ? " " : "",
-                    info->u.pvh.pvshim_extra ? info->u.pvh.pvshim_extra : "");
-
-        ret = libxl__build_hvm(gc, domid, d_config, state);
-        if (ret)
-            goto out;
-
-        vments = libxl__calloc(gc, 3, sizeof(char *));
-        vments[0] = "start_time";
-        vments[1] = GCSPRINTF("%"PRIu64".%02ld",
-                              (uint64_t)start_time.tv_sec,
-                              (long)start_time.tv_usec/10000);
-
-        break;
-    default:
-        ret = ERROR_INVAL;
-        goto out;
-    }
-    ret = libxl__build_post(gc, domid, info, state, vments, localents);
-out:
-    return ret;
-}
-
-int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
-                       libxl__domain_build_state *state,
-                       uint32_t *domid, bool soft_reset)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int ret, rc, nb_vm;
-    const char *dom_type;
-    char *uuid_string;
-    char *dom_path, *vm_path, *libxl_path;
-    struct xs_permissions roperm[2];
-    struct xs_permissions rwperm[1];
-    struct xs_permissions noperm[1];
-    xs_transaction_t t = 0;
-    libxl_vminfo *vm_list;
-
-    /* convenience aliases */
-    libxl_domain_create_info *info = &d_config->c_info;
-    libxl_domain_build_info *b_info = &d_config->b_info;
-
-    assert(soft_reset || *domid == INVALID_DOMID);
-
-    uuid_string = libxl__uuid2string(gc, info->uuid);
-    if (!uuid_string) {
-        rc = ERROR_NOMEM;
-        goto out;
-    }
-
-    if (!soft_reset) {
-        struct xen_domctl_createdomain create = {
-            .ssidref = info->ssidref,
-            .max_vcpus = b_info->max_vcpus,
-            .max_evtchn_port = b_info->event_channels,
-            .max_grant_frames = b_info->max_grant_frames,
-            .max_maptrack_frames = b_info->max_maptrack_frames,
-        };
-
-        if (info->type != LIBXL_DOMAIN_TYPE_PV) {
-            create.flags |= XEN_DOMCTL_CDF_hvm;
-            create.flags |=
-                libxl_defbool_val(info->hap) ? XEN_DOMCTL_CDF_hap : 0;
-            create.flags |=
-                libxl_defbool_val(info->oos) ? 0 : XEN_DOMCTL_CDF_oos_off;
-        }
-
-        assert(info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
-        LOG(DETAIL, "passthrough: %s",
-            libxl_passthrough_to_string(info->passthrough));
-
-        if (info->passthrough != LIBXL_PASSTHROUGH_DISABLED)
-            create.flags |= XEN_DOMCTL_CDF_iommu;
-
-        if (info->passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
-            create.iommu_opts |= XEN_DOMCTL_IOMMU_no_sharept;
-
-        /* Ultimately, handle is an array of 16 uint8_t, same as uuid */
-        libxl_uuid_copy(ctx, (libxl_uuid *)&create.handle, &info->uuid);
-
-        ret = libxl__arch_domain_prepare_config(gc, d_config, &create);
-        if (ret < 0) {
-            LOGED(ERROR, *domid, "fail to get domain config");
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        for (;;) {
-            uint32_t local_domid;
-            bool recent;
-
-            if (info->domid == RANDOM_DOMID) {
-                uint16_t v;
-
-                ret = libxl__random_bytes(gc, (void *)&v, sizeof(v));
-                if (ret < 0)
-                    break;
-
-                v &= DOMID_MASK;
-                if (!libxl_domid_valid_guest(v))
-                    continue;
-
-                local_domid = v;
-            } else {
-                local_domid = info->domid; /* May not be valid */
-            }
-
-            ret = xc_domain_create(ctx->xch, &local_domid, &create);
-            if (ret < 0) {
-                /*
-                 * If we generated a random domid and creation failed
-                 * because that domid already exists then simply try
-                 * again.
-                 */
-                if (errno == EEXIST && info->domid == RANDOM_DOMID)
-                    continue;
-
-                LOGED(ERROR, local_domid, "domain creation fail");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-
-            /* A new domain now exists */
-            *domid = local_domid;
-
-            rc = libxl__is_domid_recent(gc, local_domid, &recent);
-            if (rc)
-                goto out;
-
-            /* The domid is not recent, so we're done */
-            if (!recent)
-                break;
-
-            /*
-             * If the domid was specified then there's no point in
-             * trying again.
-             */
-            if (libxl_domid_valid_guest(info->domid)) {
-                LOGED(ERROR, local_domid, "domain id recently used");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-
-            /*
-             * The domain is recent and so cannot be used. Clear domid
-             * here since, if xc_domain_destroy() fails below there is
-             * little point calling it again in the error path.
-             */
-            *domid = INVALID_DOMID;
-
-            ret = xc_domain_destroy(ctx->xch, local_domid);
-            if (ret < 0) {
-                LOGED(ERROR, local_domid, "domain destroy fail");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-
-            /* The domain was successfully destroyed, so we can try again */
-        }
-
-        rc = libxl__arch_domain_save_config(gc, d_config, state, &create);
-        if (rc < 0)
-            goto out;
-    }
-
-    /*
-     * If soft_reset is set the the domid will have been valid on entry.
-     * If it was not set then xc_domain_create() should have assigned a
-     * valid value. Either way, if we reach this point, domid should be
-     * valid.
-     */
-    assert(libxl_domid_valid_guest(*domid));
-
-    ret = xc_cpupool_movedomain(ctx->xch, info->poolid, *domid);
-    if (ret < 0) {
-        LOGED(ERROR, *domid, "domain move fail");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    dom_path = libxl__xs_get_dompath(gc, *domid);
-    if (!dom_path) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    vm_path = GCSPRINTF("/vm/%s", uuid_string);
-    if (!vm_path) {
-        LOGD(ERROR, *domid, "cannot allocate create paths");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl_path = libxl__xs_libxl_path(gc, *domid);
-    if (!libxl_path) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    noperm[0].id = 0;
-    noperm[0].perms = XS_PERM_NONE;
-
-    roperm[0].id = 0;
-    roperm[0].perms = XS_PERM_NONE;
-    roperm[1].id = *domid;
-    roperm[1].perms = XS_PERM_READ;
-
-    rwperm[0].id = *domid;
-    rwperm[0].perms = XS_PERM_NONE;
-
-retry_transaction:
-    t = xs_transaction_start(ctx->xsh);
-
-    xs_rm(ctx->xsh, t, dom_path);
-    libxl__xs_mknod(gc, t, dom_path, roperm, ARRAY_SIZE(roperm));
-
-    xs_rm(ctx->xsh, t, vm_path);
-    libxl__xs_mknod(gc, t, vm_path, roperm, ARRAY_SIZE(roperm));
-
-    xs_rm(ctx->xsh, t, libxl_path);
-    libxl__xs_mknod(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm));
-    libxl__xs_mknod(gc, t, GCSPRINTF("%s/device", libxl_path),
-                    noperm, ARRAY_SIZE(noperm));
-
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
-    rc = libxl__domain_rename(gc, *domid, 0, info->name, t);
-    if (rc)
-        goto out;
-
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/cpu", dom_path),
-                    roperm, ARRAY_SIZE(roperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/memory", dom_path),
-                    roperm, ARRAY_SIZE(roperm));
-
-    if (!libxl_defbool_val(info->xend_suspend_evtchn_compat)) {
-        libxl__xs_mknod(gc, t,
-                        GCSPRINTF("%s/device", dom_path),
-                        roperm, ARRAY_SIZE(roperm));
-        libxl__xs_mknod(gc, t,
-                        GCSPRINTF("%s/device/suspend/event-channel",
-                                  dom_path),
-                        rwperm, ARRAY_SIZE(rwperm));
-    } else {
-        libxl__xs_mknod(gc, t,
-                        GCSPRINTF("%s/device", dom_path),
-                        rwperm, ARRAY_SIZE(rwperm));
-    }
-
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/control", dom_path),
-                    roperm, ARRAY_SIZE(roperm));
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM)
-        libxl__xs_mknod(gc, t,
-                        GCSPRINTF("%s/hvmloader", dom_path),
-                        roperm, ARRAY_SIZE(roperm));
-
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/control/shutdown", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/control/feature-poweroff", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/control/feature-reboot", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/control/feature-suspend", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        libxl__xs_mknod(gc, t,
-                        GCSPRINTF("%s/control/feature-s3", dom_path),
-                        rwperm, ARRAY_SIZE(rwperm));
-        libxl__xs_mknod(gc, t,
-                        GCSPRINTF("%s/control/feature-s4", dom_path),
-                        rwperm, ARRAY_SIZE(rwperm));
-    }
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/control/sysrq", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/data", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/drivers", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/feature", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/attr", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-    libxl__xs_mknod(gc, t,
-                    GCSPRINTF("%s/error", dom_path),
-                    rwperm, ARRAY_SIZE(rwperm));
-
-    if (libxl_defbool_val(info->driver_domain)) {
-        /*
-         * Create a local "libxl" directory for each guest, since we might want
-         * to use libxl from inside the guest
-         */
-        libxl__xs_mknod(gc, t, GCSPRINTF("%s/libxl", dom_path), rwperm,
-                        ARRAY_SIZE(rwperm));
-        /*
-         * Create a local "device-model" directory for each guest, since we
-         * might want to use Qemu from inside the guest
-         */
-        libxl__xs_mknod(gc, t, GCSPRINTF("%s/device-model", dom_path), rwperm,
-                        ARRAY_SIZE(rwperm));
-    }
-
-    vm_list = libxl_list_vm(ctx, &nb_vm);
-    if (!vm_list) {
-        LOGD(ERROR, *domid, "cannot get number of running guests");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    libxl_vminfo_list_free(vm_list, nb_vm);
-
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/name", vm_path), info->name, strlen(info->name));
-
-    libxl__xs_writev(gc, t, dom_path, info->xsdata);
-    libxl__xs_writev(gc, t, GCSPRINTF("%s/platform", dom_path), info->platformdata);
-
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1);
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-xs_reset_watches", dom_path), "1", 1);
-
-    dom_type = libxl_domain_type_to_string(info->type);
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/type", libxl_path), dom_type,
-             strlen(dom_type));
-
-    if (!xs_transaction_end(ctx->xsh, t, 0)) {
-        if (errno == EAGAIN) {
-            t = 0;
-            goto retry_transaction;
-        }
-        LOGED(ERROR, *domid, "domain creation ""xenstore transaction commit failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    t = 0;
-
-    rc = 0;
- out:
-    if (t) xs_transaction_end(ctx->xsh, t, 1);
-    return rc;
-}
-
-static int store_libxl_entry(libxl__gc *gc, uint32_t domid,
-                             libxl_domain_build_info *b_info)
-{
-    char *path = NULL;
-
-    path = libxl__xs_libxl_path(gc, domid);
-    path = GCSPRINTF("%s/dm-version", path);
-    return libxl__xs_printf(gc, XBT_NULL, path, "%s",
-                            libxl_device_model_version_to_string(b_info->device_model_version));
-}
-
-void libxl__domain_build_state_init(libxl__domain_build_state *state)
-{
-    state->dm_monitor_fd = -1;
-}
-
-void libxl__domain_build_state_dispose(libxl__domain_build_state *state)
-{
-    libxl__file_reference_unmap(&state->pv_kernel);
-    libxl__file_reference_unmap(&state->pv_ramdisk);
-    if (state->dm_monitor_fd >= 0) {
-        close(state->dm_monitor_fd);
-        state->dm_monitor_fd = -1;
-    }
-}
-
-/*----- main domain creation -----*/
-
-/* We have a linear control flow; only one event callback is
- * outstanding at any time.  Each initiation and callback function
- * arranges for the next to be called, as the very last thing it
- * does.  (If that particular sub-operation is not needed, a
- * function will call the next event callback directly.)
- */
-
-/* Event callbacks, in this order: */
-static void domcreate_bootloader_console_available(libxl__egc *egc,
-                                                   libxl__bootloader_state *bl);
-static void domcreate_console_available(libxl__egc *egc,
-                                        libxl__domain_create_state *dcs);
-
-static void domcreate_bootloader_done(libxl__egc *egc,
-                                      libxl__bootloader_state *bl,
-                                      int rc);
-static void libxl__colo_restore_setup_done(libxl__egc *egc,
-                                           libxl__colo_restore_state *crs,
-                                           int rc);
-static void domcreate_stream_done(libxl__egc *egc,
-                                  libxl__stream_read_state *srs,
-                                  int ret);
-static void domcreate_rebuild_done(libxl__egc *egc,
-                                   libxl__domain_create_state *dcs,
-                                   int ret);
-static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs,
-                                int ret);
-static void domcreate_devmodel_started(libxl__egc *egc,
-                                       libxl__dm_spawn_state *dmss,
-                                       int rc);
-static void domcreate_attach_devices(libxl__egc *egc,
-                                     libxl__multidev *multidev,
-                                     int ret);
-static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
-                                    int rc, const char *p);
-
-/* Our own function to clean up and call the user's callback.
- * The final call in the sequence. */
-static void domcreate_complete(libxl__egc *egc,
-                               libxl__domain_create_state *dcs,
-                               int rc);
-
-/* If creation is not successful, this callback will be executed
- * when domain destruction is finished */
-static void domcreate_destruction_cb(libxl__egc *egc,
-                                     libxl__domain_destroy_state *dds,
-                                     int rc);
-
-static bool ok_to_default_memkb_in_create(libxl__gc *gc)
-{
-    /*
-     * This is a fudge.  We are trying to find whether the caller
-     * calls the old version of libxl_domain_need_memory.  If they do
-     * then, because it only gets the b_info, and because it can't
-     * update the b_info (because it's const), it will base its
-     * calculations on defaulting shadow_memkb and iommu_memkb to 0
-     * In that case we probably shouldn't default them differently
-     * during libxl_domain_create.
-     *
-     * The result is that the behaviour with old callers is the same
-     * as in 4.13: no additional memory is allocated for shadow and
-     * iommu (unless the caller set shadow_memkb, eg from a call to
-     * libxl_get_required_shadow_memory).
-     */
-    return !CTX->libxl_domain_need_memory_0x041200_called ||
-            CTX->libxl_domain_need_memory_called;
-    /*
-     * Treat mixed callers as new callers.  Presumably they know what
-     * they are doing.
-     */
-}
-
-static unsigned long libxl__get_required_iommu_memory(unsigned long maxmem_kb)
-{
-    unsigned long iommu_pages = 0, mem_pages = maxmem_kb / 4;
-    unsigned int level;
-
-    /* Assume a 4 level page table with 512 entries per level */
-    for (level = 0; level < 4; level++)
-    {
-        mem_pages = DIV_ROUNDUP(mem_pages, 512);
-        iommu_pages += mem_pages;
-    }
-
-    return iommu_pages * 4;
-}
-
-int libxl__domain_config_setdefault(libxl__gc *gc,
-                                    libxl_domain_config *d_config,
-                                    uint32_t domid /* for logging, only */)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int ret;
-    bool pod_enabled = false;
-    libxl_domain_create_info *c_info = &d_config->c_info;
-
-    libxl_physinfo physinfo;
-    ret = libxl_get_physinfo(CTX, &physinfo);
-    if (ret) goto error_out;
-
-    if (d_config->c_info.ssid_label) {
-        char *s = d_config->c_info.ssid_label;
-        ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
-                                         &d_config->c_info.ssidref);
-        if (ret) {
-            if (errno == ENOSYS) {
-                LOGD(WARN, domid, "XSM Disabled: init_seclabel not supported");
-                ret = 0;
-            } else {
-                LOGD(ERROR, domid, "Invalid init_seclabel: %s", s);
-                goto error_out;
-            }
-        }
-    }
-
-    if (d_config->b_info.exec_ssid_label) {
-        char *s = d_config->b_info.exec_ssid_label;
-        ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
-                                         &d_config->b_info.exec_ssidref);
-        if (ret) {
-            if (errno == ENOSYS) {
-                LOGD(WARN, domid, "XSM Disabled: seclabel not supported");
-                ret = 0;
-            } else {
-                LOGD(ERROR, domid, "Invalid seclabel: %s", s);
-                goto error_out;
-            }
-        }
-    }
-
-    if (d_config->b_info.device_model_ssid_label) {
-        char *s = d_config->b_info.device_model_ssid_label;
-        ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
-                                         &d_config->b_info.device_model_ssidref);
-        if (ret) {
-            if (errno == ENOSYS) {
-                LOGD(WARN, domid,
-                     "XSM Disabled: device_model_stubdomain_seclabel not supported");
-                ret = 0;
-            } else {
-                LOGD(ERROR, domid, "Invalid device_model_stubdomain_seclabel: %s", s);
-                goto error_out;
-            }
-        }
-    }
-
-    if (d_config->c_info.pool_name) {
-        d_config->c_info.poolid = -1;
-        libxl_cpupool_qualifier_to_cpupoolid(ctx, d_config->c_info.pool_name,
-                                             &d_config->c_info.poolid,
-                                             NULL);
-    }
-    if (!libxl_cpupoolid_is_valid(ctx, d_config->c_info.poolid)) {
-        LOGD(ERROR, domid, "Illegal pool specified: %s",
-             d_config->c_info.pool_name);
-        ret = ERROR_INVAL;
-        goto error_out;
-    }
-
-    ret = libxl__domain_create_info_setdefault(gc, &d_config->c_info,
-                                               &physinfo);
-    if (ret) {
-        LOGD(ERROR, domid, "Unable to set domain create info defaults");
-        goto error_out;
-    }
-
-    bool need_pt = d_config->num_pcidevs || d_config->num_dtdevs;
-    if (c_info->passthrough == LIBXL_PASSTHROUGH_DEFAULT) {
-        c_info->passthrough = need_pt
-            ? LIBXL_PASSTHROUGH_ENABLED : LIBXL_PASSTHROUGH_DISABLED;
-    }
-
-    bool iommu_enabled = physinfo.cap_hvm_directio;
-    if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED && !iommu_enabled) {
-        LOGD(ERROR, domid,
-             "passthrough not supported on this platform\n");
-        ret = ERROR_INVAL;
-        goto error_out;
-    }
-
-    if (c_info->passthrough == LIBXL_PASSTHROUGH_DISABLED && need_pt) {
-        LOGD(ERROR, domid,
-             "passthrough disabled but devices are specified");
-        ret = ERROR_INVAL;
-        goto error_out;
-    }
-
-    ret = libxl__arch_passthrough_mode_setdefault(gc,domid,d_config,&physinfo);
-    if (ret) goto error_out;
-
-    /* An explicit setting should now have been chosen */
-    assert(c_info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
-    assert(c_info->passthrough != LIBXL_PASSTHROUGH_ENABLED);
-
-    /* If target_memkb is smaller than max_memkb, the subsequent call
-     * to libxc when building HVM domain will enable PoD mode.
-     */
-    pod_enabled = (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV) &&
-        (d_config->b_info.target_memkb < d_config->b_info.max_memkb);
-
-    /* We cannot have PoD and PCI device assignment at the same time
-     * for HVM guest. It was reported that IOMMU cannot work with PoD
-     * enabled because it needs to populated entire page table for
-     * guest. To stay on the safe side, we disable PCI device
-     * assignment when PoD is enabled.
-     */
-    if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
-        d_config->num_pcidevs && pod_enabled) {
-        ret = ERROR_INVAL;
-        LOGD(ERROR, domid,
-             "PCI device assignment for HVM guest failed due to PoD enabled");
-        goto error_out;
-    }
-
-    /* Disallow PoD and vNUMA to be enabled at the same time because PoD
-     * pool is not vNUMA-aware yet.
-     */
-    if (pod_enabled && d_config->b_info.num_vnuma_nodes) {
-        ret = ERROR_INVAL;
-        LOGD(ERROR, domid, "Cannot enable PoD and vNUMA at the same time");
-        goto error_out;
-    }
-
-    /* PV vNUMA is not yet supported because there is an issue with
-     * cpuid handling.
-     */
-    if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
-        d_config->b_info.num_vnuma_nodes) {
-        ret = ERROR_INVAL;
-        LOGD(ERROR, domid, "PV vNUMA is not yet supported");
-        goto error_out;
-    }
-
-    if (d_config->b_info.shadow_memkb == LIBXL_MEMKB_DEFAULT
-        && ok_to_default_memkb_in_create(gc))
-        d_config->b_info.shadow_memkb =
-            libxl_get_required_shadow_memory(d_config->b_info.max_memkb,
-                                             d_config->b_info.max_vcpus);
-
-    /* No IOMMU reservation is needed if passthrough mode is not 'sync_pt' */
-    if (d_config->b_info.iommu_memkb == LIBXL_MEMKB_DEFAULT
-        && ok_to_default_memkb_in_create(gc))
-        d_config->b_info.iommu_memkb =
-            (d_config->c_info.passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
-            ? libxl__get_required_iommu_memory(d_config->b_info.max_memkb)
-            : 0;
-
-    ret = libxl__domain_build_info_setdefault(gc, &d_config->b_info);
-    if (ret) {
-        LOGD(ERROR, domid, "Unable to set domain build info defaults");
-        goto error_out;
-    }
-
-    if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
-        (libxl_defbool_val(d_config->b_info.nested_hvm) &&
-        ((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
-          libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
-        (d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)))) {
-        ret = ERROR_INVAL;
-        LOGD(ERROR, domid, "nestedhvm and altp2mhvm cannot be used together");
-        goto error_out;
-    }
-
-    if (((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
-         libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
-        (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
-         d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)) &&
-        pod_enabled) {
-        ret = ERROR_INVAL;
-        LOGD(ERROR, domid, "Cannot enable PoD and ALTP2M at the same time");
-        goto error_out;
-    }
-
-    ret = 0;
- error_out:
-    return ret;
-}
-
-static void initiate_domain_create(libxl__egc *egc,
-                                   libxl__domain_create_state *dcs)
-{
-    STATE_AO_GC(dcs->ao);
-    uint32_t domid;
-    int i, ret;
-
-    /* convenience aliases */
-    libxl_domain_config *const d_config = dcs->guest_config;
-    libxl__domain_build_state *dbs = &dcs->build_state;
-
-    libxl__xswait_init(&dcs->console_xswait);
-
-    domid = dcs->domid;
-    libxl__domain_build_state_init(dbs);
-    dbs->restore = dcs->restore_fd >= 0;
-
-    ret = libxl__domain_config_setdefault(gc,d_config,domid);
-    if (ret) goto error_out;
-
-    ret = libxl__domain_make(gc, d_config, dbs, &domid, dcs->soft_reset);
-    if (ret) {
-        LOGD(ERROR, domid, "cannot make domain: %d", ret);
-        dcs->guest_domid = domid;
-        ret = ERROR_FAIL;
-        goto error_out;
-    }
-
-    dcs->guest_domid = domid;
-    dcs->sdss.dm.guest_domid = 0; /* means we haven't spawned */
-
-    /* post-4.13 todo: move these next bits of defaulting to
-     * libxl__domain_config_setdefault */
-
-    /*
-     * Set the dm version quite early so that libxl doesn't have to pass the
-     * build info around just to know if the domain has a device model or not.
-     */
-    store_libxl_entry(gc, domid, &d_config->b_info);
-
-    for (i = 0; i < d_config->num_disks; i++) {
-        ret = libxl__disk_devtype.set_default(gc, domid, &d_config->disks[i],
-                                              false);
-        if (ret) {
-            LOGD(ERROR, domid, "Unable to set disk defaults for disk %d", i);
-            goto error_out;
-        }
-    }
-
-    dcs->bl.ao = ao;
-    libxl_device_disk *bootdisk =
-        d_config->num_disks > 0 ? &d_config->disks[0] : NULL;
-
-    /*
-     * The devid has to be set before launching the device model. For the
-     * hotplug case this is done in libxl_device_nic_add but on domain
-     * creation this is called too late.
-     * Make two runs over configured NICs in order to avoid duplicate IDs
-     * in case the caller partially assigned IDs.
-     */
-    ret = libxl__device_nic_set_devids(gc, d_config, domid);
-    if (ret)
-        goto error_out;
-
-    if (dbs->restore || dcs->soft_reset) {
-        LOGD(DEBUG, domid, "restoring, not running bootloader");
-        domcreate_bootloader_done(egc, &dcs->bl, 0);
-    } else  {
-        LOGD(DEBUG, domid, "running bootloader");
-        dcs->bl.callback = domcreate_bootloader_done;
-        dcs->bl.console_available = domcreate_bootloader_console_available;
-        dcs->bl.info = &d_config->b_info;
-        dcs->bl.disk = bootdisk;
-        dcs->bl.domid = dcs->guest_domid;
-
-        dcs->bl.kernel = &dbs->pv_kernel;
-        dcs->bl.ramdisk = &dbs->pv_ramdisk;
-
-        libxl__bootloader_run(egc, &dcs->bl);
-    }
-    return;
-
-error_out:
-    assert(ret);
-    domcreate_complete(egc, dcs, ret);
-}
-
-static void domcreate_bootloader_console_available(libxl__egc *egc,
-                                                   libxl__bootloader_state *bl)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
-    STATE_AO_GC(bl->ao);
-    domcreate_console_available(egc, dcs);
-}
-
-static void domcreate_console_available(libxl__egc *egc,
-                                        libxl__domain_create_state *dcs) {
-    libxl__ao_progress_report(egc, dcs->ao, &dcs->aop_console_how,
-                              NEW_EVENT(egc, DOMAIN_CREATE_CONSOLE_AVAILABLE,
-                                        dcs->guest_domid,
-                                        dcs->aop_console_how.for_event));
-}
-
-static void domcreate_bootloader_done(libxl__egc *egc,
-                                      libxl__bootloader_state *bl,
-                                      int rc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
-    STATE_AO_GC(bl->ao);
-
-    /* convenience aliases */
-    const uint32_t domid = dcs->guest_domid;
-    libxl_domain_config *const d_config = dcs->guest_config;
-    const int restore_fd = dcs->restore_fd;
-    libxl__domain_build_state *const state = &dcs->build_state;
-    const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
-    libxl__colo_restore_state *const crs = &dcs->crs;
-    libxl_domain_build_info *const info = &d_config->b_info;
-    libxl__srm_restore_autogen_callbacks *const callbacks =
-        &dcs->srs.shs.callbacks.restore.a;
-
-    if (rc) {
-        domcreate_rebuild_done(egc, dcs, rc);
-        return;
-    }
-
-    /* consume bootloader outputs. state->pv_{kernel,ramdisk} have
-     * been initialised by the bootloader already.
-     */
-    state->pv_cmdline = bl->cmdline;
-
-    /* We might be going to call libxl__spawn_local_dm, or _spawn_stub_dm.
-     * Fill in any field required by either, including both relevant
-     * callbacks (_spawn_stub_dm will overwrite our trespass if needed). */
-    dcs->sdss.dm.spawn.ao = ao;
-    dcs->sdss.dm.guest_config = dcs->guest_config;
-    dcs->sdss.dm.build_state = &dcs->build_state;
-    dcs->sdss.dm.callback = domcreate_devmodel_started;
-    dcs->sdss.callback = domcreate_devmodel_started;
-
-    if (restore_fd < 0 && !dcs->soft_reset) {
-        rc = libxl__domain_build(gc, d_config, domid, state);
-        domcreate_rebuild_done(egc, dcs, rc);
-        return;
-    }
-
-    /* Prepare environment for domcreate_stream_done */
-    dcs->srs.dcs = dcs;
-
-    /* Restore */
-    callbacks->static_data_done = libxl__srm_callout_callback_static_data_done;
-    callbacks->restore_results = libxl__srm_callout_callback_restore_results;
-
-    /* COLO only supports HVM now because it does not work very
-     * well with pv drivers:
-     * 1. We need to resume vm in the slow path. In this case we
-     *    need to disconnect/reconnect backend and frontend. It
-     *    will take too much time and the performance is very slow.
-     * 2. PV disk cannot reuse block replication that is implemented
-     *    in QEMU.
-     */
-    if (info->type != LIBXL_DOMAIN_TYPE_HVM &&
-        checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
-        LOGD(ERROR, domid, "COLO only supports HVM, unable to restore domain");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__build_pre(gc, domid, d_config, state);
-    if (rc)
-        goto out;
-
-    dcs->srs.ao = ao;
-    dcs->srs.fd = restore_fd;
-    dcs->srs.legacy = (dcs->restore_params.stream_version == 1);
-    dcs->srs.back_channel = false;
-    dcs->srs.completion_callback = domcreate_stream_done;
-
-    if (restore_fd >= 0) {
-        switch (checkpointed_stream) {
-        case LIBXL_CHECKPOINTED_STREAM_COLO:
-            /* colo restore setup */
-            crs->ao = ao;
-            crs->domid = domid;
-            crs->send_back_fd = dcs->send_back_fd;
-            crs->recv_fd = restore_fd;
-            crs->hvm = (info->type != LIBXL_DOMAIN_TYPE_PV);
-            crs->callback = libxl__colo_restore_setup_done;
-            libxl__colo_restore_setup(egc, crs);
-            break;
-        case LIBXL_CHECKPOINTED_STREAM_REMUS:
-            libxl__remus_restore_setup(egc, dcs);
-            /* fall through */
-        case LIBXL_CHECKPOINTED_STREAM_NONE:
-            libxl__stream_read_start(egc, &dcs->srs);
-        }
-        return;
-    }
-
- out:
-    domcreate_stream_done(egc, &dcs->srs, rc);
-}
-
-static void libxl__colo_restore_setup_done(libxl__egc *egc,
-                                           libxl__colo_restore_state *crs,
-                                           int rc)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
-
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, dcs->guest_domid, "colo restore setup fails: %d", rc);
-        domcreate_stream_done(egc, &dcs->srs, rc);
-        return;
-    }
-
-    libxl__stream_read_start(egc, &dcs->srs);
-}
-
-int libxl__srm_callout_callback_static_data_done(unsigned int missing,
-                                                 void *user)
-{
-    libxl__save_helper_state *shs = user;
-    libxl__domain_create_state *dcs = shs->caller_state;
-    STATE_AO_GC(dcs->ao);
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-
-    libxl_domain_config *d_config = dcs->guest_config;
-    libxl_domain_build_info *info = &d_config->b_info;
-
-    /*
-     * CPUID/MSR information is not present in pre Xen-4.14 streams.
-     *
-     * Libxl used to always regenerate the CPUID policy from first principles
-     * on migrate.  Continue to do so for backwards compatibility when the
-     * stream doesn't contain any CPUID data.
-     */
-    if (missing & XGR_SDD_MISSING_CPUID)
-        libxl__cpuid_legacy(ctx, dcs->guest_domid, true, info);
-
-    return 0;
-}
-
-void libxl__srm_callout_callback_restore_results(xen_pfn_t store_mfn,
-          xen_pfn_t console_mfn, void *user)
-{
-    libxl__save_helper_state *shs = user;
-    libxl__domain_create_state *dcs = shs->caller_state;
-    STATE_AO_GC(dcs->ao);
-    libxl__domain_build_state *const state = &dcs->build_state;
-
-    state->store_mfn =            store_mfn;
-    state->console_mfn =          console_mfn;
-    shs->need_results =           0;
-}
-
-static void domcreate_stream_done(libxl__egc *egc,
-                                  libxl__stream_read_state *srs,
-                                  int ret)
-{
-    /* NB perhaps only srs->dcs is valid; eg in the case of an
-     * early branch to domcreate_bootloader_done's `out' block */
-    libxl__domain_create_state *dcs = srs->dcs;
-    STATE_AO_GC(dcs->ao);
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char **vments = NULL, **localents = NULL;
-    struct timeval start_time;
-    int i, esave;
-
-    /* convenience aliases */
-    const uint32_t domid = dcs->guest_domid;
-    libxl_domain_config *const d_config = dcs->guest_config;
-    libxl_domain_build_info *const info = &d_config->b_info;
-    libxl__domain_build_state *const state = &dcs->build_state;
-    const int fd = dcs->restore_fd;
-
-    if (ret)
-        goto out;
-
-    gettimeofday(&start_time, NULL);
-
-    switch (info->type) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        vments = libxl__calloc(gc, 7, sizeof(char *));
-        vments[0] = "rtc/timeoffset";
-        vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
-        vments[2] = "image/ostype";
-        vments[3] = "hvm";
-        vments[4] = "start_time";
-        vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
-        break;
-    case LIBXL_DOMAIN_TYPE_PV:
-        vments = libxl__calloc(gc, 11, sizeof(char *));
-        i = 0;
-        vments[i++] = "image/ostype";
-        vments[i++] = "linux";
-        vments[i++] = "image/kernel";
-        vments[i++] = (char *) state->pv_kernel.path;
-        vments[i++] = "start_time";
-        vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000);
-        if (state->pv_ramdisk.path) {
-            vments[i++] = "image/ramdisk";
-            vments[i++] = (char *) state->pv_ramdisk.path;
-        }
-        if (state->pv_cmdline) {
-            vments[i++] = "image/cmdline";
-            vments[i++] = (char *) state->pv_cmdline;
-        }
-        break;
-    case LIBXL_DOMAIN_TYPE_PVH:
-        vments = libxl__calloc(gc, 3, sizeof(char *));
-        vments[0] = "start_time";
-        vments[1] = GCSPRINTF("%"PRIu64".%02ld",
-                              (uint64_t)start_time.tv_sec,
-                              (long)start_time.tv_usec/10000);
-        break;
-    default:
-        ret = ERROR_INVAL;
-        goto out;
-    }
-
-    /*
-     * The scheduler on the sending domain may be different than the
-     * scheduler running here.  Setting the scheduler to UNKNOWN will
-     * cause the code to take to take whatever parameters are
-     * available in that scheduler, while discarding the rest.
-     */
-    info->sched_params.sched = LIBXL_SCHEDULER_UNKNOWN;
-
-    ret = libxl__build_post(gc, domid, info, state, vments, localents);
-    if (ret)
-        goto out;
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        state->saved_state = GCSPRINTF(
-                       LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
-    }
-
-out:
-    if (info->type == LIBXL_DOMAIN_TYPE_PV) {
-        libxl__file_reference_unmap(&state->pv_kernel);
-        libxl__file_reference_unmap(&state->pv_ramdisk);
-    }
-
-    /* fd == -1 here means we're doing soft reset. */
-    if (fd != -1) {
-        esave = errno;
-        libxl_fd_set_nonblock(ctx, fd, 0);
-        errno = esave;
-    }
-    domcreate_rebuild_done(egc, dcs, ret);
-}
-
-static void domcreate_rebuild_done(libxl__egc *egc,
-                                   libxl__domain_create_state *dcs,
-                                   int ret)
-{
-    STATE_AO_GC(dcs->ao);
-
-    /* convenience aliases */
-    const uint32_t domid = dcs->guest_domid;
-    libxl_domain_config *const d_config = dcs->guest_config;
-
-    if (ret) {
-        LOGD(ERROR, domid, "cannot (re-)build domain: %d", ret);
-        ret = ERROR_FAIL;
-        goto error_out;
-    }
-
-    store_libxl_entry(gc, domid, &d_config->b_info);
-
-    libxl__multidev_begin(ao, &dcs->multidev);
-    dcs->multidev.callback = domcreate_launch_dm;
-    libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev);
-    libxl__multidev_prepared(egc, &dcs->multidev, 0);
-
-    return;
-
- error_out:
-    assert(ret);
-    domcreate_complete(egc, dcs, ret);
-}
-
-static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
-                                int ret)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
-    STATE_AO_GC(dcs->ao);
-    int i;
-
-    /* convenience aliases */
-    const uint32_t domid = dcs->guest_domid;
-    libxl_domain_config *const d_config = dcs->guest_config;
-    libxl__domain_build_state *const state = &dcs->build_state;
-
-    if (ret) {
-        LOGD(ERROR, domid, "unable to add disk devices");
-        goto error_out;
-    }
-
-    for (i = 0; i < d_config->b_info.num_ioports; i++) {
-        libxl_ioport_range *io = &d_config->b_info.ioports[i];
-
-        LOGD(DEBUG, domid, "ioports %"PRIx32"-%"PRIx32,
-             io->first, io->first + io->number - 1);
-
-        ret = xc_domain_ioport_permission(CTX->xch, domid,
-                                          io->first, io->number, 1);
-        if (ret < 0) {
-            LOGED(ERROR, domid,
-                  "failed give domain access to ioports %"PRIx32"-%"PRIx32,
-                  io->first, io->first + io->number - 1);
-            ret = ERROR_FAIL;
-            goto error_out;
-        }
-    }
-
-    for (i = 0; i < d_config->b_info.num_irqs; i++) {
-        int irq = d_config->b_info.irqs[i];
-
-        LOGD(DEBUG, domid, "irq %d", irq);
-
-        ret = irq >= 0 ? libxl__arch_domain_map_irq(gc, domid, irq)
-                       : -EOVERFLOW;
-        if (ret) {
-            LOGED(ERROR, domid, "failed give domain access to irq %d", irq);
-            ret = ERROR_FAIL;
-            goto error_out;
-        }
-    }
-
-    for (i = 0; i < d_config->b_info.num_iomem; i++) {
-        libxl_iomem_range *io = &d_config->b_info.iomem[i];
-
-        LOGD(DEBUG, domid, "iomem %"PRIx64"-%"PRIx64,
-             io->start, io->start + io->number - 1);
-
-        ret = xc_domain_iomem_permission(CTX->xch, domid,
-                                          io->start, io->number, 1);
-        if (ret < 0) {
-            LOGED(ERROR, domid,
-                  "failed give domain access to iomem range %"PRIx64"-%"PRIx64,
-                  io->start, io->start + io->number - 1);
-            ret = ERROR_FAIL;
-            goto error_out;
-        }
-        ret = xc_domain_memory_mapping(CTX->xch, domid,
-                                       io->gfn, io->start,
-                                       io->number, 1);
-        if (ret < 0) {
-            LOGED(ERROR, domid,
-                  "failed to map to domain iomem range %"PRIx64"-%"PRIx64
-                  " to guest address %"PRIx64,
-                  io->start, io->start + io->number - 1, io->gfn);
-            ret = ERROR_FAIL;
-            goto error_out;
-        }
-    }
-
-    /* For both HVM and PV the 0th console is a regular console. We
-       map channels to IOEMU consoles starting at 1 */
-    for (i = 0; i < d_config->num_channels; i++) {
-        libxl__device_console console;
-        libxl__device device;
-        ret = libxl__init_console_from_channel(gc, &console, i + 1,
-                                               &d_config->channels[i]);
-        if ( ret ) {
-            libxl__device_console_dispose(&console);
-            goto error_out;
-        }
-        libxl__device_console_add(gc, domid, &console, NULL, &device);
-        libxl__device_console_dispose(&console);
-    }
-
-    for (i = 0; i < d_config->num_p9s; i++)
-        libxl__device_add(gc, domid, &libxl__p9_devtype, &d_config->p9s[i]);
-
-    for (i = 0; i < d_config->num_pvcallsifs; i++)
-        libxl__device_add(gc, domid, &libxl__pvcallsif_devtype,
-                          &d_config->pvcallsifs[i]);
-
-    switch (d_config->c_info.type) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-    {
-        libxl__device_console console;
-        libxl__device device;
-        libxl_device_vkb vkb;
-
-        init_console_info(gc, &console, 0);
-        console.backend_domid = state->console_domid;
-        libxl__device_console_add(gc, domid, &console, state, &device);
-        libxl__device_console_dispose(&console);
-
-        if (libxl_defbool_val(d_config->b_info.u.hvm.vkb_device)) {
-            libxl_device_vkb_init(&vkb);
-            libxl__device_add(gc, domid, &libxl__vkb_devtype, &vkb);
-            libxl_device_vkb_dispose(&vkb);
-        }
-
-        dcs->sdss.dm.guest_domid = domid;
-        if (libxl_defbool_val(d_config->b_info.device_model_stubdomain))
-            libxl__spawn_stub_dm(egc, &dcs->sdss);
-        else
-            libxl__spawn_local_dm(egc, &dcs->sdss.dm);
-
-        /*
-         * Handle the domain's (and the related stubdomain's) access to
-         * the VGA framebuffer.
-         */
-        ret = libxl__grant_vga_iomem_permission(gc, domid, d_config);
-        if ( ret )
-            goto error_out;
-
-        return;
-    }
-    case LIBXL_DOMAIN_TYPE_PV:
-    case LIBXL_DOMAIN_TYPE_PVH:
-    {
-        libxl__device_console console, vuart;
-        libxl__device device;
-
-        for (i = 0; i < d_config->num_vfbs; i++) {
-            libxl__device_add(gc, domid, &libxl__vfb_devtype,
-                              &d_config->vfbs[i]);
-        }
-
-        for (i = 0; i < d_config->num_vkbs; i++) {
-            libxl__device_add(gc, domid, &libxl__vkb_devtype,
-                              &d_config->vkbs[i]);
-        }
-
-        if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
-            init_console_info(gc, &vuart, 0);
-            vuart.backend_domid = state->console_domid;
-            libxl__device_vuart_add(gc, domid, &vuart, state);
-            libxl__device_console_dispose(&vuart);
-        }
-
-        init_console_info(gc, &console, 0);
-        console.backend_domid = state->console_domid;
-        libxl__device_console_add(gc, domid, &console, state, &device);
-        libxl__device_console_dispose(&console);
-
-        ret = libxl__need_xenpv_qemu(gc, d_config);
-        if (ret < 0)
-            goto error_out;
-        if (ret) {
-            dcs->sdss.dm.guest_domid = domid;
-            libxl__spawn_local_dm(egc, &dcs->sdss.dm);
-            return;
-        } else {
-            assert(!dcs->sdss.dm.guest_domid);
-            domcreate_devmodel_started(egc, &dcs->sdss.dm, 0);
-            return;
-        }
-    }
-    default:
-        ret = ERROR_INVAL;
-        goto error_out;
-    }
-    abort(); /* not reached */
-
- error_out:
-    assert(ret);
-    domcreate_complete(egc, dcs, ret);
-}
-
-static void libxl__add_dtdevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
-                              libxl_domain_config *d_config,
-                              libxl__multidev *multidev)
-{
-    AO_GC;
-    libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
-    int i, rc = 0;
-
-    for (i = 0; i < d_config->num_dtdevs; i++) {
-        const libxl_device_dtdev *dtdev = &d_config->dtdevs[i];
-
-        LOGD(DEBUG, domid, "Assign device \"%s\" to domain", dtdev->path);
-        rc = xc_assign_dt_device(CTX->xch, domid, dtdev->path);
-        if (rc < 0) {
-            LOGD(ERROR, domid, "xc_assign_dtdevice failed: %d", rc);
-            goto out;
-        }
-    }
-
-out:
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-#define libxl_device_dtdev_list NULL
-#define libxl_device_dtdev_compare NULL
-#define libxl__device_from_dtdev NULL
-#define libxl__device_dtdev_setdefault NULL
-#define libxl__device_dtdev_update_devid NULL
-static DEFINE_DEVICE_TYPE_STRUCT(dtdev, NONE);
-
-const libxl__device_type *device_type_tbl[] = {
-    &libxl__disk_devtype,
-    &libxl__nic_devtype,
-    &libxl__vtpm_devtype,
-    &libxl__usbctrl_devtype,
-    &libxl__usbdev_devtype,
-    &libxl__pcidev_devtype,
-    &libxl__dtdev_devtype,
-    &libxl__vdispl_devtype,
-    &libxl__vsnd_devtype,
-    NULL
-};
-
-static void domcreate_devmodel_started(libxl__egc *egc,
-                                       libxl__dm_spawn_state *dmss,
-                                       int ret)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(dmss, *dcs, sdss.dm);
-    STATE_AO_GC(dmss->spawn.ao);
-    int domid = dcs->guest_domid;
-
-    if (ret) {
-        LOGD(ERROR, domid, "device model did not start: %d", ret);
-        goto error_out;
-    }
-
-    dcs->device_type_idx = -1;
-    domcreate_attach_devices(egc, &dcs->multidev, 0);
-    return;
-
-error_out:
-    assert(ret);
-    domcreate_complete(egc, dcs, ret);
-}
-
-static void domcreate_attach_devices(libxl__egc *egc,
-                                     libxl__multidev *multidev,
-                                     int ret)
-{
-    libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
-    STATE_AO_GC(dcs->ao);
-    int domid = dcs->guest_domid;
-    libxl_domain_config *const d_config = dcs->guest_config;
-    const libxl__device_type *dt;
-    char *tty_path;
-
-    if (ret) {
-        LOGD(ERROR, domid, "unable to add %s devices",
-             libxl__device_kind_to_string(device_type_tbl[dcs->device_type_idx]->type));
-        goto error_out;
-    }
-
-    dcs->device_type_idx++;
-    dt = device_type_tbl[dcs->device_type_idx];
-    if (dt) {
-        if (*libxl__device_type_get_num(dt, d_config) > 0 && !dt->skip_attach) {
-            /* Attach devices */
-            libxl__multidev_begin(ao, &dcs->multidev);
-            dcs->multidev.callback = domcreate_attach_devices;
-            dt->add(egc, ao, domid, d_config, &dcs->multidev);
-            libxl__multidev_prepared(egc, &dcs->multidev, 0);
-            return;
-        }
-
-        domcreate_attach_devices(egc, &dcs->multidev, 0);
-        return;
-    }
-
-    ret = libxl__console_tty_path(gc, domid, 0, LIBXL_CONSOLE_TYPE_PV, &tty_path);
-    if (ret) {
-        LOG(ERROR, "failed to get domain %d console tty path",
-            domid);
-        goto error_out;
-    }
-
-    dcs->console_xswait.ao = ao;
-    dcs->console_xswait.what = GCSPRINTF("domain %d console tty", domid);
-    dcs->console_xswait.path = tty_path;
-    dcs->console_xswait.timeout_ms = LIBXL_INIT_TIMEOUT * 1000;
-    dcs->console_xswait.callback = console_xswait_callback;
-    ret = libxl__xswait_start(gc, &dcs->console_xswait);
-    if (ret) {
-        LOG(ERROR, "unable to set up watch for domain %d console tty path",
-            domid);
-        goto error_out;
-    }
-
-    return;
-
-error_out:
-    assert(ret);
-    domcreate_complete(egc, dcs, ret);
-}
-
-static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
-                                    int rc, const char *p)
-{
-    EGC_GC;
-    libxl__domain_create_state *dcs = CONTAINER_OF(xswa, *dcs, console_xswait);
-
-    if (rc) {
-        if (rc == ERROR_TIMEDOUT)
-            LOG(ERROR, "%s: timed out", xswa->what);
-        goto out;
-    }
-
-    if (p && p[0] != '\0') {
-        domcreate_console_available(egc, dcs);
-        goto out;
-    }
-
-    return;
-
-out:
-    libxl__xswait_stop(gc, xswa);
-    domcreate_complete(egc, dcs, rc);
-}
-
-static void domcreate_complete(libxl__egc *egc,
-                               libxl__domain_create_state *dcs,
-                               int rc)
-{
-    STATE_AO_GC(dcs->ao);
-    libxl_domain_config *const d_config = dcs->guest_config;
-    libxl_domain_config *d_config_saved = &dcs->guest_config_saved;
-
-    libxl__xswait_stop(gc, &dcs->console_xswait);
-
-    libxl__domain_build_state_dispose(&dcs->build_state);
-
-    if (!rc && d_config->b_info.exec_ssidref)
-        rc = xc_flask_relabel_domain(CTX->xch, dcs->guest_domid, d_config->b_info.exec_ssidref);
-
-    bool retain_domain = !rc || rc == ERROR_ABORTED;
-
-    if (retain_domain) {
-        libxl__flock *lock;
-
-        /* Note that we hold CTX lock at this point so only need to
-         * take data store lock
-         */
-        lock = libxl__lock_domain_userdata(gc, dcs->guest_domid);
-        if (!lock) {
-            rc = ERROR_LOCK_FAIL;
-        } else {
-            libxl__update_domain_configuration(gc, d_config_saved, d_config);
-            int cfg_rc = libxl__set_domain_configuration
-                (gc, dcs->guest_domid, d_config_saved);
-            if (!rc)
-                rc = cfg_rc;
-            libxl__unlock_file(lock);
-        }
-    }
-
-    libxl_domain_config_dispose(d_config_saved);
-
-    if (!retain_domain) {
-        if (dcs->guest_domid > 0) {
-            dcs->dds.ao = ao;
-            dcs->dds.domid = dcs->guest_domid;
-            dcs->dds.callback = domcreate_destruction_cb;
-            libxl__domain_destroy(egc, &dcs->dds);
-            return;
-        }
-        dcs->guest_domid = INVALID_DOMID;
-    }
-    dcs->callback(egc, dcs, rc, dcs->guest_domid);
-}
-
-static void domcreate_destruction_cb(libxl__egc *egc,
-                                     libxl__domain_destroy_state *dds,
-                                     int rc)
-{
-    STATE_AO_GC(dds->ao);
-    libxl__domain_create_state *dcs = CONTAINER_OF(dds, *dcs, dds);
-
-    if (rc)
-        LOGD(ERROR, dds->domid, "unable to destroy domain following failed creation");
-
-    dcs->callback(egc, dcs, ERROR_FAIL, dcs->guest_domid);
-}
-
-/*----- application-facing domain creation interface -----*/
-
-typedef struct {
-    libxl__domain_create_state dcs;
-    uint32_t *domid_out;
-} libxl__app_domain_create_state;
-
-typedef struct {
-    libxl__app_domain_create_state cdcs;
-    libxl__domain_destroy_state dds;
-    libxl__domain_save_state dss;
-    char *toolstack_buf;
-    uint32_t toolstack_len;
-} libxl__domain_soft_reset_state;
-
-static void domain_create_cb(libxl__egc *egc,
-                             libxl__domain_create_state *dcs,
-                             int rc, uint32_t domid);
-
-static int do_domain_create(libxl_ctx *ctx, libxl_domain_config *d_config,
-                            uint32_t *domid, int restore_fd, int send_back_fd,
-                            const libxl_domain_restore_params *params,
-                            const libxl_asyncop_how *ao_how,
-                            const libxl_asyncprogress_how *aop_console_how)
-{
-    AO_CREATE(ctx, 0, ao_how);
-    libxl__app_domain_create_state *cdcs;
-    int rc;
-
-    GCNEW(cdcs);
-    cdcs->dcs.ao = ao;
-    cdcs->dcs.guest_config = d_config;
-    libxl_domain_config_init(&cdcs->dcs.guest_config_saved);
-    libxl_domain_config_copy(ctx, &cdcs->dcs.guest_config_saved, d_config);
-    cdcs->dcs.restore_fd = cdcs->dcs.libxc_fd = restore_fd;
-    cdcs->dcs.send_back_fd = send_back_fd;
-    if (restore_fd >= 0) {
-        cdcs->dcs.restore_params = *params;
-        rc = libxl__fd_flags_modify_save(gc, cdcs->dcs.restore_fd,
-                                         ~(O_NONBLOCK|O_NDELAY), 0,
-                                         &cdcs->dcs.restore_fdfl);
-        if (rc < 0) goto out_err;
-    }
-    cdcs->dcs.callback = domain_create_cb;
-    cdcs->dcs.domid = INVALID_DOMID;
-    cdcs->dcs.soft_reset = false;
-
-    if (cdcs->dcs.restore_params.checkpointed_stream ==
-        LIBXL_CHECKPOINTED_STREAM_COLO) {
-        cdcs->dcs.colo_proxy_script =
-            cdcs->dcs.restore_params.colo_proxy_script;
-        cdcs->dcs.crs.cps.is_userspace_proxy =
-            libxl_defbool_val(cdcs->dcs.restore_params.userspace_colo_proxy);
-    } else {
-        cdcs->dcs.colo_proxy_script = NULL;
-        cdcs->dcs.crs.cps.is_userspace_proxy = false;
-    }
-
-    libxl__ao_progress_gethow(&cdcs->dcs.aop_console_how, aop_console_how);
-    cdcs->domid_out = domid;
-
-    initiate_domain_create(egc, &cdcs->dcs);
-
-    return AO_INPROGRESS;
-
- out_err:
-    return AO_CREATE_FAIL(rc);
-
-}
-
-static void domain_soft_reset_cb(libxl__egc *egc,
-                                 libxl__domain_destroy_state *dds,
-                                 int rc)
-{
-    STATE_AO_GC(dds->ao);
-    libxl__domain_soft_reset_state *srs = CONTAINER_OF(dds, *srs, dds);
-    libxl__app_domain_create_state *cdcs = &srs->cdcs;
-    char *savefile, *restorefile;
-
-    if (rc) {
-        LOGD(ERROR, dds->domid, "destruction of domain failed.");
-        goto error;
-    }
-
-    cdcs->dcs.guest_domid = dds->domid;
-    rc = libxl__restore_emulator_xenstore_data(&cdcs->dcs, srs->toolstack_buf,
-                                               srs->toolstack_len);
-    if (rc) {
-        LOGD(ERROR, dds->domid, "failed to restore toolstack record.");
-        goto error;
-    }
-
-    if (cdcs->dcs.guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
-        savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", dds->domid);
-        restorefile = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d",
-                                dds->domid);
-        rc = rename(savefile, restorefile);
-        if (rc) {
-            LOGD(ERROR, dds->domid, "failed to rename dm save file.");
-            goto error;
-        }
-    }
-
-    initiate_domain_create(egc, &cdcs->dcs);
-    return;
-
-error:
-    domcreate_complete(egc, &cdcs->dcs, rc);
-}
-
-static void soft_reset_dm_suspended(libxl__egc *egc,
-                                    libxl__domain_suspend_state *dsps,
-                                    int rc);
-static int do_domain_soft_reset(libxl_ctx *ctx,
-                                libxl_domain_config *d_config,
-                                uint32_t domid,
-                                const libxl_asyncop_how *ao_how,
-                                const libxl_asyncprogress_how
-                                *aop_console_how)
-{
-    AO_CREATE(ctx, 0, ao_how);
-    libxl__domain_soft_reset_state *srs;
-    libxl__app_domain_create_state *cdcs;
-    libxl__domain_create_state *dcs;
-    libxl__domain_build_state *state;
-    libxl__domain_save_state *dss;
-    const char *console_tty, *xs_store_mfn, *xs_console_mfn;
-    char *dom_path;
-    uint32_t domid_out;
-    int rc;
-
-    GCNEW(srs);
-    cdcs = &srs->cdcs;
-    dcs = &cdcs->dcs;
-    state = &dcs->build_state;
-    dss = &srs->dss;
-
-    srs->cdcs.dcs.ao = ao;
-    srs->cdcs.dcs.guest_config = d_config;
-    libxl_domain_config_init(&srs->cdcs.dcs.guest_config_saved);
-    libxl_domain_config_copy(ctx, &srs->cdcs.dcs.guest_config_saved,
-                             d_config);
-    cdcs->dcs.restore_fd = -1;
-    cdcs->dcs.domid = domid;
-    cdcs->dcs.soft_reset = true;
-    cdcs->dcs.callback = domain_create_cb;
-    libxl__ao_progress_gethow(&srs->cdcs.dcs.aop_console_how,
-                              aop_console_how);
-    cdcs->domid_out = &domid_out;
-
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path) {
-        LOGD(ERROR, domid, "failed to read domain path");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/store/ring-ref", dom_path),
-                                &xs_store_mfn);
-    if (rc) {
-        LOGD(ERROR, domid, "failed to read store/ring-ref.");
-        goto out;
-    }
-    state->store_mfn = xs_store_mfn ? atol(xs_store_mfn): 0;
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/console/ring-ref", dom_path),
-                                &xs_console_mfn);
-    if (rc) {
-        LOGD(ERROR, domid, "failed to read console/ring-ref.");
-        goto out;
-    }
-    state->console_mfn = xs_console_mfn ? atol(xs_console_mfn): 0;
-
-    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
-                                  GCSPRINTF("%s/console/tty", dom_path),
-                                  &console_tty);
-    if (rc) {
-        LOGD(ERROR, domid, "failed to read console/tty.");
-        goto out;
-    }
-    state->console_tty = libxl__strdup(gc, console_tty);
-
-    dss->ao = ao;
-    dss->domid = dss->dsps.domid = domid;
-    dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d",
-                                      domid);
-
-    rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf,
-                                            &srs->toolstack_len);
-    if (rc) {
-        LOGD(ERROR, domid, "failed to save toolstack record.");
-        goto out;
-    }
-
-    dss->dsps.ao = ao;
-    dss->dsps.callback_device_model_done = soft_reset_dm_suspended;
-    libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */
-
-    return AO_INPROGRESS;
-
- out:
-    return AO_CREATE_FAIL(rc);
-}
-
-static void soft_reset_dm_suspended(libxl__egc *egc,
-                                    libxl__domain_suspend_state *dsps,
-                                    int rc)
-{
-    STATE_AO_GC(dsps->ao);
-    libxl__domain_soft_reset_state *srs =
-        CONTAINER_OF(dsps, *srs, dss.dsps);
-    libxl__app_domain_create_state *cdcs = &srs->cdcs;
-
-    /*
-     * Ask all backends to disconnect by removing the domain from
-     * xenstore. On the creation path the domain will be introduced to
-     * xenstore again with probably different store/console/...
-     * channels.
-     */
-    xs_release_domain(CTX->xsh, cdcs->dcs.domid);
-
-    srs->dds.ao = ao;
-    srs->dds.domid = cdcs->dcs.domid;
-    srs->dds.callback = domain_soft_reset_cb;
-    srs->dds.soft_reset = true;
-    libxl__domain_destroy(egc, &srs->dds);
-}
-
-static void domain_create_cb(libxl__egc *egc,
-                             libxl__domain_create_state *dcs,
-                             int rc, uint32_t domid)
-{
-    libxl__app_domain_create_state *cdcs = CONTAINER_OF(dcs, *cdcs, dcs);
-    int flrc;
-    STATE_AO_GC(cdcs->dcs.ao);
-
-    *cdcs->domid_out = domid;
-
-    if (dcs->restore_fd >= 0) {
-        flrc = libxl__fd_flags_restore(gc,
-                dcs->restore_fd, dcs->restore_fdfl);
-        /*
-         * If restore has failed already then report that error not
-         * this one.
-         */
-        if (flrc && !rc) rc = flrc;
-    }
-
-    libxl__ao_complete(egc, ao, rc);
-}
-
-
-static void set_disk_colo_restore(libxl_domain_config *d_config)
-{
-    int i;
-
-    for (i = 0; i < d_config->num_disks; i++)
-        libxl_defbool_set(&d_config->disks[i].colo_restore_enable, true);
-}
-
-static void unset_disk_colo_restore(libxl_domain_config *d_config)
-{
-    int i;
-
-    for (i = 0; i < d_config->num_disks; i++)
-        libxl_defbool_set(&d_config->disks[i].colo_restore_enable, false);
-}
-
-int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
-                            uint32_t *domid,
-                            const libxl_asyncop_how *ao_how,
-                            const libxl_asyncprogress_how *aop_console_how)
-{
-    unset_disk_colo_restore(d_config);
-    return do_domain_create(ctx, d_config, domid, -1, -1, NULL,
-                            ao_how, aop_console_how);
-}
-
-int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
-                                uint32_t *domid, int restore_fd,
-                                int send_back_fd,
-                                const libxl_domain_restore_params *params,
-                                const libxl_asyncop_how *ao_how,
-                                const libxl_asyncprogress_how *aop_console_how)
-{
-    if (params->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
-        set_disk_colo_restore(d_config);
-    } else {
-        unset_disk_colo_restore(d_config);
-    }
-
-    return do_domain_create(ctx, d_config, domid, restore_fd, send_back_fd,
-                            params, ao_how, aop_console_how);
-}
-
-int libxl_domain_soft_reset(libxl_ctx *ctx,
-                            libxl_domain_config *d_config,
-                            uint32_t domid,
-                            const libxl_asyncop_how *ao_how,
-                            const libxl_asyncprogress_how
-                            *aop_console_how)
-{
-    libxl_domain_build_info *const info = &d_config->b_info;
-
-    if (info->type != LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL;
-
-    return do_domain_soft_reset(ctx, d_config, domid, ao_how,
-                                aop_console_how);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c
deleted file mode 100644 (file)
index e081faf..0000000
+++ /dev/null
@@ -1,2090 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-static char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device)
-{
-    char *dom_path = libxl__xs_get_dompath(gc, device->domid);
-
-    /* Console 0 is a special case */
-    if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0)
-        return GCSPRINTF("%s/%s", dom_path,
-                         libxl__device_kind_to_string(device->kind));
-
-    if (device->kind == LIBXL__DEVICE_KIND_VUART)
-        return GCSPRINTF("%s/%s/%d", dom_path,
-                         libxl__device_kind_to_string(device->kind),
-                         device->devid);
-
-    return GCSPRINTF("%s/device/%s/%d", dom_path,
-                     libxl__device_kind_to_string(device->kind),
-                     device->devid);
-}
-
-char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
-                                         libxl__device_kind device_kind)
-{
-    char *dom_path = libxl__xs_get_dompath(gc, domid);
-
-    return GCSPRINTF("%s/device/%s/%d", dom_path,
-                     libxl__device_kind_to_string(device_kind), devid);
-}
-
-char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device)
-{
-    char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid);
-
-    return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
-                     libxl__device_kind_to_string(device->backend_kind),
-                     device->domid, device->devid);
-}
-
-char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
-                                        uint32_t domid, uint32_t devid,
-                                        libxl__device_kind backend_kind)
-{
-    char *dom_path = libxl__xs_get_dompath(gc, backend_domid);
-
-    return GCSPRINTF("%s/backend/%s/%u/%d", dom_path,
-                     libxl__device_kind_to_string(backend_kind),
-                     domid, devid);
-}
-
-char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device)
-{
-    char *libxl_dom_path = libxl__xs_libxl_path(gc, device->domid);
-
-    return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
-                     libxl__device_kind_to_string(device->kind),
-                     device->devid);
-}
-
-char *libxl__domain_device_libxl_path(libxl__gc *gc,  uint32_t domid, uint32_t devid,
-                                      libxl__device_kind device_kind)
-{
-    char *libxl_dom_path = libxl__xs_libxl_path(gc, domid);
-
-    return GCSPRINTF("%s/device/%s/%d", libxl_dom_path,
-                     libxl__device_kind_to_string(device_kind), devid);
-}
-
-/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
-int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
-                         libxl__device *device)
-{
-    int rc;
-    char *be_path = libxl__device_libxl_path(gc, device);
-    const char *dir;
-
-    rc = libxl__xs_read_checked(gc, t, be_path, &dir);
-
-    if (rc)
-        return rc;
-
-    if (dir)
-        return 1;
-    return 0;
-}
-
-int libxl__parse_backend_path(libxl__gc *gc,
-                              const char *path,
-                              libxl__device *dev)
-{
-    /* /local/domain/<domid>/backend/<kind>/<domid>/<devid> */
-    char strkind[16]; /* Longest is actually "console" */
-    int rc = sscanf(path, "/local/domain/%d/backend/%15[^/]/%u/%d",
-                    &dev->backend_domid,
-                    strkind,
-                    &dev->domid,
-                    &dev->devid);
-
-    if (rc != 4)
-        return ERROR_FAIL;
-
-    return libxl__device_kind_from_string(strkind, &dev->backend_kind);
-}
-
-int libxl__nic_type(libxl__gc *gc, libxl__device *dev, libxl_nic_type *nictype)
-{
-    char *snictype, *be_path;
-    int rc = 0;
-
-    be_path = libxl__device_backend_path(gc, dev);
-    snictype = libxl__xs_read(gc, XBT_NULL,
-                              GCSPRINTF("%s/%s", be_path, "type"));
-    if (!snictype) {
-        LOGED(ERROR, dev->domid, "unable to read nictype from %s", be_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = libxl_nic_type_from_string(snictype, nictype);
-    if (rc) {
-        LOGED(ERROR, dev->domid, "unable to parse nictype from %s", be_path);
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
-        libxl__device *device, char **bents, char **fents, char **ro_fents)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *frontend_path = NULL, *backend_path = NULL, *libxl_path;
-    struct xs_permissions frontend_perms[2];
-    struct xs_permissions ro_frontend_perms[2];
-    struct xs_permissions backend_perms[2];
-    int create_transaction = t == XBT_NULL;
-    int libxl_only = device->backend_kind == LIBXL__DEVICE_KIND_NONE;
-    int rc;
-
-    if (libxl_only) {
-        /* bents should be set as this is used to setup libxl_path content. */
-        assert(!fents && !ro_fents);
-    } else {
-        frontend_path = libxl__device_frontend_path(gc, device);
-        backend_path = libxl__device_backend_path(gc, device);
-    }
-    libxl_path = libxl__device_libxl_path(gc, device);
-
-    frontend_perms[0].id = device->domid;
-    frontend_perms[0].perms = XS_PERM_NONE;
-    frontend_perms[1].id = device->backend_domid;
-    frontend_perms[1].perms = XS_PERM_READ;
-
-    ro_frontend_perms[0].id = backend_perms[0].id = device->backend_domid;
-    ro_frontend_perms[0].perms = backend_perms[0].perms = XS_PERM_NONE;
-    ro_frontend_perms[1].id = backend_perms[1].id = device->domid;
-    ro_frontend_perms[1].perms = backend_perms[1].perms = XS_PERM_READ;
-
-retry_transaction:
-    if (create_transaction)
-        t = xs_transaction_start(ctx->xsh);
-
-    /* FIXME: read frontend_path and check state before removing stuff */
-
-    rc = libxl__xs_rm_checked(gc, t, libxl_path);
-    if (rc) goto out;
-
-    if (!libxl_only) {
-        rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/frontend",libxl_path),
-                                     frontend_path);
-        if (rc) goto out;
-
-        rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/backend",libxl_path),
-                                     backend_path);
-        if (rc) goto out;
-    }
-
-    /* xxx much of this function lacks error checks! */
-
-    if (fents || ro_fents) {
-        xs_rm(ctx->xsh, t, frontend_path);
-        xs_mkdir(ctx->xsh, t, frontend_path);
-        /* Console 0 is a special case. It doesn't use the regular PV
-         * state machine but also the frontend directory has
-         * historically contained other information, such as the
-         * vnc-port, which we don't want the guest fiddling with.
-         */
-        if ((device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0) ||
-            (device->kind == LIBXL__DEVICE_KIND_VUART))
-            xs_set_permissions(ctx->xsh, t, frontend_path,
-                               ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
-        else
-            xs_set_permissions(ctx->xsh, t, frontend_path,
-                               frontend_perms, ARRAY_SIZE(frontend_perms));
-        xs_write(ctx->xsh, t, GCSPRINTF("%s/backend", frontend_path),
-                 backend_path, strlen(backend_path));
-        if (fents)
-            libxl__xs_writev_perms(gc, t, frontend_path, fents,
-                                   frontend_perms, ARRAY_SIZE(frontend_perms));
-        if (ro_fents)
-            libxl__xs_writev_perms(gc, t, frontend_path, ro_fents,
-                                   ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
-    }
-
-    if (bents) {
-        if (!libxl_only) {
-            xs_rm(ctx->xsh, t, backend_path);
-            xs_mkdir(ctx->xsh, t, backend_path);
-            xs_set_permissions(ctx->xsh, t, backend_path, backend_perms,
-                               ARRAY_SIZE(backend_perms));
-            xs_write(ctx->xsh, t, GCSPRINTF("%s/frontend", backend_path),
-                     frontend_path, strlen(frontend_path));
-            libxl__xs_writev(gc, t, backend_path, bents);
-        }
-
-        /*
-         * We make a copy of everything for the backend in the libxl
-         * path as well.  This means we don't need to trust the
-         * backend.  Ideally this information would not be used and we
-         * would use the information from the json configuration
-         * instead.  But there are still places in libxl that try to
-         * reconstruct a config from xenstore.
-         *
-         * For devices without PV backend (e.g. USB devices emulated via qemu)
-         * only the libxl path is written.
-         *
-         * This duplication will typically produces duplicate keys
-         * which will go out of date, but that's OK because nothing
-         * reads those.  For example, there is usually
-         *   /libxl/$guest/device/$kind/$devid/state
-         * which starts out containing XenbusStateInitialising ("1")
-         * just like the copy in
-         *  /local/domain/$driverdom/backend/$guest/$kind/$devid/state
-         * but which won't ever be updated.
-         *
-         * This duplication is superfluous and messy but as discussed
-         * the proper fix is more intrusive than we want to do now.
-         */
-        rc = libxl__xs_writev(gc, t, libxl_path, bents);
-        if (rc) goto out;
-    }
-
-    if (!create_transaction)
-        return 0;
-
-    if (!xs_transaction_end(ctx->xsh, t, 0)) {
-        if (errno == EAGAIN)
-            goto retry_transaction;
-        else {
-            LOGED(ERROR, device->domid, "xs transaction failed");
-            return ERROR_FAIL;
-        }
-    }
-    return 0;
-
- out:
-    if (create_transaction && t)
-        libxl__xs_transaction_abort(gc, &t);
-    return rc;
-}
-
-typedef struct {
-    libxl__gc *gc;
-    libxl_device_disk *disk;
-    struct stat stab;
-} disk_try_backend_args;
-
-static int disk_try_backend(disk_try_backend_args *a,
-                            libxl_disk_backend backend)
- {
-    libxl__gc *gc = a->gc;
-    /* returns 0 (ie, DISK_BACKEND_UNKNOWN) on failure, or
-     * backend on success */
-
-    switch (backend) {
-    case LIBXL_DISK_BACKEND_PHY:
-        if (a->disk->format != LIBXL_DISK_FORMAT_RAW) {
-            goto bad_format;
-        }
-
-        if (libxl_defbool_val(a->disk->colo_enable))
-            goto bad_colo;
-
-        if (a->disk->backend_domid != LIBXL_TOOLSTACK_DOMID) {
-            LOG(DEBUG, "Disk vdev=%s, is using a storage driver domain, "
-                       "skipping physical device check", a->disk->vdev);
-            return backend;
-        }
-
-        if (a->disk->script) {
-            LOG(DEBUG, "Disk vdev=%s, uses script=... assuming phy backend",
-                a->disk->vdev);
-            return backend;
-        }
-
-        if (libxl__try_phy_backend(a->stab.st_mode))
-            return backend;
-
-        LOG(DEBUG, "Disk vdev=%s, backend phy unsuitable as phys path not a "
-                   "block device", a->disk->vdev);
-        return 0;
-
-    case LIBXL_DISK_BACKEND_TAP:
-        LOG(DEBUG, "Disk vdev=%s, backend tap unsuitable because blktap "
-                   "not available", a->disk->vdev);
-        return 0;
-
-    case LIBXL_DISK_BACKEND_QDISK:
-        if (a->disk->script) goto bad_script;
-        return backend;
-
-    default:
-        LOG(DEBUG, "Disk vdev=%s, backend %d unknown", a->disk->vdev, backend);
-        return 0;
-
-    }
-    abort(); /* notreached */
-
- bad_format:
-    LOG(DEBUG, "Disk vdev=%s, backend %s unsuitable due to format %s",
-               a->disk->vdev,
-               libxl_disk_backend_to_string(backend),
-               libxl_disk_format_to_string(a->disk->format));
-    return 0;
-
- bad_script:
-    LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with script=...",
-        a->disk->vdev, libxl_disk_backend_to_string(backend));
-    return 0;
-
- bad_colo:
-    LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with colo",
-        a->disk->vdev, libxl_disk_backend_to_string(backend));
-    return 0;
-}
-
-int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
-                                   libxl_domid *domid_out) {
-    int r;
-    unsigned int domid_sc;
-    char delim_sc;
-
-    r = sscanf(be_path, "/local/domain/%u%c", &domid_sc, &delim_sc);
-    if (!(r==2 && delim_sc=='/')) {
-        LOG(ERROR, "internal error: backend path %s unparseable!", be_path);
-        return ERROR_FAIL;
-    }
-    *domid_out = domid_sc;
-    return 0;
-}
-
-int libxl__device_disk_set_backend(libxl__gc *gc, libxl_device_disk *disk) {
-    libxl_disk_backend ok;
-    disk_try_backend_args a;
-
-    a.gc = gc;
-    a.disk = disk;
-
-    LOG(DEBUG, "Disk vdev=%s spec.backend=%s", disk->vdev,
-               libxl_disk_backend_to_string(disk->backend));
-
-    if (disk->format == LIBXL_DISK_FORMAT_EMPTY) {
-        if (!disk->is_cdrom) {
-            LOG(ERROR, "Disk vdev=%s is empty but not cdrom", disk->vdev);
-            return ERROR_INVAL;
-        }
-        if (disk->pdev_path != NULL && strcmp(disk->pdev_path, "")) {
-            LOG(ERROR,
-                "Disk vdev=%s is empty but an image has been provided: %s",
-                disk->vdev, disk->pdev_path);
-            return ERROR_INVAL;
-        }
-        memset(&a.stab, 0, sizeof(a.stab));
-    } else if ((disk->backend == LIBXL_DISK_BACKEND_UNKNOWN ||
-                disk->backend == LIBXL_DISK_BACKEND_PHY) &&
-               disk->backend_domid == LIBXL_TOOLSTACK_DOMID &&
-               !disk->script) {
-        if (stat(disk->pdev_path, &a.stab)) {
-            LOGE(ERROR, "Disk vdev=%s failed to stat: %s",
-                        disk->vdev, disk->pdev_path);
-            return ERROR_INVAL;
-        }
-    }
-
-    if (disk->backend != LIBXL_DISK_BACKEND_UNKNOWN) {
-        ok= disk_try_backend(&a, disk->backend);
-    } else {
-        ok=
-            disk_try_backend(&a, LIBXL_DISK_BACKEND_PHY) ?:
-            disk_try_backend(&a, LIBXL_DISK_BACKEND_QDISK) ?:
-            disk_try_backend(&a, LIBXL_DISK_BACKEND_TAP);
-        if (ok)
-            LOG(DEBUG, "Disk vdev=%s, using backend %s",
-                       disk->vdev,
-                       libxl_disk_backend_to_string(ok));
-    }
-    if (!ok) {
-        LOG(ERROR, "no suitable backend for disk %s", disk->vdev);
-        return ERROR_INVAL;
-    }
-    disk->backend = ok;
-    return 0;
-}
-
-char *libxl__device_disk_string_of_format(libxl_disk_format format)
-{
-    switch (format) {
-        case LIBXL_DISK_FORMAT_QCOW: return "qcow";
-        case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
-        case LIBXL_DISK_FORMAT_VHD: return "vhd";
-        case LIBXL_DISK_FORMAT_RAW:
-        case LIBXL_DISK_FORMAT_EMPTY: return "aio";
-        case LIBXL_DISK_FORMAT_QED: return "qed";
-        default: return NULL;
-    }
-}
-
-char *libxl__device_disk_string_of_backend(libxl_disk_backend backend)
-{
-    switch (backend) {
-        case LIBXL_DISK_BACKEND_QDISK: return "qdisk";
-        case LIBXL_DISK_BACKEND_TAP: return "phy";
-        case LIBXL_DISK_BACKEND_PHY: return "phy";
-        default: return NULL;
-    }
-}
-
-const char *libxl__qemu_disk_format_string(libxl_disk_format format)
-{
-    switch (format) {
-    case LIBXL_DISK_FORMAT_QCOW: return "qcow";
-    case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
-    case LIBXL_DISK_FORMAT_VHD: return "vpc";
-    case LIBXL_DISK_FORMAT_RAW: return "raw";
-    case LIBXL_DISK_FORMAT_EMPTY: return NULL;
-    case LIBXL_DISK_FORMAT_QED: return "qed";
-    default: return NULL;
-    }
-}
-
-int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor)
-{
-    struct stat buf;
-    if (stat(physpath, &buf) < 0)
-        return -1;
-    if (!S_ISBLK(buf.st_mode))
-        return -1;
-    *major = major(buf.st_rdev);
-    *minor = minor(buf.st_rdev);
-    return 0;
-}
-
-static int device_virtdisk_matches(const char *virtpath, const char *devtype,
-                                   int *index_r, int max_index,
-                                   int *partition_r, int max_partition) {
-    const char *p;
-    char *ep;
-    int tl, c;
-    long pl;
-
-    tl = strlen(devtype);
-    if (memcmp(virtpath, devtype, tl))
-        return 0;
-
-    /* We decode the drive letter as if it were in base 52
-     * with digits a-zA-Z, more or less */
-    *index_r = -1;
-    p = virtpath + tl;
-    for (;;) {
-        c = *p++;
-        if (c >= 'a' && c <= 'z') {
-            c -= 'a';
-        } else {
-            --p;
-            break;
-        }
-        (*index_r)++;
-        (*index_r) *= 26;
-        (*index_r) += c;
-
-        if (*index_r > max_index)
-            return 0;
-    }
-
-    if (!*p) {
-        *partition_r = 0;
-        return 1;
-    }
-
-    if (*p=='0')
-        return 0; /* leading zeroes not permitted in partition number */
-
-    pl = strtoul(p, &ep, 10);
-    if (pl > max_partition || *ep)
-        return 0;
-
-    *partition_r = pl;
-    return 1;
-}
-
-int libxl__device_disk_dev_number(const char *virtpath, int *pdisk,
-                                  int *ppartition)
-{
-    int disk, partition;
-    char *ep;
-    unsigned long ul;
-    int chrused;
-
-    chrused = -1;
-    if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused)  >= 2
-         && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
-        ||
-        device_virtdisk_matches(virtpath, "xvd",
-                                &disk, (1<<20)-1,
-                                &partition, 255)) {
-        if (pdisk) *pdisk = disk;
-        if (ppartition) *ppartition = partition;
-        if (disk <= 15 && partition <= 15)
-            return (202 << 8) | (disk << 4) | partition;
-        else
-            return (1 << 28) | (disk << 8) | partition;
-    }
-
-    errno = 0;
-    ul = strtoul(virtpath, &ep, 0);
-    if (!errno && !*ep && ul <= INT_MAX) {
-        /* FIXME: should parse ul to determine these. */
-        if (pdisk || ppartition)
-            return -1;
-        return ul;
-    }
-
-    if (device_virtdisk_matches(virtpath, "hd",
-                                &disk, 3,
-                                &partition, 63)) {
-        if (pdisk) *pdisk = disk;
-        if (ppartition) *ppartition = partition;
-        return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
-    }
-    if (device_virtdisk_matches(virtpath, "sd",
-                                &disk, 15,
-                                &partition, 15)) {
-        if (pdisk) *pdisk = disk;
-        if (ppartition) *ppartition = partition;
-        return (8 << 8) | (disk << 4) | partition;
-    }
-    return -1;
-}
-
-static char *encode_disk_name(char *ptr, unsigned int n)
-{
-    if (n >= 26)
-        ptr = encode_disk_name(ptr, n / 26 - 1);
-    *ptr = 'a' + n % 26;
-    return ptr + 1;
-}
-
-char *libxl__devid_to_vdev(libxl__gc *gc, int devid)
-{
-    unsigned int minor;
-    int offset;
-    int nr_parts;
-    char *ptr = NULL;
-/* Same as in Linux.
- * encode_disk_name might end up using up to 29 bytes (BUFFER_SIZE - 3)
- * including the trailing \0.
- *
- * The code is safe because 26 raised to the power of 28 (that is the
- * maximum offset that can be stored in the allocated buffer as a
- * string) is far greater than UINT_MAX on 64 bits so offset cannot be
- * big enough to exhaust the available bytes in ret. */
-#define BUFFER_SIZE 32
-    char *ret = libxl__zalloc(gc, BUFFER_SIZE);
-
-#define EXT_SHIFT 28
-#define EXTENDED (1<<EXT_SHIFT)
-#define VDEV_IS_EXTENDED(dev) ((dev)&(EXTENDED))
-#define BLKIF_MINOR_EXT(dev) ((dev)&(~EXTENDED))
-/* the size of the buffer to store the device name is 32 bytes to match the
- * equivalent buffer in the Linux kernel code */
-
-    if (!VDEV_IS_EXTENDED(devid)) {
-        minor = devid & 0xff;
-        nr_parts = 16;
-    } else {
-        minor = BLKIF_MINOR_EXT(devid);
-        nr_parts = 256;
-    }
-    offset = minor / nr_parts;
-
-    strcpy(ret, "xvd");
-    ptr = encode_disk_name(ret + 3, offset);
-    if (minor % nr_parts == 0)
-        *ptr = 0;
-    else
-        /* overflow cannot happen, thanks to the upper bound */
-        snprintf(ptr, ret + 32 - ptr,
-                "%d", minor & (nr_parts - 1));
-    return ret;
-#undef BUFFER_SIZE
-#undef EXT_SHIFT
-#undef EXTENDED
-#undef VDEV_IS_EXTENDED
-#undef BLKIF_MINOR_EXT
-}
-
-/* Device AO operations */
-
-void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev)
-{
-    aodev->ao = ao;
-    aodev->rc = 0;
-    aodev->dev = NULL;
-    aodev->num_exec = 0;
-    /* Initialize timer for QEMU Bodge */
-    libxl__ev_time_init(&aodev->timeout);
-    /*
-     * Initialize xs_watch, because it's not used on all possible
-     * execution paths, but it's unconditionally destroyed when finished.
-     */
-    libxl__xswait_init(&aodev->xswait);
-    aodev->active = 1;
-    /* We init this here because we might call device_hotplug_done
-     * without actually calling any hotplug script */
-    libxl__async_exec_init(&aodev->aes);
-    libxl__ev_child_init(&aodev->child);
-
-    libxl__ev_qmp_init(&aodev->qmp);
-}
-
-/* multidev */
-
-void libxl__multidev_begin(libxl__ao *ao, libxl__multidev *multidev)
-{
-    AO_GC;
-
-    multidev->ao = ao;
-    multidev->array = 0;
-    multidev->used = multidev->allocd = 0;
-
-    /* We allocate an aodev to represent the operation of preparing
-     * all of the other operations.  This operation is completed when
-     * we have started all the others (ie, when the user calls
-     * _prepared).  That arranges automatically that
-     *  (i) we do not think we have finished even if one of the
-     *      operations completes while we are still preparing
-     *  (ii) if we are starting zero operations, we do still
-     *      make the callback as soon as we know this fact
-     *  (iii) we have a nice consistent way to deal with any
-     *      error that might occur while deciding what to initiate
-     */
-    multidev->preparation = libxl__multidev_prepare(multidev);
-}
-
-void libxl__multidev_prepare_with_aodev(libxl__multidev *multidev,
-                                        libxl__ao_device *aodev) {
-    STATE_AO_GC(multidev->ao);
-
-    aodev->multidev = multidev;
-    aodev->callback = libxl__multidev_one_callback;
-    libxl__prepare_ao_device(ao, aodev);
-
-    if (multidev->used >= multidev->allocd) {
-        multidev->allocd = multidev->used * 2 + 5;
-        GCREALLOC_ARRAY(multidev->array, multidev->allocd);
-    }
-    multidev->array[multidev->used++] = aodev;
-}
-
-libxl__ao_device *libxl__multidev_prepare(libxl__multidev *multidev) {
-    STATE_AO_GC(multidev->ao);
-    libxl__ao_device *aodev;
-
-    GCNEW(aodev);
-    libxl__multidev_prepare_with_aodev(multidev, aodev);
-
-    return aodev;
-}
-
-void libxl__multidev_one_callback(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    libxl__multidev *multidev = aodev->multidev;
-    int i, error = 0;
-
-    aodev->active = 0;
-
-    for (i = 0; i < multidev->used; i++) {
-        if (multidev->array[i]->active)
-            return;
-
-        if (multidev->array[i]->rc)
-            error = multidev->array[i]->rc;
-    }
-
-    multidev->callback(egc, multidev, error);
-    return;
-}
-
-void libxl__multidev_prepared(libxl__egc *egc,
-                              libxl__multidev *multidev, int rc)
-{
-    multidev->preparation->rc = rc;
-    libxl__multidev_one_callback(egc, multidev->preparation);
-}
-
-/******************************************************************************/
-
-int libxl__device_destroy(libxl__gc *gc, libxl__device *dev)
-{
-    const char *be_path = NULL;
-    const char *fe_path = NULL;
-    const char *libxl_path = libxl__device_libxl_path(gc, dev);
-    xs_transaction_t t = 0;
-    int rc;
-    uint32_t domid;
-    int libxl_only = dev->backend_kind == LIBXL__DEVICE_KIND_NONE;
-
-    if (!libxl_only) {
-        be_path = libxl__device_backend_path(gc, dev);
-        fe_path = libxl__device_frontend_path(gc, dev);
-    }
-
-    rc = libxl__get_domid(gc, &domid);
-    if (rc) goto out;
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        if (domid == LIBXL_TOOLSTACK_DOMID) {
-            /*
-             * The toolstack domain is in charge of removing the
-             * frontend and libxl paths.
-             */
-            if (!libxl_only)
-                libxl__xs_path_cleanup(gc, t, fe_path);
-            libxl__xs_path_cleanup(gc, t, libxl_path);
-        }
-        if (dev->backend_domid == domid && !libxl_only) {
-            /*
-             * The driver domain is in charge of removing what it can
-             * from the backend path.
-             */
-            libxl__xs_path_cleanup(gc, t, be_path);
-        }
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    return rc;
-}
-
-/* Callback for device destruction */
-
-static void devices_remove_callback(libxl__egc *egc,
-                                    libxl__multidev *multidev, int rc);
-
-void libxl__devices_destroy(libxl__egc *egc, libxl__devices_remove_state *drs)
-{
-    STATE_AO_GC(drs->ao);
-    uint32_t domid = drs->domid;
-    char *path;
-    unsigned int num_kinds, num_dev_xsentries;
-    char **kinds = NULL, **devs = NULL;
-    int i, j, rc = 0;
-    libxl__device *dev;
-    libxl__multidev *multidev = &drs->multidev;
-    libxl__ao_device *aodev;
-    libxl__device_kind kind;
-
-    libxl__multidev_begin(ao, multidev);
-    multidev->callback = devices_remove_callback;
-
-    path = GCSPRINTF("/libxl/%d/device", domid);
-    kinds = libxl__xs_directory(gc, XBT_NULL, path, &num_kinds);
-    if (!kinds) {
-        if (errno != ENOENT) {
-            LOGE(ERROR, "unable to get xenstore device listing %s", path);
-            goto out;
-        }
-        num_kinds = 0;
-    }
-    for (i = 0; i < num_kinds; i++) {
-        if (libxl__device_kind_from_string(kinds[i], &kind))
-            continue;
-
-        path = GCSPRINTF("/libxl/%d/device/%s", domid, kinds[i]);
-        devs = libxl__xs_directory(gc, XBT_NULL, path, &num_dev_xsentries);
-        if (!devs)
-            continue;
-        for (j = 0; j < num_dev_xsentries; j++) {
-            path = GCSPRINTF("/libxl/%d/device/%s/%s/backend",
-                             domid, kinds[i], devs[j]);
-            path = libxl__xs_read(gc, XBT_NULL, path);
-            GCNEW(dev);
-            if (path && libxl__parse_backend_path(gc, path, dev) == 0) {
-                dev->domid = domid;
-                dev->kind = kind;
-                dev->devid = atoi(devs[j]);
-                if (dev->backend_kind == LIBXL__DEVICE_KIND_CONSOLE ||
-                    dev->backend_kind == LIBXL__DEVICE_KIND_VUART) {
-                    /* Currently console devices can be destroyed
-                     * synchronously by just removing xenstore entries,
-                     * this is what libxl__device_destroy does.
-                     */
-                    libxl__device_destroy(gc, dev);
-                    continue;
-                }
-                aodev = libxl__multidev_prepare(multidev);
-                aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-                aodev->dev = dev;
-                aodev->force = drs->force;
-                if (dev->kind == LIBXL__DEVICE_KIND_VUSB)
-                    libxl__initiate_device_usbctrl_remove(egc, aodev);
-                else
-                    libxl__initiate_device_generic_remove(egc, aodev);
-            }
-        }
-    }
-
-out:
-    libxl__multidev_prepared(egc, multidev, rc);
-}
-
-/* Callbacks for device related operations */
-
-/*
- * device_backend_callback is the main callback entry point, for both device
- * addition and removal. It gets called if we reach the desired state
- * (XenbusStateClosed or XenbusStateInitWait). After that, all this
- * functions get called in the order displayed below.
- *
- * If new device types are added, they should only need to modify the
- * specific hotplug scripts call, which can be found in each OS specific
- * file. If this new devices don't need a hotplug script, no modification
- * should be needed.
- */
-
-/* This callback is part of the Qemu devices Badge */
-static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                                const struct timeval *requested_abs, int rc);
-
-static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
-                                   int rc);
-
-static void device_backend_cleanup(libxl__gc *gc,
-                                   libxl__ao_device *aodev);
-
-static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev);
-
-static void device_hotplug_child_death_cb(libxl__egc *egc,
-                                          libxl__async_exec_state *aes,
-                                          int rc, int status);
-
-static void device_destroy_be_watch_cb(libxl__egc *egc,
-                                       libxl__xswait_state *xswait,
-                                       int rc, const char *data);
-
-static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev);
-
-static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev);
-
-void libxl__wait_device_connection(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    char *be_path = libxl__device_backend_path(gc, aodev->dev);
-    char *state_path = GCSPRINTF("%s/state", be_path);
-    int rc = 0;
-
-    if (QEMU_BACKEND(aodev->dev)) {
-        /*
-         * If Qemu is not running, there's no point in waiting for
-         * it to change the state of the device.
-         *
-         * If Qemu is running, it will set the state of the device to
-         * 4 directly, without waiting in state 2 for any hotplug execution.
-         */
-        device_hotplug(egc, aodev);
-        return;
-    }
-
-    rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
-                                 device_backend_callback,
-                                 state_path, XenbusStateInitWait,
-                                 LIBXL_INIT_TIMEOUT * 1000);
-    if (rc) {
-        LOGD(ERROR, aodev->dev->domid, "unable to initialize device %s", be_path);
-        goto out;
-    }
-
-    return;
-
-out:
-    aodev->rc = rc;
-    device_hotplug_done(egc, aodev);
-    return;
-}
-
-void libxl__initiate_device_generic_remove(libxl__egc *egc,
-                                           libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    xs_transaction_t t = 0;
-    char *be_path = libxl__device_backend_path(gc, aodev->dev);
-    char *state_path = GCSPRINTF("%s/state", be_path);
-    char *online_path = GCSPRINTF("%s/online", be_path);
-    const char *state;
-    libxl_dominfo info;
-    uint32_t my_domid, domid = aodev->dev->domid;
-    int rc = 0;
-
-    libxl_dominfo_init(&info);
-
-    rc = libxl__get_domid(gc, &my_domid);
-    if (rc) {
-        LOGD(ERROR, domid, "unable to get my domid");
-        goto out;
-    }
-
-    if (my_domid == LIBXL_TOOLSTACK_DOMID) {
-        rc = libxl_domain_info(CTX, &info, domid);
-        if (rc) {
-            LOGD(ERROR, domid, "unable to get info for domain %d", domid);
-            goto out;
-        }
-        if (QEMU_BACKEND(aodev->dev) &&
-            (info.paused || info.dying || info.shutdown)) {
-            /*
-             * TODO: 4.2 Bodge due to QEMU, see comment on top of
-             * libxl__initiate_device_generic_remove in libxl_internal.h
-             */
-            rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
-                                             device_qemu_timeout,
-                                             LIBXL_QEMU_BODGE_TIMEOUT * 1000);
-            if (rc) {
-                LOGD(ERROR, domid, "unable to register timeout for Qemu device %s",
-                            be_path);
-                goto out;
-            }
-            goto out_success;
-        }
-    }
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) {
-            LOGD(ERROR, domid, "unable to start transaction");
-            goto out;
-        }
-
-        if (aodev->force.flag == LIBXL__FORCE_ON)
-            libxl__xs_path_cleanup(gc, t,
-                                   libxl__device_frontend_path(gc, aodev->dev));
-
-        rc = libxl__xs_read_checked(gc, t, state_path, &state);
-        if (rc) {
-            LOGD(ERROR, domid, "unable to read device state from path %s", state_path);
-            goto out;
-        }
-
-        /* if state_path is empty, assume backend is gone (backend domain
-         * shutdown?), cleanup frontend only; rc=0 */
-        if (!state) {
-            LOG(INFO, "backend %s already removed, cleanup frontend only", be_path);
-            goto out;
-        }
-
-        rc = libxl__xs_write_checked(gc, t, online_path, "0");
-        if (rc)
-            goto out;
-
-        /*
-         * Check if device is already in "closed" state, in which case
-         * it should not be changed.
-         */
-         if (state && atoi(state) != XenbusStateClosed) {
-            rc = libxl__xs_write_checked(gc, t, state_path, GCSPRINTF("%d", XenbusStateClosing));
-            if (rc) {
-                LOGD(ERROR, domid, "unable to write to xenstore path %s", state_path);
-                goto out;
-            }
-        }
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds,
-                                 device_backend_callback,
-                                 state_path, XenbusStateClosed,
-                                 LIBXL_DESTROY_TIMEOUT * 1000);
-    if (rc) {
-        LOGD(ERROR, domid, "unable to remove device %s", be_path);
-        goto out;
-    }
-
-out_success:
-    libxl_dominfo_dispose(&info);
-    return;
-
-out:
-    aodev->rc = rc;
-    libxl_dominfo_dispose(&info);
-    libxl__xs_transaction_abort(gc, &t);
-    device_hotplug_done(egc, aodev);
-    return;
-}
-
-static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                                const struct timeval *requested_abs, int rc)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-    STATE_AO_GC(aodev->ao);
-    char *be_path = libxl__device_backend_path(gc, aodev->dev);
-    char *state_path = GCSPRINTF("%s/state", be_path);
-    const char *xs_state;
-    xs_transaction_t t = 0;
-
-    if (rc != ERROR_TIMEDOUT)
-        goto out;
-
-    libxl__ev_time_deregister(gc, &aodev->timeout);
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) {
-            LOGD(ERROR, aodev->dev->domid, "unable to start transaction");
-            goto out;
-        }
-
-        /*
-         * Check that the state path exists and is actually different than
-         * 6 before unconditionally setting it. If Qemu runs on a driver
-         * domain it is possible that the driver domain has already cleaned
-         * the backend path if the device has reached state 6.
-         */
-        rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &xs_state);
-        if (rc) goto out;
-
-        if (xs_state && atoi(xs_state) != XenbusStateClosed) {
-            rc = libxl__xs_write_checked(gc, XBT_NULL, state_path, GCSPRINTF("%d", XenbusStateClosed));
-            if (rc) goto out;
-        }
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    device_hotplug(egc, aodev);
-    return;
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    aodev->rc = rc;
-    device_hotplug_done(egc, aodev);
-}
-
-static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds,
-                                   int rc) {
-    libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds);
-    STATE_AO_GC(aodev->ao);
-
-    LOGD(DEBUG, aodev->dev->domid, "calling device_backend_cleanup");
-    device_backend_cleanup(gc, aodev);
-
-    if (rc == ERROR_TIMEDOUT &&
-        aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
-        aodev->force.flag == LIBXL__FORCE_AUTO) {
-        LOGD(DEBUG, aodev->dev->domid, "Timeout reached, initiating forced remove");
-        aodev->force.flag = LIBXL__FORCE_ON;
-        libxl__initiate_device_generic_remove(egc, aodev);
-        return;
-    }
-
-    if (rc) {
-        LOGD(ERROR, aodev->dev->domid, "unable to %s device with path %s",
-                    libxl__device_action_to_string(aodev->action),
-                    libxl__device_backend_path(gc, aodev->dev));
-        goto out;
-    }
-
-    device_hotplug(egc, aodev);
-    return;
-
-out:
-    aodev->rc = rc;
-    device_hotplug_done(egc, aodev);
-    return;
-}
-
-static void device_backend_cleanup(libxl__gc *gc, libxl__ao_device *aodev)
-{
-    if (!aodev) return;
-    libxl__ev_devstate_cancel(gc, &aodev->backend_ds);
-}
-
-static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    libxl__async_exec_state *aes = &aodev->aes;
-    char *be_path = libxl__device_backend_path(gc, aodev->dev);
-    char **args = NULL, **env = NULL;
-    int rc = 0;
-    int hotplug, nullfd = -1;
-    uint32_t domid;
-
-    /*
-     * If device is attached from a driver domain don't try to execute
-     * hotplug scripts
-     */
-    rc = libxl__get_domid(gc, &domid);
-    if (rc) {
-        LOGD(ERROR, aodev->dev->domid, "Failed to get domid");
-        goto out;
-    }
-    if (aodev->dev->backend_domid != domid) {
-        LOGD(DEBUG, aodev->dev->domid,
-             "Backend domid %d, domid %d, assuming driver domains",
-             aodev->dev->backend_domid, domid);
-
-        if (aodev->action != LIBXL__DEVICE_ACTION_REMOVE) {
-            LOG(DEBUG, "Not a remove, not executing hotplug scripts");
-            goto out;
-        }
-
-        aodev->xswait.ao = ao;
-        aodev->xswait.what = "removal of backend path";
-        aodev->xswait.path = be_path;
-        aodev->xswait.timeout_ms = LIBXL_DESTROY_TIMEOUT * 1000;
-        aodev->xswait.callback = device_destroy_be_watch_cb;
-        rc = libxl__xswait_start(gc, &aodev->xswait);
-        if (rc) {
-            LOGD(ERROR, aodev->dev->domid,
-                 "Setup of backend removal watch failed (path %s)", be_path);
-            goto out;
-        }
-
-        return;
-    }
-
-    /* Check if we have to execute hotplug scripts for this device
-     * and return the necessary args/env vars for execution */
-    hotplug = libxl__get_hotplug_script_info(gc, aodev->dev, &args, &env,
-                                             aodev->action,
-                                             aodev->num_exec);
-    switch (hotplug) {
-    case 0:
-        /* no hotplug script to execute */
-        LOGD(DEBUG, aodev->dev->domid, "No hotplug script to execute");
-        goto out;
-    case 1:
-        /* execute hotplug script */
-        break;
-    default:
-        /* everything else is an error */
-        LOGD(ERROR, aodev->dev->domid,
-                    "unable to get args/env to execute hotplug script for "
-                    "device %s", libxl__device_backend_path(gc, aodev->dev));
-        rc = hotplug;
-        goto out;
-    }
-
-    assert(args != NULL);
-    LOGD(DEBUG, aodev->dev->domid, "calling hotplug script: %s %s", args[0], args[1]);
-    LOGD(DEBUG, aodev->dev->domid, "extra args:");
-    {
-        const char *arg;
-        unsigned int x;
-
-        for (x = 2; (arg = args[x]); x++)
-            LOGD(DEBUG, aodev->dev->domid, "\t%s", arg);
-    }
-    LOGD(DEBUG, aodev->dev->domid, "env:");
-    if (env != NULL) {
-        const char *k, *v;
-        unsigned int x;
-
-        for (x = 0; (k = env[x]); x += 2) {
-            v = env[x+1];
-            LOGD(DEBUG, aodev->dev->domid, "\t%s: %s", k, v);
-        }
-    }
-
-    nullfd = open("/dev/null", O_RDONLY);
-    if (nullfd < 0) {
-        LOGD(ERROR, aodev->dev->domid, "unable to open /dev/null for hotplug script");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    aes->ao = ao;
-    aes->what = GCSPRINTF("%s %s", args[0], args[1]);
-    aes->env = env;
-    aes->args = args;
-    aes->callback = device_hotplug_child_death_cb;
-    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
-    aes->stdfds[0] = nullfd;
-    aes->stdfds[1] = 2;
-    aes->stdfds[2] = -1;
-
-    rc = libxl__async_exec_start(aes);
-    if (rc)
-        goto out;
-
-    close(nullfd);
-    assert(libxl__async_exec_inuse(&aodev->aes));
-
-    return;
-
-out:
-    if (nullfd >= 0) close(nullfd);
-    aodev->rc = rc;
-    device_hotplug_done(egc, aodev);
-    return;
-}
-
-static void device_hotplug_child_death_cb(libxl__egc *egc,
-                                          libxl__async_exec_state *aes,
-                                          int rc, int status)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
-    STATE_AO_GC(aodev->ao);
-    char *be_path = libxl__device_backend_path(gc, aodev->dev);
-    char *hotplug_error;
-
-    device_hotplug_clean(gc, aodev);
-
-    if (status && !rc) {
-        hotplug_error = libxl__xs_read(gc, XBT_NULL,
-                                       GCSPRINTF("%s/hotplug-error", be_path));
-        if (hotplug_error)
-            LOG(ERROR, "script: %s", hotplug_error);
-        rc = ERROR_FAIL;
-    }
-
-    if (rc) {
-        if (!aodev->rc)
-            aodev->rc = rc;
-        if (aodev->action == LIBXL__DEVICE_ACTION_ADD)
-            /*
-             * Only fail on device connection, on disconnection
-             * ignore error, and continue with the remove process
-             */
-             goto error;
-    }
-
-    /* Increase num_exec and call hotplug scripts again if necessary
-     * If no more executions are needed, device_hotplug will call
-     * device_hotplug_done breaking the loop.
-     */
-    aodev->num_exec++;
-    device_hotplug(egc, aodev);
-
-    return;
-
-error:
-    assert(aodev->rc);
-    device_hotplug_done(egc, aodev);
-}
-
-static void device_destroy_be_watch_cb(libxl__egc *egc,
-                                       libxl__xswait_state *xswait,
-                                       int rc, const char *dir)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(xswait, *aodev, xswait);
-    STATE_AO_GC(aodev->ao);
-
-    if (rc) {
-        if (rc == ERROR_TIMEDOUT)
-            LOGD(ERROR, aodev->dev->domid,
-                 "timed out while waiting for %s to be removed",
-                 xswait->path);
-        aodev->rc = rc;
-        goto out;
-    }
-
-    if (dir) {
-        /* backend path still exists, wait a little longer... */
-        return;
-    }
-
-out:
-    /* We are done, backend path no longer exists */
-    device_hotplug_done(egc, aodev);
-}
-
-static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    int rc;
-
-    device_hotplug_clean(gc, aodev);
-
-    /* Clean xenstore if it's a disconnection */
-    if (aodev->action == LIBXL__DEVICE_ACTION_REMOVE &&
-        (aodev->force.flag == LIBXL__FORCE_ON || !aodev->rc)) {
-        rc = libxl__device_destroy(gc, aodev->dev);
-        if (!aodev->rc)
-            aodev->rc = rc;
-    }
-
-    aodev->callback(egc, aodev);
-    return;
-}
-
-static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev)
-{
-    /* Clean events and check reentrancy */
-    libxl__ev_time_deregister(gc, &aodev->timeout);
-    libxl__xswait_stop(gc, &aodev->xswait);
-    assert(!libxl__async_exec_inuse(&aodev->aes));
-}
-
-static void devices_remove_callback(libxl__egc *egc,
-                                    libxl__multidev *multidev, int rc)
-{
-    libxl__devices_remove_state *drs = CONTAINER_OF(multidev, *drs, multidev);
-    STATE_AO_GC(drs->ao);
-
-    drs->callback(egc, drs, rc);
-    return;
-}
-
-int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
-                                 uint32_t domid, char *state,
-                                 libxl__spawn_starting *spawning,
-                                 int (*check_callback)(libxl__gc *gc,
-                                                       uint32_t domid,
-                                                       const char *state,
-                                                       void *userdata),
-                                 void *check_callback_userdata)
-{
-    char *path;
-    uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
-    return libxl__xenstore_child_wait_deprecated(gc, domid,
-                                     LIBXL_DEVICE_MODEL_START_TIMEOUT,
-                                     "Device Model", path, state, spawning,
-                                     check_callback, check_callback_userdata);
-}
-
-int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
-                            const char *state)
-{
-    int watchdog = 100;
-    const char *p, *path = GCSPRINTF("%s/state", be_path);
-    int rc;
-
-    while (watchdog-- > 0) {
-        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &p);
-        if (rc) return rc;
-
-        if (p == NULL) {
-            LOG(ERROR, "Backend %s does not exist", be_path);
-            return ERROR_FAIL;
-        }
-
-        if (!strcmp(p, state))
-            return 0;
-
-        usleep(100000);
-    }
-
-    LOG(ERROR, "Backend %s not ready", be_path);
-    return ERROR_FAIL;
-}
-
-/* generic callback for devices that only need to set ao_complete */
-void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-
-    if (aodev->rc) {
-        if (aodev->dev) {
-            LOGD(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
-                        libxl__device_action_to_string(aodev->action),
-                        libxl__device_kind_to_string(aodev->dev->kind),
-                        aodev->dev->devid);
-        } else {
-            LOG(ERROR, "unable to %s device",
-                       libxl__device_action_to_string(aodev->action));
-        }
-        goto out;
-    }
-
-out:
-    libxl__ao_complete(egc, ao, aodev->rc);
-    return;
-}
-
-/* common function to get next device id */
-int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
-                         libxl__device_kind device)
-{
-    char *libxl_dom_path, **l;
-    unsigned int nb;
-    int nextid = -1;
-
-    if (!(libxl_dom_path = libxl__xs_libxl_path(gc, domid)))
-        return nextid;
-
-    l = libxl__xs_directory(gc, XBT_NULL,
-        GCSPRINTF("%s/device/%s", libxl_dom_path,
-                  libxl__device_kind_to_string(device)), &nb);
-    if (l == NULL || nb == 0)
-        nextid = 0;
-    else
-        nextid = strtoul(l[nb - 1], NULL, 10) + 1;
-
-    return nextid;
-}
-
-static void device_complete(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-
-    LOG(DEBUG, "device %s %s %s",
-               libxl__device_backend_path(gc, aodev->dev),
-               libxl__device_action_to_string(aodev->action),
-               aodev->rc ? "failed" : "succeed");
-
-    libxl__nested_ao_free(aodev->ao);
-}
-
-static void qdisk_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss,
-                                int rc)
-{
-    STATE_AO_GC(dmss->spawn.ao);
-
-    LOGD(DEBUG, dmss->guest_domid, "qdisk backend spawn %s",
-                rc ? "failed" : "succeed");
-
-    libxl__nested_ao_free(dmss->spawn.ao);
-}
-
-/*
- * Data structures used to track devices handled by driver domains
- */
-
-/*
- * Structure that describes a device handled by a driver domain
- */
-typedef struct libxl__ddomain_device {
-    libxl__device *dev;
-    LIBXL_SLIST_ENTRY(struct libxl__ddomain_device) next;
-} libxl__ddomain_device;
-
-/*
- * Structure that describes a domain and it's associated devices
- */
-typedef struct libxl__ddomain_guest {
-    uint32_t domid;
-    int num_qdisks;
-    LIBXL_SLIST_HEAD(, struct libxl__ddomain_device) devices;
-    LIBXL_SLIST_ENTRY(struct libxl__ddomain_guest) next;
-} libxl__ddomain_guest;
-
-/*
- * Main structure used by a driver domain to keep track of devices
- * currently in use
- */
-typedef struct {
-    libxl__ao *ao;
-    libxl__ev_xswatch watch;
-    LIBXL_SLIST_HEAD(, struct libxl__ddomain_guest) guests;
-} libxl__ddomain;
-
-static libxl__ddomain_guest *search_for_guest(libxl__ddomain *ddomain,
-                                               uint32_t domid)
-{
-    libxl__ddomain_guest *dguest;
-
-    LIBXL_SLIST_FOREACH(dguest, &ddomain->guests, next) {
-        if (dguest->domid == domid)
-            return dguest;
-    }
-    return NULL;
-}
-
-static libxl__ddomain_device *search_for_device(libxl__ddomain_guest *dguest,
-                                                libxl__device *dev)
-{
-    libxl__ddomain_device *ddev;
-
-    LIBXL_SLIST_FOREACH(ddev, &dguest->devices, next) {
-#define LIBXL_DEVICE_CMP(dev1, dev2, entry) (dev1->entry == dev2->entry)
-        if (LIBXL_DEVICE_CMP(ddev->dev, dev, backend_devid) &&
-            LIBXL_DEVICE_CMP(ddev->dev, dev, backend_domid) &&
-            LIBXL_DEVICE_CMP(ddev->dev, dev, devid) &&
-            LIBXL_DEVICE_CMP(ddev->dev, dev, domid) &&
-            LIBXL_DEVICE_CMP(ddev->dev, dev, backend_kind) &&
-            LIBXL_DEVICE_CMP(ddev->dev, dev, kind))
-            return ddev;
-#undef LIBXL_DEVICE_CMP
-    }
-
-    return NULL;
-}
-
-static void check_and_maybe_remove_guest(libxl__gc *gc,
-                                         libxl__ddomain *ddomain,
-                                         libxl__ddomain_guest *dguest)
-{
-    assert(ddomain);
-
-    if (dguest != NULL && LIBXL_SLIST_FIRST(&dguest->devices) == NULL) {
-        LIBXL_SLIST_REMOVE(&ddomain->guests, dguest, libxl__ddomain_guest,
-                           next);
-        LOGD(DEBUG, dguest->domid, "Removed domain from the list of active guests");
-        /* Clear any leftovers in libxl/<domid> */
-        libxl__xs_rm_checked(gc, XBT_NULL,
-                             GCSPRINTF("libxl/%u", dguest->domid));
-        free(dguest);
-    }
-}
-
-/*
- * The following comment applies to both add_device and remove_device.
- *
- * If the return value is greater than 0, it means there's no ao dispatched,
- * so the free of the nested ao should be done by the parent when it has
- * finished.
- */
-static int add_device(libxl__egc *egc, libxl__ao *ao,
-                      libxl__ddomain_guest *dguest,
-                      libxl__device *dev)
-{
-    AO_GC;
-    libxl__ao_device *aodev;
-    libxl__ddomain_device *ddev;
-    libxl__dm_spawn_state *dmss;
-    int rc = 0;
-
-    /*
-     * New device addition, allocate a struct to hold it and add it
-     * to the list of active devices for a given guest.
-     */
-    ddev = libxl__zalloc(NOGC, sizeof(*ddev));
-    ddev->dev = libxl__zalloc(NOGC, sizeof(*ddev->dev));
-    *ddev->dev = *dev;
-    LIBXL_SLIST_INSERT_HEAD(&dguest->devices, ddev, next);
-    LOGD(DEBUG, dev->domid, "Added device %s to the list of active devices",
-         libxl__device_backend_path(gc, dev));
-
-    switch(dev->backend_kind) {
-    case LIBXL__DEVICE_KIND_QDISK:
-        if (dguest->num_qdisks == 0) {
-            GCNEW(dmss);
-            dmss->guest_domid = dev->domid;
-            dmss->spawn.ao = ao;
-            dmss->callback = qdisk_spawn_outcome;
-
-            libxl__spawn_qdisk_backend(egc, dmss);
-        }
-        dguest->num_qdisks++;
-        break;
-    default:
-        GCNEW(aodev);
-        libxl__prepare_ao_device(ao, aodev);
-        /*
-         * Clone the libxl__device to avoid races if remove_device is called
-         * before the device addition has finished.
-         */
-        GCNEW(aodev->dev);
-        *aodev->dev = *dev;
-        aodev->action = LIBXL__DEVICE_ACTION_ADD;
-        aodev->callback = device_complete;
-        libxl__wait_device_connection(egc, aodev);
-        break;
-    }
-
-    return rc;
-}
-
-static int remove_device(libxl__egc *egc, libxl__ao *ao,
-                         libxl__ddomain_guest *dguest,
-                         libxl__ddomain_device *ddev)
-{
-    AO_GC;
-    libxl__device *dev = ddev->dev;
-    libxl__ao_device *aodev;
-    int rc = 0;
-
-    switch(ddev->dev->backend_kind) {
-    case LIBXL__DEVICE_KIND_QDISK:
-        if (--dguest->num_qdisks == 0) {
-            rc = libxl__destroy_qdisk_backend(gc, dev->domid);
-            if (rc)
-                goto out;
-        }
-        libxl__device_destroy(gc, dev);
-        /* Return > 0, no ao has been dispatched */
-        rc = 1;
-        break;
-    default:
-        GCNEW(aodev);
-        libxl__prepare_ao_device(ao, aodev);
-        /*
-         * Clone the libxl__device to avoid races if there's a add_device
-         * running in parallel.
-         */
-        GCNEW(aodev->dev);
-        *aodev->dev = *dev;
-        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-        aodev->callback = device_complete;
-        libxl__initiate_device_generic_remove(egc, aodev);
-        break;
-    }
-
-    /*
-     * Removal of an active device, remove it from the list and
-     * free it's data structures if they are no longer needed.
-     *
-     * NB: the freeing is safe because all the async ops launched
-     * above or from add_device make a copy of the data they use, so
-     * there's no risk of dereferencing.
-     */
-    LIBXL_SLIST_REMOVE(&dguest->devices, ddev, libxl__ddomain_device,
-                       next);
-    LOGD(DEBUG, dev->domid, "Removed device %s from the list of active devices",
-         libxl__device_backend_path(gc, dev));
-
-    free(ddev->dev);
-    free(ddev);
-
-out:
-    return rc;
-}
-
-static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
-                                   const char *watch_path,
-                                   const char *event_path)
-{
-    libxl__ddomain *ddomain = CONTAINER_OF(watch, *ddomain, watch);
-    libxl__ao *nested_ao = libxl__nested_ao_create(ddomain->ao);
-    STATE_AO_GC(nested_ao);
-    char *p, *path;
-    const char *sstate, *sonline;
-    int state, online, rc;
-    libxl__device *dev;
-    libxl__ddomain_device *ddev = NULL;
-    libxl__ddomain_guest *dguest = NULL;
-    bool free_ao = false;
-
-    /* Check if event_path ends with "state" or "online" and truncate it. */
-    path = libxl__strdup(gc, event_path);
-    p = strrchr(path, '/');
-    if (p == NULL)
-        goto skip;
-    if (strcmp(p, "/state") != 0 && strcmp(p, "/online") != 0)
-        goto skip;
-    /* Truncate the string so it points to the backend directory. */
-    *p = '\0';
-
-    /* Fetch the value of the state and online nodes. */
-    rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/state", path),
-                                &sstate);
-    if (rc || !sstate)
-        goto skip;
-    state = atoi(sstate);
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/online", path),
-                                &sonline);
-    if (rc || !sonline)
-        goto skip;
-    online = atoi(sonline);
-
-    GCNEW(dev);
-    rc = libxl__parse_backend_path(gc, path, dev);
-    if (rc)
-        goto skip;
-
-    dguest = search_for_guest(ddomain, dev->domid);
-    if (dguest == NULL && state == XenbusStateClosed) {
-        /*
-         * Spurious state change, device has already been disconnected
-         * or never attached.
-         */
-        goto skip;
-    }
-    if (dguest == NULL) {
-        /* Create a new guest struct and initialize it */
-        dguest = libxl__zalloc(NOGC, sizeof(*dguest));
-        dguest->domid = dev->domid;
-        LIBXL_SLIST_INIT(&dguest->devices);
-        LIBXL_SLIST_INSERT_HEAD(&ddomain->guests, dguest, next);
-        LOGD(DEBUG, dguest->domid, "Added domain to the list of active guests");
-    }
-    ddev = search_for_device(dguest, dev);
-    if (ddev == NULL && state == XenbusStateClosed) {
-        /*
-         * Spurious state change, device has already been disconnected
-         * or never attached.
-         */
-        goto skip;
-    } else if (ddev == NULL) {
-        rc = add_device(egc, nested_ao, dguest, dev);
-        if (rc > 0)
-            free_ao = true;
-    } else if (state == XenbusStateClosed && online == 0) {
-        rc = remove_device(egc, nested_ao, dguest, ddev);
-        if (rc > 0)
-            free_ao = true;
-        check_and_maybe_remove_guest(gc, ddomain, dguest);
-    }
-
-    if (free_ao)
-        libxl__nested_ao_free(nested_ao);
-
-    return;
-
-skip:
-    libxl__nested_ao_free(nested_ao);
-    check_and_maybe_remove_guest(gc, ddomain, dguest);
-    return;
-}
-
-/* Handler of events for device driver domains */
-int libxl_device_events_handler(libxl_ctx *ctx,
-                                const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, 0, ao_how);
-    int rc;
-    uint32_t domid;
-    libxl__ddomain ddomain;
-    char *be_path;
-    char **kinds = NULL, **domains = NULL, **devs = NULL;
-    const char *sstate;
-    char *state_path;
-    int state;
-    unsigned int nkinds, ndomains, ndevs;
-    int i, j, k;
-
-    ddomain.ao = ao;
-    LIBXL_SLIST_INIT(&ddomain.guests);
-
-    rc = libxl__get_domid(gc, &domid);
-    if (rc) {
-        LOG(ERROR, "unable to get domain id");
-        goto out;
-    }
-
-    /*
-     * We use absolute paths because we want xswatch to also return
-     * absolute paths that can be parsed by libxl__parse_backend_path.
-     */
-    be_path = GCSPRINTF("/local/domain/%u/backend", domid);
-    rc = libxl__ev_xswatch_register(gc, &ddomain.watch, backend_watch_callback,
-                                    be_path);
-    if (rc) goto out;
-
-    kinds = libxl__xs_directory(gc, XBT_NULL, be_path, &nkinds);
-    if (kinds) {
-        for (i = 0; i < nkinds; i++) {
-            domains = libxl__xs_directory(gc, XBT_NULL,
-                    GCSPRINTF("%s/%s", be_path, kinds[i]), &ndomains);
-            if (!domains)
-                continue;
-            for (j = 0; j < ndomains; j++) {
-                devs = libxl__xs_directory(gc, XBT_NULL,
-                        GCSPRINTF("%s/%s/%s", be_path, kinds[i], domains[j]), &ndevs);
-                if (!devs)
-                    continue;
-                for (k = 0; k < ndevs; k++) {
-                    state_path = GCSPRINTF("%s/%s/%s/%s/state",
-                            be_path, kinds[i], domains[j], devs[k]);
-                    rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &sstate);
-                    if (rc || !sstate)
-                        continue;
-                    state = atoi(sstate);
-                    if (state == XenbusStateInitWait)
-                        backend_watch_callback(egc, &ddomain.watch,
-                                               be_path, state_path);
-                }
-            }
-        }
-    }
-
-    return AO_INPROGRESS;
-
-out:
-    return AO_CREATE_FAIL(rc);
-}
-
-void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
-                              const libxl__device_type *dt, const void *dev)
-{
-    int *num_dev;
-    unsigned int i;
-    void *item = NULL;
-
-    num_dev = libxl__device_type_get_num(dt, d_config);
-
-    /* Check for existing device */
-    for (i = 0; i < *num_dev; i++) {
-        if (dt->compare(libxl__device_type_get_elem(dt, d_config, i), dev)) {
-            item = libxl__device_type_get_elem(dt, d_config, i);
-        }
-    }
-
-    if (!item) {
-        void **devs = libxl__device_type_get_ptr(dt, d_config);
-        *devs = libxl__realloc(NOGC, *devs,
-                               dt->dev_elem_size * (*num_dev + 1));
-        item = libxl__device_type_get_elem(dt, d_config, *num_dev);
-        (*num_dev)++;
-    } else {
-        dt->dispose(item);
-    }
-
-    dt->init(item);
-    dt->copy(CTX, item, dev);
-}
-
-void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
-                             const libxl__device_type *dt, void *type,
-                             libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    flexarray_t *back;
-    flexarray_t *front, *ro_front;
-    libxl__device *device;
-    xs_transaction_t t = XBT_NULL;
-    libxl_domain_config d_config;
-    void *type_saved;
-    libxl__flock *lock = NULL;
-    int rc;
-
-    libxl_domain_config_init(&d_config);
-
-    type_saved = libxl__malloc(gc, dt->dev_elem_size);
-
-    dt->init(type_saved);
-    dt->copy(CTX, type_saved, type);
-
-    if (dt->set_default) {
-        rc = dt->set_default(gc, domid, type, aodev->update_json);
-        if (rc) goto out;
-    }
-
-    if (dt->update_devid) {
-        rc = dt->update_devid(gc, domid, type);
-        if (rc) goto out;
-    }
-
-    if (dt->update_config)
-        dt->update_config(gc, type_saved, type);
-
-    GCNEW(device);
-    rc = dt->to_device(gc, domid, type, device);
-    if (rc) goto out;
-
-    if (aodev->update_json) {
-        lock = libxl__lock_domain_userdata(gc, domid);
-        if (!lock) {
-            rc = ERROR_LOCK_FAIL;
-            goto out;
-        }
-
-        rc = libxl__get_domain_configuration(gc, domid, &d_config);
-        if (rc) goto out;
-
-        device_add_domain_config(gc, &d_config, dt, type_saved);
-
-        rc = libxl__dm_check_start(gc, &d_config, domid);
-        if (rc) goto out;
-    }
-
-    back = flexarray_make(gc, 16, 1);
-    front = flexarray_make(gc, 16, 1);
-    ro_front = flexarray_make(gc, 16, 1);
-
-    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
-    flexarray_append_pair(back, "online", "1");
-    flexarray_append_pair(back, "state",
-                          GCSPRINTF("%d", XenbusStateInitialising));
-
-    flexarray_append_pair(front, "backend-id",
-                          GCSPRINTF("%d", device->backend_domid));
-    flexarray_append_pair(front, "state",
-                          GCSPRINTF("%d", XenbusStateInitialising));
-
-    if (dt->set_xenstore_config)
-        dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        rc = libxl__device_exists(gc, t, device);
-        if (rc < 0) goto out;
-        if (rc == 1) {              /* already exists in xenstore */
-            LOGD(ERROR, domid, "device already exists in xenstore");
-            aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
-            rc = ERROR_DEVICE_EXISTS;
-            goto out;
-        }
-
-        if (aodev->update_json) {
-            rc = libxl__set_domain_configuration(gc, domid, &d_config);
-            if (rc) goto out;
-        }
-
-        libxl__device_generic_add(gc, t, device,
-                                  libxl__xs_kvs_of_flexarray(gc, back),
-                                  libxl__xs_kvs_of_flexarray(gc, front),
-                                  libxl__xs_kvs_of_flexarray(gc, ro_front));
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    aodev->dev = device;
-    aodev->action = LIBXL__DEVICE_ACTION_ADD;
-    libxl__wait_device_connection(egc, aodev);
-
-    rc = 0;
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    if (lock) libxl__unlock_file(lock);
-    dt->dispose(type_saved);
-    libxl_domain_config_dispose(&d_config);
-    aodev->rc = rc;
-    if (rc) aodev->callback(egc, aodev);
-    return;
-}
-
-int libxl__device_add(libxl__gc *gc, uint32_t domid,
-                      const libxl__device_type *dt, void *type)
-{
-    flexarray_t *back;
-    flexarray_t *front, *ro_front;
-    libxl__device *device;
-    int rc;
-
-    if (dt->set_default) {
-        rc = dt->set_default(gc, domid, type, false);
-        if (rc) goto out;
-    }
-
-    if (dt->update_devid) {
-        rc = dt->update_devid(gc, domid, type);
-        if (rc) goto out;
-    }
-
-    GCNEW(device);
-    rc = dt->to_device(gc, domid, type, device);
-    if (rc) goto out;
-
-    back = flexarray_make(gc, 16, 1);
-    front = flexarray_make(gc, 16, 1);
-    ro_front = flexarray_make(gc, 16, 1);
-
-    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
-    flexarray_append_pair(back, "online", "1");
-    flexarray_append_pair(back, "state",
-                          GCSPRINTF("%d", XenbusStateInitialising));
-    flexarray_append_pair(front, "backend-id",
-                          libxl__sprintf(gc, "%d", device->backend_domid));
-    flexarray_append_pair(front, "state",
-                          GCSPRINTF("%d", XenbusStateInitialising));
-
-    if (dt->set_xenstore_config)
-        dt->set_xenstore_config(gc, domid, type, back, front, ro_front);
-
-    rc = libxl__device_generic_add(gc, XBT_NULL, device,
-                                   libxl__xs_kvs_of_flexarray(gc, back),
-                                   libxl__xs_kvs_of_flexarray(gc, front),
-                                   libxl__xs_kvs_of_flexarray(gc, ro_front));
-    if (rc) goto out;
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-void *libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
-                         uint32_t domid, int *num)
-{
-    void *r = NULL;
-    void *list = NULL;
-    void *item = NULL;
-    char *libxl_path;
-    char **dir = NULL;
-    unsigned int ndirs = 0;
-    unsigned int ndevs = 0;
-    int rc;
-
-    *num = 0;
-
-    libxl_path = GCSPRINTF("%s/device/%s",
-                           libxl__xs_libxl_path(gc, domid),
-                           libxl__device_kind_to_string(dt->type));
-
-    dir = libxl__xs_directory(gc, XBT_NULL, libxl_path, &ndirs);
-
-    if (dir && ndirs) {
-        if (dt->get_num) {
-            if (ndirs != 1) {
-                LOGD(ERROR, domid, "multiple entries in %s\n", libxl_path);
-                rc = ERROR_FAIL;
-                goto out;
-            }
-            rc = dt->get_num(gc, GCSPRINTF("%s/%s", libxl_path, *dir), &ndevs);
-            if (rc) goto out;
-        } else {
-            ndevs = ndirs;
-        }
-        list = libxl__malloc(NOGC, dt->dev_elem_size * ndevs);
-        item = list;
-
-        while (*num < ndevs) {
-            dt->init(item);
-
-            if (dt->from_xenstore) {
-                int nr = dt->get_num ? *num : atoi(*dir);
-                char *device_libxl_path = GCSPRINTF("%s/%s", libxl_path, *dir);
-                rc = dt->from_xenstore(gc, device_libxl_path, nr, item);
-                if (rc) goto out;
-            }
-
-            item = (uint8_t *)item + dt->dev_elem_size;
-            ++(*num);
-            if (!dt->get_num)
-                ++dir;
-        }
-    }
-
-    r = list;
-    list = NULL;
-
-out:
-
-    if (list) {
-        libxl__device_list_free(dt, list, *num);
-        *num = 0;
-    }
-
-    return r;
-}
-
-void libxl__device_list_free(const libxl__device_type *dt,
-                             void *list, int num)
-{
-    int i;
-
-    for (i = 0; i < num; i++)
-        dt->dispose((uint8_t*)list + i * dt->dev_elem_size);
-
-    free(list);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_disk.c b/tools/libxl/libxl_disk.c
deleted file mode 100644 (file)
index de183e0..0000000
+++ /dev/null
@@ -1,1390 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-#define BACKEND_STRING_SIZE 5
-
-static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
-                                        const char *wpath, const char *epath) {
-    EGC_GC;
-    libxl_evgen_disk_eject *evg = (void*)w;
-    const char *backend;
-    char *value;
-    char backend_type[BACKEND_STRING_SIZE+1];
-    int rc;
-
-    value = libxl__xs_read(gc, XBT_NULL, wpath);
-
-    if (!value || strcmp(value,  "eject"))
-        return;
-
-    if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) {
-        LIBXL__EVENT_DISASTER(gc, "xs_write failed acknowledging eject",
-                              errno, LIBXL_EVENT_TYPE_DISK_EJECT);
-        return;
-    }
-
-    libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user);
-    libxl_device_disk *disk = &ev->u.disk_eject.disk;
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend);
-    if (rc) {
-        LIBXL__EVENT_DISASTER(gc, "xs_read failed reading be_ptr_path",
-                              errno, LIBXL_EVENT_TYPE_DISK_EJECT);
-        return;
-    }
-    if (!backend) {
-        /* device has been removed, not simply ejected */
-        return;
-    }
-
-    sscanf(backend,
-            "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
-           "[a-z]/%*d/%*d",
-           &disk->backend_domid, backend_type);
-    if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
-        disk->backend = LIBXL_DISK_BACKEND_TAP;
-    } else if (!strcmp(backend_type, "qdisk")) {
-        disk->backend = LIBXL_DISK_BACKEND_QDISK;
-    } else {
-        disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
-    }
-
-    disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
-    disk->format = LIBXL_DISK_FORMAT_EMPTY;
-    /* this value is returned to the user: do not free right away */
-    disk->vdev = libxl__strdup(NOGC, evg->vdev);
-    disk->removable = 1;
-    disk->readwrite = 0;
-    disk->is_cdrom = 1;
-
-    libxl__event_occurred(egc, ev);
-}
-
-int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
-                              const char *vdev, libxl_ev_user user,
-                              libxl_evgen_disk_eject **evgen_out) {
-    GC_INIT(ctx);
-    CTX_LOCK;
-    int rc;
-    char *path;
-    libxl_evgen_disk_eject *evg = NULL;
-
-    evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
-    memset(evg, 0, sizeof(*evg));
-    evg->user = user;
-    evg->domid = guest_domid;
-    LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
-
-    uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
-
-    if (!domid)
-        domid = guest_domid;
-
-    int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
-
-    path = GCSPRINTF("%s/eject",
-                    libxl__domain_device_frontend_path(gc, domid, devid,
-                    LIBXL__DEVICE_KIND_VBD));
-    if (!path) { rc = ERROR_NOMEM; goto out; }
-
-    const char *libxl_path = libxl__domain_device_frontend_path(gc, domid, devid,
-                                                                LIBXL__DEVICE_KIND_VBD);
-    evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
-
-    const char *configured_vdev;
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-            GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
-    if (rc) goto out;
-
-    evg->vdev = libxl__strdup(NOGC, configured_vdev);
-
-    rc = libxl__ev_xswatch_register(gc, &evg->watch,
-                                    disk_eject_xswatch_callback, path);
-    if (rc) goto out;
-
-    *evgen_out = evg;
-    CTX_UNLOCK;
-    GC_FREE;
-    return 0;
-
- out:
-    if (evg)
-        libxl__evdisable_disk_eject(gc, evg);
-    CTX_UNLOCK;
-    GC_FREE;
-    return rc;
-}
-
-void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
-    CTX_LOCK;
-
-    LIBXL_LIST_REMOVE(evg, entry);
-
-    if (libxl__ev_xswatch_isregistered(&evg->watch))
-        libxl__ev_xswatch_deregister(gc, &evg->watch);
-
-    free(evg->vdev);
-    free(evg->be_ptr_path);
-    free(evg);
-
-    CTX_UNLOCK;
-}
-
-void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
-    GC_INIT(ctx);
-    libxl__evdisable_disk_eject(gc, evg);
-    GC_FREE;
-}
-
-static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
-                                         libxl_device_disk *disk, bool hotplug)
-{
-    int rc;
-
-    libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
-    libxl_defbool_setdefault(&disk->colo_enable, false);
-    libxl_defbool_setdefault(&disk->colo_restore_enable, false);
-
-    rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
-    if (rc < 0) return rc;
-
-    /* Force Qdisk backend for CDROM devices of guests with a device model. */
-    if (disk->is_cdrom != 0 &&
-        libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
-        if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
-              disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
-            LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
-            return ERROR_FAIL;
-        }
-        disk->backend = LIBXL_DISK_BACKEND_QDISK;
-    }
-
-    rc = libxl__device_disk_set_backend(gc, disk);
-    return rc;
-}
-
-static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
-                                   const libxl_device_disk *disk,
-                                   libxl__device *device)
-{
-    int devid;
-
-    devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
-    if (devid==-1) {
-        LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
-             disk->vdev);
-        return ERROR_INVAL;
-    }
-
-    device->backend_domid = disk->backend_domid;
-    device->backend_devid = devid;
-
-    switch (disk->backend) {
-        case LIBXL_DISK_BACKEND_PHY:
-            device->backend_kind = LIBXL__DEVICE_KIND_VBD;
-            break;
-        case LIBXL_DISK_BACKEND_TAP:
-            device->backend_kind = LIBXL__DEVICE_KIND_VBD;
-            break;
-        case LIBXL_DISK_BACKEND_QDISK:
-            device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
-            break;
-        default:
-            LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
-                 disk->backend);
-            return ERROR_INVAL;
-    }
-
-    device->domid = domid;
-    device->devid = devid;
-    device->kind  = LIBXL__DEVICE_KIND_VBD;
-
-    return 0;
-}
-
-/* Specific function called directly only by local disk attach,
- * all other users should instead use the regular
- * libxl__device_disk_add wrapper
- *
- * The (optionally) passed function get_vdev will be used to
- * set the vdev the disk should be attached to. When it is set the caller
- * must also pass get_vdev_user, which will be passed to get_vdev.
- *
- * The passed get_vdev function is also in charge of printing
- * the corresponding error message when appropiate.
- */
-static void device_disk_add(libxl__egc *egc, uint32_t domid,
-                           libxl_device_disk *disk,
-                           libxl__ao_device *aodev,
-                           char *get_vdev(libxl__gc *, void *,
-                                          xs_transaction_t),
-                           void *get_vdev_user)
-{
-    STATE_AO_GC(aodev->ao);
-    flexarray_t *front = NULL;
-    flexarray_t *back = NULL;
-    char *dev = NULL, *script;
-    libxl__device *device;
-    int rc;
-    libxl_ctx *ctx = gc->owner;
-    xs_transaction_t t = XBT_NULL;
-    libxl_domain_config d_config;
-    libxl_device_disk disk_saved;
-    libxl__flock *lock = NULL;
-
-    libxl_domain_config_init(&d_config);
-    libxl_device_disk_init(&disk_saved);
-    libxl_device_disk_copy(ctx, &disk_saved, disk);
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /*
-     * get_vdev != NULL -> local attach
-     * get_vdev == NULL -> block attach
-     *
-     * We don't care about local attach state because it's only
-     * intermediate state.
-     */
-    if (!get_vdev && aodev->update_json) {
-        lock = libxl__lock_domain_userdata(gc, domid);
-        if (!lock) {
-            rc = ERROR_LOCK_FAIL;
-            goto out;
-        }
-
-        rc = libxl__get_domain_configuration(gc, domid, &d_config);
-        if (rc) goto out;
-
-        device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
-                                 &disk_saved);
-
-        rc = libxl__dm_check_start(gc, &d_config, domid);
-        if (rc) goto out;
-    }
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        if (get_vdev) {
-            assert(get_vdev_user);
-            disk->vdev = get_vdev(gc, get_vdev_user, t);
-            if (disk->vdev == NULL) {
-                rc = ERROR_FAIL;
-                goto out;
-            }
-        }
-
-        rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
-        if (rc) goto out;
-
-        front = flexarray_make(gc, 16, 1);
-        back = flexarray_make(gc, 16, 1);
-
-        GCNEW(device);
-        rc = libxl__device_from_disk(gc, domid, disk, device);
-        if (rc != 0) {
-            LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
-                 disk->vdev);
-            goto out;
-        }
-
-        rc = libxl__device_exists(gc, t, device);
-        if (rc < 0) goto out;
-        if (rc == 1) {              /* already exists in xenstore */
-            LOGD(ERROR, domid, "device already exists in xenstore");
-            aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
-            rc = ERROR_DEVICE_EXISTS;
-            goto out;
-        }
-
-        switch (disk->backend) {
-            case LIBXL_DISK_BACKEND_PHY:
-                dev = disk->pdev_path;
-
-                flexarray_append(back, "params");
-                flexarray_append(back, dev);
-
-                script = libxl__abs_path(gc, disk->script?: "block",
-                                         libxl__xen_script_dir_path());
-                flexarray_append_pair(back, "script", script);
-
-                assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
-                break;
-
-            case LIBXL_DISK_BACKEND_TAP:
-                LOG(ERROR, "blktap is not supported");
-                rc = ERROR_FAIL;
-                goto out;
-            case LIBXL_DISK_BACKEND_QDISK:
-                flexarray_append(back, "params");
-                flexarray_append(back, GCSPRINTF("%s:%s",
-                              libxl__device_disk_string_of_format(disk->format),
-                              disk->pdev_path ? : ""));
-                if (libxl_defbool_val(disk->colo_enable)) {
-                    flexarray_append(back, "colo-host");
-                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
-                    flexarray_append(back, "colo-port");
-                    flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
-                    flexarray_append(back, "colo-export");
-                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
-                    flexarray_append(back, "active-disk");
-                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
-                    flexarray_append(back, "hidden-disk");
-                    flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
-                }
-                assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
-                break;
-            default:
-                LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
-                     disk->backend);
-                rc = ERROR_INVAL;
-                goto out;
-        }
-
-        flexarray_append(back, "frontend-id");
-        flexarray_append(back, GCSPRINTF("%d", domid));
-        flexarray_append(back, "online");
-        flexarray_append(back, "1");
-        flexarray_append(back, "removable");
-        flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
-        flexarray_append(back, "bootable");
-        flexarray_append(back, GCSPRINTF("%d", 1));
-        flexarray_append(back, "state");
-        flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
-        flexarray_append(back, "dev");
-        flexarray_append(back, disk->vdev);
-        flexarray_append(back, "type");
-        flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
-        flexarray_append(back, "mode");
-        flexarray_append(back, disk->readwrite ? "w" : "r");
-        flexarray_append(back, "device-type");
-        flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
-        if (disk->direct_io_safe) {
-            flexarray_append(back, "direct-io-safe");
-            flexarray_append(back, "1");
-        }
-        flexarray_append_pair(back, "discard-enable",
-                              libxl_defbool_val(disk->discard_enable) ?
-                              "1" : "0");
-
-        flexarray_append(front, "backend-id");
-        flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
-        flexarray_append(front, "state");
-        flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
-        flexarray_append(front, "virtual-device");
-        flexarray_append(front, GCSPRINTF("%d", device->devid));
-        flexarray_append(front, "device-type");
-        flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
-
-        /*
-         * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
-         * write disk native protocol to frontend node. Xend does this, port
-         * this behaviour to xl.
-         *
-         * New kernels write this node themselves. In that case it just
-         * overwrites an existing node which is OK.
-         */
-        if (type == LIBXL_DOMAIN_TYPE_PV) {
-            const char *protocol =
-                xc_domain_get_native_protocol(ctx->xch, domid);
-            if (protocol) {
-                flexarray_append(front, "protocol");
-                flexarray_append(front, libxl__strdup(gc, protocol));
-            }
-        }
-
-        if (!get_vdev && aodev->update_json) {
-            rc = libxl__set_domain_configuration(gc, domid, &d_config);
-            if (rc) goto out;
-        }
-
-        libxl__device_generic_add(gc, t, device,
-                                  libxl__xs_kvs_of_flexarray(gc, back),
-                                  libxl__xs_kvs_of_flexarray(gc, front),
-                                  NULL);
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    aodev->dev = device;
-    aodev->action = LIBXL__DEVICE_ACTION_ADD;
-    libxl__wait_device_connection(egc, aodev);
-
-    rc = 0;
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    if (lock) libxl__unlock_file(lock);
-    libxl_device_disk_dispose(&disk_saved);
-    libxl_domain_config_dispose(&d_config);
-    aodev->rc = rc;
-    if (rc) aodev->callback(egc, aodev);
-    return;
-}
-
-static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
-                                   libxl_device_disk *disk,
-                                   libxl__ao_device *aodev)
-{
-    device_disk_add(egc, domid, disk, aodev, NULL, NULL);
-}
-
-static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
-                                     libxl_devid devid,
-                                     libxl_device_disk *disk)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    unsigned int len;
-    char *tmp;
-    int rc;
-
-    const char *backend_path;
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/backend", libxl_path),
-                                &backend_path);
-    if (rc) goto out;
-
-    if (!backend_path) {
-        LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
-    if (rc) {
-        LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
-        goto out;
-    }
-
-    /*
-     * "params" may not be present; but everything else must be.
-     * colo releated entries(colo-host, colo-port, colo-export,
-     * active-disk and hidden-disk) are present only if colo is
-     * enabled.
-     */
-    tmp = xs_read(ctx->xsh, XBT_NULL,
-                  GCSPRINTF("%s/params", libxl_path), &len);
-    if (tmp && strchr(tmp, ':')) {
-        disk->pdev_path = strdup(strchr(tmp, ':') + 1);
-        free(tmp);
-    } else {
-        disk->pdev_path = tmp;
-    }
-
-    tmp = xs_read(ctx->xsh, XBT_NULL,
-                  GCSPRINTF("%s/colo-host", libxl_path), &len);
-    if (tmp) {
-        libxl_defbool_set(&disk->colo_enable, true);
-        disk->colo_host = tmp;
-
-        tmp = xs_read(ctx->xsh, XBT_NULL,
-                      GCSPRINTF("%s/colo-port", libxl_path), &len);
-        if (!tmp) {
-            LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
-            goto cleanup;
-        }
-        disk->colo_port = atoi(tmp);
-
-#define XS_READ_COLO(param, item) do {                                  \
-        tmp = xs_read(ctx->xsh, XBT_NULL,                               \
-                      GCSPRINTF("%s/"#param"", libxl_path), &len);         \
-        if (!tmp) {                                                     \
-            LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path);   \
-            goto cleanup;                                               \
-        }                                                               \
-        disk->item = tmp;                                               \
-} while (0)
-        XS_READ_COLO(colo-export, colo_export);
-        XS_READ_COLO(active-disk, active_disk);
-        XS_READ_COLO(hidden-disk, hidden_disk);
-#undef XS_READ_COLO
-    } else {
-        libxl_defbool_set(&disk->colo_enable, false);
-    }
-
-    tmp = libxl__xs_read(gc, XBT_NULL,
-                         GCSPRINTF("%s/type", libxl_path));
-    if (!tmp) {
-        LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
-        goto cleanup;
-    }
-    libxl_string_to_backend(ctx, tmp, &(disk->backend));
-
-    disk->vdev = xs_read(ctx->xsh, XBT_NULL,
-                         GCSPRINTF("%s/dev", libxl_path), &len);
-    if (!disk->vdev) {
-        LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
-        goto cleanup;
-    }
-
-    tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
-                         (gc, "%s/removable", libxl_path));
-    if (!tmp) {
-        LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
-        goto cleanup;
-    }
-    disk->removable = atoi(tmp);
-
-    tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
-    if (!tmp) {
-        LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
-        goto cleanup;
-    }
-    if (!strcmp(tmp, "w"))
-        disk->readwrite = 1;
-    else
-        disk->readwrite = 0;
-
-    tmp = libxl__xs_read(gc, XBT_NULL,
-                         GCSPRINTF("%s/device-type", libxl_path));
-    if (!tmp) {
-        LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
-        goto cleanup;
-    }
-    disk->is_cdrom = !strcmp(tmp, "cdrom");
-
-    disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
-
-    return 0;
-cleanup:
-    rc = ERROR_FAIL;
- out:
-    libxl_device_disk_dispose(disk);
-    return rc;
-}
-
-int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
-                              const char *vdev, libxl_device_disk *disk)
-{
-    GC_INIT(ctx);
-    char *libxl_path;
-    int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
-    int rc = ERROR_FAIL;
-
-    if (devid < 0)
-        return ERROR_INVAL;
-
-    libxl_device_disk_init(disk);
-
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, devid,
-                                                 LIBXL__DEVICE_KIND_VBD);
-
-    rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
-                              const libxl_device_disk *disk,
-                              libxl_diskinfo *diskinfo)
-{
-    GC_INIT(ctx);
-    char *fe_path, *libxl_path;
-    char *val;
-    int rc;
-
-    diskinfo->backend = NULL;
-
-    diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
-
-    /* tap devices entries in xenstore are written as vbd devices. */
-    fe_path = libxl__domain_device_frontend_path(gc, domid, diskinfo->devid,
-                                                 LIBXL__DEVICE_KIND_VBD);
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, diskinfo->devid,
-                                                 LIBXL__DEVICE_KIND_VBD);
-    diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
-                                GCSPRINTF("%s/backend", libxl_path), NULL);
-    if (!diskinfo->backend) {
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
-                                        &diskinfo->backend_id);
-    if (rc) goto out;
-
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
-    diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
-    diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
-    diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
-    diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
-                                 GCSPRINTF("%s/frontend", libxl_path), NULL);
-    diskinfo->frontend_id = domid;
-
-    GC_FREE;
-    return 0;
-
- out:
-    free(diskinfo->backend);
-    return rc;
-}
-
-typedef struct {
-    libxl__ao *ao;
-    libxl_domid domid;
-    libxl_device_disk *disk;
-    libxl_device_disk disk_saved;
-    libxl__ev_slowlock qmp_lock;
-    int dm_ver;
-    libxl__ev_time time;
-    libxl__ev_qmp qmp;
-} libxl__cdrom_insert_state;
-
-static void cdrom_insert_lock_acquired(libxl__egc *, libxl__ev_slowlock *,
-                                       int rc);
-static void cdrom_insert_ejected(libxl__egc *egc, libxl__ev_qmp *,
-                                 const libxl__json_object *, int rc);
-static void cdrom_insert_addfd_cb(libxl__egc *egc, libxl__ev_qmp *,
-                                  const libxl__json_object *, int rc);
-static void cdrom_insert_inserted(libxl__egc *egc, libxl__ev_qmp *,
-                                  const libxl__json_object *, int rc);
-static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
-                                const struct timeval *requested_abs,
-                                int rc);
-static void cdrom_insert_done(libxl__egc *egc,
-                              libxl__cdrom_insert_state *cis,
-                              int rc);
-
-int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
-                       const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int num = 0, i;
-    libxl_device_disk *disks = NULL;
-    int rc;
-    libxl__cdrom_insert_state *cis;
-
-    GCNEW(cis);
-    cis->ao = ao;
-    cis->domid = domid;
-    cis->disk = disk;
-    libxl_device_disk_init(&cis->disk_saved);
-    libxl_device_disk_copy(ctx, &cis->disk_saved, disk);
-    libxl__ev_devlock_init(&cis->qmp_lock);
-    cis->qmp_lock.ao = ao;
-    cis->qmp_lock.domid = domid;
-    libxl__ev_time_init(&cis->time);
-    libxl__ev_qmp_init(&cis->qmp);
-    cis->qmp.ao = ao;
-    cis->qmp.domid = domid;
-    cis->qmp.payload_fd = -1;
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (type != LIBXL_DOMAIN_TYPE_HVM) {
-        LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    if (libxl_get_stubdom_id(ctx, domid) != 0) {
-        LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    cis->dm_ver = libxl__device_model_version_running(gc, domid);
-    if (cis->dm_ver == -1) {
-        LOGD(ERROR, domid, "Cannot determine device model version");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
-    for (i = 0; i < num; i++) {
-        if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
-        {
-            /* Found.  Set backend type appropriately. */
-            disk->backend=disks[i].backend;
-            break;
-        }
-    }
-    if (i == num) {
-        LOGD(ERROR, domid, "Virtual device not found");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__device_disk_setdefault(gc, domid, disk, false);
-    if (rc) goto out;
-
-    if (!disk->pdev_path) {
-        disk->pdev_path = libxl__strdup(NOGC, "");
-        disk->format = LIBXL_DISK_FORMAT_EMPTY;
-    }
-
-out:
-    libxl__device_list_free(&libxl__disk_devtype, disks, num);
-    if (rc) {
-        cdrom_insert_done(egc, cis, rc); /* must be last */
-    } else {
-        cis->qmp_lock.callback = cdrom_insert_lock_acquired;
-        libxl__ev_slowlock_lock(egc, &cis->qmp_lock); /* must be last */
-    }
-    return AO_INPROGRESS;
-}
-
-static void cdrom_insert_lock_acquired(libxl__egc *egc,
-                                       libxl__ev_slowlock *lock,
-                                       int rc)
-{
-    libxl__cdrom_insert_state *cis = CONTAINER_OF(lock, *cis, qmp_lock);
-    STATE_AO_GC(cis->ao);
-
-    if (rc) goto out;
-
-    rc = libxl__ev_time_register_rel(ao, &cis->time,
-                                     cdrom_insert_timout,
-                                     LIBXL_HOTPLUG_TIMEOUT * 1000);
-    if (rc) goto out;
-
-    /* We need to eject the original image first.
-     * JSON is not updated.
-     */
-
-    if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
-        libxl__json_object *args = NULL;
-        int devid = libxl__device_disk_dev_number(cis->disk->vdev,
-                                                  NULL, NULL);
-
-        QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
-        cis->qmp.callback = cdrom_insert_ejected;
-        rc = libxl__ev_qmp_send(egc, &cis->qmp, "eject", args);
-        if (rc) goto out;
-    } else {
-        cdrom_insert_ejected(egc, &cis->qmp, NULL, 0); /* must be last */
-    }
-    return;
-
-out:
-    cdrom_insert_done(egc, cis, rc); /* must be last */
-}
-
-static void cdrom_insert_ejected(libxl__egc *egc,
-                                 libxl__ev_qmp *qmp,
-                                 const libxl__json_object *response,
-                                 int rc)
-{
-    EGC_GC;
-    libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
-    libxl__flock *data_lock = NULL;
-    libxl__device device;
-    const char *be_path, *libxl_path;
-    flexarray_t *empty = NULL;
-    xs_transaction_t t = XBT_NULL;
-    char *tmp;
-    libxl_domain_config d_config;
-    bool has_callback = false;
-
-    /* convenience aliases */
-    libxl_domid domid = cis->domid;
-    libxl_device_disk *disk = cis->disk;
-
-    libxl_domain_config_init(&d_config);
-
-    if (rc) goto out;
-
-    rc = libxl__device_from_disk(gc, domid, disk, &device);
-    if (rc) goto out;
-    be_path = libxl__device_backend_path(gc, &device);
-    libxl_path = libxl__device_libxl_path(gc, &device);
-
-    data_lock = libxl__lock_domain_userdata(gc, domid);
-    if (!data_lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    empty = flexarray_make(gc, 4, 1);
-    flexarray_append_pair(empty, "type",
-                          libxl__device_disk_string_of_backend(disk->backend));
-    flexarray_append_pair(empty, "params", "");
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-        /* Sanity check: make sure the device exists before writing here */
-        tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
-        if (!tmp)
-        {
-            LOGD(ERROR, domid, "Internal error: %s does not exist",
-                 GCSPRINTF("%s/frontend", libxl_path));
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
-
-        rc = libxl__xs_writev(gc, t, be_path, kvs);
-        if (rc) goto out;
-
-        rc = libxl__xs_writev(gc, t, libxl_path, kvs);
-        if (rc) goto out;
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    /*
-     * Now that the drive is empty, we can insert the new media.
-     */
-
-    rc = libxl__get_domain_configuration(gc, domid, &d_config);
-    if (rc) goto out;
-
-    device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
-                             &cis->disk_saved);
-
-    rc = libxl__dm_check_start(gc, &d_config, domid);
-    if (rc) goto out;
-
-    if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
-        disk->format != LIBXL_DISK_FORMAT_EMPTY) {
-        libxl__json_object *args = NULL;
-
-        assert(qmp->payload_fd == -1);
-        qmp->payload_fd = open(disk->pdev_path, O_RDONLY);
-        if (qmp->payload_fd < 0) {
-            LOGED(ERROR, domid, "Failed to open cdrom file %s",
-                  disk->pdev_path);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        /* This free form parameter is not use by QEMU or libxl. */
-        QMP_PARAMETERS_SPRINTF(&args, "opaque", "%s:%s",
-                               libxl_disk_format_to_string(disk->format),
-                               disk->pdev_path);
-        qmp->callback = cdrom_insert_addfd_cb;
-        rc = libxl__ev_qmp_send(egc, qmp, "add-fd", args);
-        if (rc) goto out;
-        has_callback = true;
-    } else {
-        has_callback = false;
-    }
-
-    rc = 0;
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    libxl_domain_config_dispose(&d_config);
-    if (data_lock) libxl__unlock_file(data_lock);
-    if (rc) {
-        cdrom_insert_done(egc, cis, rc); /* must be last */
-    } else if (!has_callback) {
-        /* Only called if no asynchronous callback are set. */
-        cdrom_insert_inserted(egc, qmp, NULL, 0); /* must be last */
-    }
-}
-
-static void cdrom_insert_addfd_cb(libxl__egc *egc,
-                                  libxl__ev_qmp *qmp,
-                                  const libxl__json_object *response,
-                                  int rc)
-{
-    EGC_GC;
-    libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
-    libxl__json_object *args = NULL;
-    const libxl__json_object *o;
-    int devid;
-    int fdset;
-
-    /* convenience aliases */
-    libxl_device_disk *disk = cis->disk;
-
-    close(qmp->payload_fd);
-    qmp->payload_fd = -1;
-
-    if (rc) goto out;
-
-    o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
-    if (!o) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    fdset = libxl__json_object_get_integer(o);
-
-    devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
-    QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
-    QMP_PARAMETERS_SPRINTF(&args, "target", "/dev/fdset/%d", fdset);
-    libxl__qmp_param_add_string(gc, &args, "arg",
-        libxl__qemu_disk_format_string(disk->format));
-    qmp->callback = cdrom_insert_inserted;
-    rc = libxl__ev_qmp_send(egc, qmp, "change", args);
-out:
-    if (rc)
-        cdrom_insert_done(egc, cis, rc); /* must be last */
-}
-
-static void cdrom_insert_inserted(libxl__egc *egc,
-                                  libxl__ev_qmp *qmp,
-                                  const libxl__json_object *response,
-                                  int rc)
-{
-    EGC_GC;
-    libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
-    libxl__flock *data_lock = NULL;
-    libxl_domain_config d_config;
-    flexarray_t *insert = NULL;
-    xs_transaction_t t = XBT_NULL;
-    libxl__device device;
-    const char *be_path, *libxl_path;
-    char *tmp;
-
-    /* convenience aliases */
-    libxl_domid domid = cis->domid;
-    libxl_device_disk *disk = cis->disk;
-
-    libxl_domain_config_init(&d_config);
-
-    if (rc) goto out;
-
-    rc = libxl__device_from_disk(gc, domid, disk, &device);
-    if (rc) goto out;
-    be_path = libxl__device_backend_path(gc, &device);
-    libxl_path = libxl__device_libxl_path(gc, &device);
-
-    data_lock = libxl__lock_domain_userdata(gc, domid);
-    if (!data_lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    rc = libxl__get_domain_configuration(gc, domid, &d_config);
-    if (rc) goto out;
-
-    device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
-                             &cis->disk_saved);
-
-    insert = flexarray_make(gc, 4, 1);
-    flexarray_append_pair(insert, "type",
-                      libxl__device_disk_string_of_backend(disk->backend));
-    if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
-        flexarray_append_pair(insert, "params",
-                    GCSPRINTF("%s:%s",
-                        libxl__device_disk_string_of_format(disk->format),
-                        disk->pdev_path));
-    else
-        flexarray_append_pair(insert, "params", "");
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-        /* Sanity check: make sure the device exists before writing here */
-        tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
-        if (!tmp)
-        {
-            LOGD(ERROR, domid, "Internal error: %s does not exist",
-                 GCSPRINTF("%s/frontend", libxl_path));
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        rc = libxl__set_domain_configuration(gc, domid, &d_config);
-        if (rc) goto out;
-
-        char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
-
-        rc = libxl__xs_writev(gc, t, be_path, kvs);
-        if (rc) goto out;
-
-        rc = libxl__xs_writev(gc, t, libxl_path, kvs);
-        if (rc) goto out;
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    rc = 0;
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    libxl_domain_config_dispose(&d_config);
-    if (data_lock) libxl__unlock_file(data_lock);
-    cdrom_insert_done(egc, cis, rc); /* must be last */
-}
-
-static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
-                                const struct timeval *requested_abs,
-                                int rc)
-{
-    EGC_GC;
-    libxl__cdrom_insert_state *cis = CONTAINER_OF(ev, *cis, time);
-    LOGD(ERROR, cis->domid, "cdrom insertion timed out");
-    cdrom_insert_done(egc, cis, rc);
-}
-
-static void cdrom_insert_done(libxl__egc *egc,
-                              libxl__cdrom_insert_state *cis,
-                              int rc)
-{
-    EGC_GC;
-
-    libxl__ev_time_deregister(gc, &cis->time);
-    libxl__ev_qmp_dispose(gc, &cis->qmp);
-    if (cis->qmp.payload_fd >= 0) close(cis->qmp.payload_fd);
-    libxl__ev_slowlock_unlock(gc, &cis->qmp_lock);
-    libxl_device_disk_dispose(&cis->disk_saved);
-    libxl__ao_complete(egc, cis->ao, rc);
-}
-
-/* libxl__alloc_vdev only works on the local domain, that is the domain
- * where the toolstack is running */
-static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
-        xs_transaction_t t)
-{
-    const char *blkdev_start = (const char *) get_vdev_user;
-    int devid = 0, disk = 0, part = 0;
-
-    libxl__device_disk_dev_number(blkdev_start, &disk, &part);
-    if (part != 0) {
-        LOG(ERROR, "blkdev_start is invalid");
-        return NULL;
-    }
-
-    do {
-        devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
-                NULL, NULL);
-        if (devid < 0)
-            return NULL;
-        if (libxl__xs_read(gc, t, GCSPRINTF("%s/backend",
-                           libxl__domain_device_libxl_path(gc,
-                           LIBXL_TOOLSTACK_DOMID, devid,
-                           LIBXL__DEVICE_KIND_VBD))) == NULL) {
-            if (errno == ENOENT)
-                return libxl__devid_to_vdev(gc, devid);
-            else
-                return NULL;
-        }
-        disk++;
-    } while (1);
-    return NULL;
-}
-
-/* Callbacks */
-
-char *libxl__device_disk_find_local_path(libxl__gc *gc,
-                                          libxl_domid guest_domid,
-                                          const libxl_device_disk *disk,
-                                          bool qdisk_direct)
-{
-    char *path = NULL;
-
-    /* No local paths for driver domains */
-    if (disk->backend_domname != NULL) {
-        LOG(DEBUG, "Non-local backend, can't access locally.\n");
-        goto out;
-    }
-
-    /*
-     * If this is in raw format, and we're not using a script or a
-     * driver domain, we can access the target path directly.
-     */
-    if (disk->format == LIBXL_DISK_FORMAT_RAW
-        && disk->script == NULL) {
-        path = libxl__strdup(gc, disk->pdev_path);
-        LOG(DEBUG, "Directly accessing local RAW disk %s", path);
-        goto out;
-    }
-
-    /*
-     * If we're being called for a qemu path, we can pass the target
-     * string directly as well
-     */
-    if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
-        path = libxl__strdup(gc, disk->pdev_path);
-        LOG(DEBUG, "Directly accessing local QDISK target %s", path);
-        goto out;
-    }
-
-    /*
-     * If the format isn't raw and / or we're using a script, then see
-     * if the script has written a path to the "cooked" node
-     */
-    if (disk->script && guest_domid != INVALID_DOMID) {
-        libxl__device device;
-        char *be_path, *pdpath;
-        int rc;
-
-        LOGD(DEBUG, guest_domid,
-             "Run from a script; checking for physical-device-path (vdev %s)",
-             disk->vdev);
-
-        rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
-        if (rc < 0)
-            goto out;
-
-        be_path = libxl__device_backend_path(gc, &device);
-
-        pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
-
-        LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
-        path = libxl__xs_read(gc, XBT_NULL, pdpath);
-
-        if (path)
-            LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
-        else
-            LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
-
-        goto out;
-    }
-
- out:
-    return path;
-}
-
-static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
-
-void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
-                                     libxl__disk_local_state *dls)
-{
-    STATE_AO_GC(dls->ao);
-    int rc;
-    const libxl_device_disk *in_disk = dls->in_disk;
-    libxl_device_disk *disk = &dls->disk;
-    const char *blkdev_start = dls->blkdev_start;
-
-    assert(in_disk->pdev_path);
-
-    disk->vdev = NULL;
-
-    if (dls->diskpath)
-        LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
-
-    LOG(DEBUG, "Trying to find local path");
-
-    dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
-                                                       in_disk, false);
-    if (dls->diskpath) {
-        LOG(DEBUG, "Local path found, executing callback.");
-        dls->callback(egc, dls, 0);
-    } else {
-        LOG(DEBUG, "Local path not found, initiating attach.");
-
-        memcpy(disk, in_disk, sizeof(libxl_device_disk));
-        disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
-        if (in_disk->script != NULL)
-            disk->script = libxl__strdup(gc, in_disk->script);
-        disk->vdev = NULL;
-
-        rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
-                                           false);
-        if (rc) goto out;
-
-        libxl__prepare_ao_device(ao, &dls->aodev);
-        dls->aodev.callback = local_device_attach_cb;
-        device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
-                        libxl__alloc_vdev, (void *) blkdev_start);
-    }
-
-    return;
-
- out:
-    assert(rc);
-    dls->rc = rc;
-    libxl__device_disk_local_initiate_detach(egc, dls);
-    dls->callback(egc, dls, rc);
-}
-
-static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
-    char *be_path = NULL;
-    int rc;
-    libxl__device device;
-    libxl_device_disk *disk = &dls->disk;
-
-    rc = aodev->rc;
-    if (rc) {
-        LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
-        goto out;
-    }
-
-    rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
-    if (rc < 0)
-        goto out;
-    be_path = libxl__device_backend_path(gc, &device);
-    rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
-    if (rc < 0)
-        goto out;
-
-    dls->diskpath = GCSPRINTF("/dev/%s",
-                              libxl__devid_to_localdev(gc, device.devid));
-    LOG(DEBUG, "locally attached disk %s", dls->diskpath);
-
-    dls->callback(egc, dls, 0);
-    return;
-
- out:
-    assert(rc);
-    dls->rc = rc;
-    libxl__device_disk_local_initiate_detach(egc, dls);
-    return;
-}
-
-/* Callbacks for local detach */
-
-static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
-
-void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
-                                     libxl__disk_local_state *dls)
-{
-    STATE_AO_GC(dls->ao);
-    int rc = 0;
-    libxl_device_disk *disk = &dls->disk;
-    libxl__device *device;
-    libxl__ao_device *aodev = &dls->aodev;
-    libxl__prepare_ao_device(ao, aodev);
-
-    if (!dls->diskpath) goto out;
-
-    if (disk->vdev != NULL) {
-        GCNEW(device);
-        rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
-                                     disk, device);
-        if (rc != 0) goto out;
-
-        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-        aodev->dev = device;
-        aodev->callback = local_device_detach_cb;
-        aodev->force.flag = LIBXL__FORCE_AUTO;
-        libxl__initiate_device_generic_remove(egc, aodev);
-        return;
-    }
-
-out:
-    aodev->rc = rc;
-    local_device_detach_cb(egc, aodev);
-    return;
-}
-
-static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
-    int rc;
-
-    if (aodev->rc) {
-        LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
-                     libxl__device_action_to_string(aodev->action),
-                     libxl__device_kind_to_string(aodev->dev->kind),
-                     aodev->dev->devid);
-        goto out;
-    }
-
-out:
-    /*
-     * If there was an error in dls->rc, it means we have been called from
-     * a failed execution of libxl__device_disk_local_initiate_attach,
-     * so return the original error.
-     */
-    rc = dls->rc ? dls->rc : aodev->rc;
-    dls->callback(egc, dls, rc);
-    return;
-}
-
-/* The following functions are defined:
- * libxl_device_disk_add
- * libxl__add_disks
- * libxl_device_disk_remove
- * libxl_device_disk_destroy
- * libxl_device_disk_safe_remove
- */
-LIBXL_DEFINE_DEVICE_ADD(disk)
-LIBXL_DEFINE_DEVICES_ADD(disk)
-LIBXL_DEFINE_DEVICE_REMOVE(disk)
-LIBXL_DEFINE_DEVICE_SAFE_REMOVE(disk)
-
-static int libxl_device_disk_compare(const libxl_device_disk *d1,
-                                     const libxl_device_disk *d2)
-{
-    return COMPARE_DISK(d1, d2);
-}
-
-/* Take care of removable device. We maintain invariant in the
- * insert / remove operation so that:
- * 1. if xenstore is "empty" while JSON is not, the result
- *    is "empty"
- * 2. if xenstore has a different media than JSON, use the
- *    one in JSON
- * 3. if xenstore and JSON have the same media, well, you
- *    know the answer :-)
- *
- * Currently there is only one removable device -- CDROM.
- * Look for libxl_cdrom_insert for reference.
- */
-static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
-{
-    GC_INIT(ctx);
-    libxl_device_disk *src = d1;
-    libxl_device_disk *dst = d2;
-
-    if (src->removable) {
-        if (!src->pdev_path || *src->pdev_path == '\0') {
-            /* 1, no media in drive */
-            free(dst->pdev_path);
-            dst->pdev_path = libxl__strdup(NOGC, "");
-            dst->format = LIBXL_DISK_FORMAT_EMPTY;
-        } else {
-            /* 2 and 3, use JSON, no need to touch anything */
-            ;
-        }
-    }
-}
-
-static int libxl_device_disk_dm_needed(void *e, unsigned domid)
-{
-    libxl_device_disk *elem = e;
-
-    return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
-           elem->backend_domid == domid;
-}
-
-LIBXL_DEFINE_DEVICE_LIST(disk)
-
-#define libxl__device_disk_update_devid NULL
-
-DEFINE_DEVICE_TYPE_STRUCT(disk, VBD,
-    .merge       = libxl_device_disk_merge,
-    .dm_needed   = libxl_device_disk_dm_needed,
-    .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
-    .skip_attach = 1,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_dm.c b/tools/libxl/libxl_dm.c
deleted file mode 100644 (file)
index fec4e0f..0000000
+++ /dev/null
@@ -1,3795 +0,0 @@
-/*
- * Copyright (C) 2010      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- * Author Gianni Tedesco <gianni.tedesco@citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <xenctrl_dom.h>
-#include <xen/hvm/e820.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-
-static const char *libxl_tapif_script(libxl__gc *gc)
-{
-#if defined(__linux__) || defined(__FreeBSD__)
-    return libxl__strdup(gc, "no");
-#else
-    return GCSPRINTF("%s/qemu-ifup", libxl__xen_script_dir_path());
-#endif
-}
-
-const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid)
-{
-    return GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", domid);
-}
-
-static const char *qemu_xen_path(libxl__gc *gc)
-{
-    return QEMU_XEN_PATH;
-}
-
-static int libxl__create_qemu_logfile(libxl__gc *gc, char *name)
-{
-    char *logfile;
-    int rc, logfile_w;
-
-    rc = libxl_create_logfile(CTX, name, &logfile);
-    if (rc) return rc;
-
-    logfile_w = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
-
-    if (logfile_w < 0) {
-        LOGE(ERROR, "unable to open Qemu logfile: %s", logfile);
-        free(logfile);
-        return ERROR_FAIL;
-    }
-
-    free(logfile);
-
-    return logfile_w;
-}
-
-/*
- *  userlookup_helper_getpwnam(libxl__gc*, const char *user,
- *                             struct passwd **pwd_r);
- *
- *  userlookup_helper_getpwuid(libxl__gc*, uid_t uid,
- *                             struct passwd **pwd_r);
- *
- *  If the user is found, return 0 and set *pwd_r to the appropriat
- *  value.
- *
- *  If the user is not found but there are no errors, return 0
- *  and set *pwd_r to NULL.
- *
- *  On error, return a libxl-style error code.
- */
-#define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF)     \
-    static int userlookup_helper_##NAME(libxl__gc *gc,                  \
-                                        SPEC_TYPE spec,                 \
-                                        struct STRUCTNAME *resultbuf,   \
-                                        struct STRUCTNAME **out)        \
-    {                                                                   \
-        struct STRUCTNAME *resultp = NULL;                              \
-        char *buf = NULL;                                               \
-        long buf_size;                                                  \
-        int r;                                                          \
-                                                                        \
-        buf_size = sysconf(SYSCONF);                                    \
-        if (buf_size < 0) {                                             \
-            buf_size = 2048;                                            \
-            LOG(DEBUG,                                                  \
-    "sysconf failed, setting the initial buffer size to %ld",           \
-                buf_size);                                              \
-        }                                                               \
-                                                                        \
-        while (1) {                                                     \
-            buf = libxl__realloc(gc, buf, buf_size);                    \
-            r = NAME##_r(spec, resultbuf, buf, buf_size, &resultp);     \
-            if (r == ERANGE) {                                          \
-                buf_size += 128;                                        \
-                continue;                                               \
-            }                                                           \
-            if (r != 0) {                                               \
-                LOGEV(ERROR, r, "Looking up username/uid with " #NAME); \
-                return ERROR_FAIL;                                      \
-            }                                                           \
-            *out = resultp;                                             \
-            return 0;                                                   \
-        }                                                               \
-    }
-
-DEFINE_USERLOOKUP_HELPER(getpwnam, const char*, passwd, _SC_GETPW_R_SIZE_MAX);
-DEFINE_USERLOOKUP_HELPER(getpwuid, uid_t,       passwd, _SC_GETPW_R_SIZE_MAX);
-
-static int libxl__domain_get_device_model_uid(libxl__gc *gc,
-                                              libxl__dm_spawn_state *dmss)
-{
-    int guest_domid = dmss->guest_domid;
-    libxl__domain_build_state *const state = dmss->build_state;
-    const libxl_domain_build_info *b_info = &dmss->guest_config->b_info;
-
-    struct passwd *user_base, user_pwbuf;
-    int rc;
-    char *user;
-    uid_t intended_uid = -1;
-    bool kill_by_uid;
-
-    /* Only qemu-upstream can run as a different uid */
-    if (b_info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN)
-        return 0;
-
-    /*
-     * From this point onward, all paths should go through the `out`
-     * label.  The invariants should be:
-     * - rc may be 0, or an error code.
-     * - if rc is an error code, user and intended_uid are ignored.
-     * - if rc is 0, user may be set or not set.
-     * - if user is set, then intended_uid must be set to a UID matching
-     *   the username `user`, and kill_by_uid must be set to the appropriate
-     *   value.  intended_uid will be checked for root (0).
-     */
-    
-    /*
-     * If device_model_user is present, set `-runas` even if
-     * dm_restrict isn't in use
-     */
-    user = b_info->device_model_user;
-    if (user) {
-        rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
-        if (rc)
-            goto out;
-
-        if (!user_base) {
-            LOGD(ERROR, guest_domid, "Couldn't find device_model_user %s",
-                 user);
-            rc = ERROR_INVAL;
-            goto out;
-        }
-
-        intended_uid = user_base->pw_uid;
-        kill_by_uid = true;
-        rc = 0;
-        goto out;
-    }
-
-    /*
-     * If dm_restrict isn't set, and we don't have a specified user, don't
-     * bother setting a `-runas` parameter.
-     */
-    if (!libxl_defbool_val(b_info->dm_restrict)) {
-        LOGD(DEBUG, guest_domid,
-             "dm_restrict disabled, starting QEMU as root");
-        user = NULL; /* Should already be null, but just in case */
-        kill_by_uid = false; /* Keep older versions of gcc happy */
-        rc = 0;
-        goto out;
-    }
-
-    /*
-     * dm_restrict is set, but device_model_user isn't set; look for
-     * QEMU_USER_BASE_RANGE
-     */
-    rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
-                                         &user_pwbuf, &user_base);
-    if (rc)
-        goto out;
-    if (user_base) {
-        struct passwd *user_clash, user_clash_pwbuf;
-
-        intended_uid = user_base->pw_uid + guest_domid;
-        rc = userlookup_helper_getpwuid(gc, intended_uid,
-                                         &user_clash_pwbuf, &user_clash);
-        if (rc)
-            goto out;
-        if (user_clash) {
-            LOGD(ERROR, guest_domid,
-                 "wanted to use uid %ld (%s + %d) but that is user %s !",
-                 (long)intended_uid, LIBXL_QEMU_USER_RANGE_BASE,
-                 guest_domid, user_clash->pw_name);
-            rc = ERROR_INVAL;
-            goto out;
-        }
-
-        LOGD(DEBUG, guest_domid, "using uid %ld", (long)intended_uid);
-        user = GCSPRINTF("%ld:%ld", (long)intended_uid,
-                         (long)user_base->pw_gid);
-        kill_by_uid = true;
-        rc = 0;
-        goto out;
-    }
-
-    /*
-     * We couldn't find QEMU_USER_BASE_RANGE; look for
-     * QEMU_USER_SHARED.  NB for QEMU_USER_SHARED, all QEMU will run
-     * as the same UID, we can't kill by uid; therefore don't set uid.
-     */
-    user = LIBXL_QEMU_USER_SHARED;
-    rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
-    if (rc)
-        goto out;
-    if (user_base) {
-        LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s",
-             LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED);
-        intended_uid = user_base->pw_uid;
-        kill_by_uid = false;
-        rc = 0;
-        goto out;
-    }
-
-    /*
-     * dm_depriv is set, but we can't find a non-root uid to run as;
-     * fail domain creation
-     */
-    LOGD(ERROR, guest_domid,
-         "Could not find user %s or range base pseudo-user %s, cannot restrict",
-         LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE);
-    rc = ERROR_INVAL;
-
-out:
-    /* First, do a root check if appropriate */
-    if (!rc) {
-        if (user && intended_uid == 0) {
-            LOGD(ERROR, guest_domid, "intended_uid is 0 (root)!");
-            rc = ERROR_INVAL;
-        }
-    }
-
-    /* Then do the final set, if still appropriate */
-    if (!rc && user) {
-        state->dm_runas = user;
-        if (kill_by_uid)
-            state->dm_kill_uid = GCSPRINTF("%ld", (long)intended_uid);
-    }
-
-    return rc;
-}
-
-/*
- * Look up "reaper UID".  If present and non-root, returns 0 and sets
- * reaper_uid.  Otherwise returns libxl-style error.
- */
-static int libxl__get_reaper_uid(libxl__gc *gc, uid_t *reaper_uid)
-{
-    struct passwd *user_base, user_pwbuf;
-    int rc;
-
-    rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_REAPER,
-                                         &user_pwbuf, &user_base);
-    /* 
-     * Either there was an error, or we found a suitable user; stop
-     * looking
-     */
-    if (rc || user_base)
-        goto out;
-
-    rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
-                                         &user_pwbuf, &user_base);
-    if (rc || user_base)
-        goto out;
-
-    LOG(WARN, "Couldn't find uid for reaper process");
-    rc = ERROR_INVAL;
-
- out:
-    /* First check to see if the discovered user maps to root */
-    if (!rc) {
-        if (user_base->pw_uid == 0) {
-            LOG(ERROR, "UID for reaper process maps to root!");
-            rc = ERROR_INVAL;
-        }
-    }
-
-    /* If everything is OK, set reaper_uid as appropriate */
-    if (!rc)
-        *reaper_uid = user_base->pw_uid;
-
-    return rc;
-}
-
-const char *libxl__domain_device_model(libxl__gc *gc,
-                                       const libxl_domain_build_info *info)
-{
-    const char *dm;
-
-    if (libxl_defbool_val(info->device_model_stubdomain))
-        return NULL;
-
-    if (info->device_model) {
-        dm = libxl__strdup(gc, info->device_model);
-    } else {
-        switch (info->device_model_version) {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            dm = libxl__abs_path(gc, "qemu-dm", libxl__private_bindir_path());
-            break;
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            dm = qemu_xen_path(gc);
-            break;
-        default:
-            LOG(ERROR, "invalid device model version %d",
-                info->device_model_version);
-            dm = NULL;
-            break;
-        }
-    }
-    return dm;
-}
-
-static int
-libxl__xc_device_get_rdm(libxl__gc *gc,
-                         uint32_t flags,
-                         uint16_t seg,
-                         uint8_t bus,
-                         uint8_t devfn,
-                         unsigned int *nr_entries,
-                         struct xen_reserved_device_memory **xrdm)
-{
-    int rc = 0, r;
-
-    /*
-     * We really can't presume how many entries we can get in advance.
-     */
-    *nr_entries = 0;
-    r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
-                                      NULL, nr_entries);
-    assert(r <= 0);
-    /* "0" means we have no any rdm entry. */
-    if (!r) goto out;
-
-    if (errno != ENOBUFS) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    GCNEW_ARRAY(*xrdm, *nr_entries);
-    r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn,
-                                      *xrdm, nr_entries);
-    if (r)
-        rc = ERROR_FAIL;
-
- out:
-    if (rc) {
-        *nr_entries = 0;
-        *xrdm = NULL;
-        LOG(ERROR, "Could not get reserved device memory maps.");
-    }
-    return rc;
-}
-
-/*
- * Check whether there exists rdm hole in the specified memory range.
- * Returns true if exists, else returns false.
- */
-static bool overlaps_rdm(uint64_t start, uint64_t memsize,
-                         uint64_t rdm_start, uint64_t rdm_size)
-{
-    return (start + memsize > rdm_start) && (start < rdm_start + rdm_size);
-}
-
-static void
-add_rdm_entry(libxl__gc *gc, libxl_domain_config *d_config,
-              uint64_t rdm_start, uint64_t rdm_size, int rdm_policy)
-{
-    d_config->rdms = libxl__realloc(NOGC, d_config->rdms,
-                    (d_config->num_rdms+1) * sizeof(libxl_device_rdm));
-
-    d_config->rdms[d_config->num_rdms].start = rdm_start;
-    d_config->rdms[d_config->num_rdms].size = rdm_size;
-    d_config->rdms[d_config->num_rdms].policy = rdm_policy;
-    d_config->num_rdms++;
-}
-
-/*
- * Check reported RDM regions and handle potential gfn conflicts according
- * to user preferred policy.
- *
- * RDM can reside in address space beyond 4G theoretically, but we never
- * see this in real world. So in order to avoid breaking highmem layout
- * we don't solve highmem conflict. Note this means highmem rmrr could
- * still be supported if no conflict.
- *
- * But in the case of lowmem, RDM probably scatter the whole RAM space.
- * Especially multiple RDM entries would worsen this to lead a complicated
- * memory layout. And then its hard to extend hvm_info_table{} to work
- * hvmloader out. So here we're trying to figure out a simple solution to
- * avoid breaking existing layout. So when a conflict occurs,
- *
- * #1. Above a predefined boundary (default 2G)
- * - Move lowmem_end below reserved region to solve conflict;
- *
- * #2. Below a predefined boundary (default 2G)
- * - Check strict/relaxed policy.
- * "strict" policy leads to fail libxl.
- * "relaxed" policy issue a warning message and also mask this entry
- * INVALID to indicate we shouldn't expose this entry to hvmloader.
- * Note when both policies are specified on a given region, the per-device
- * policy should override the global policy.
- */
-int libxl__domain_device_construct_rdm(libxl__gc *gc,
-                                       libxl_domain_config *d_config,
-                                       uint64_t rdm_mem_boundary,
-                                       struct xc_dom_image *dom)
-{
-    int i, j, conflict, rc;
-    struct xen_reserved_device_memory *xrdm = NULL;
-    uint32_t strategy = d_config->b_info.u.hvm.rdm.strategy;
-    uint16_t seg;
-    uint8_t bus, devfn;
-    uint64_t rdm_start, rdm_size;
-    uint64_t highmem_end = dom->highmem_end;
-
-    /*
-     * We just want to construct RDM once since RDM is specific to the
-     * given platform, so this shouldn't change again.
-     */
-    if (d_config->num_rdms)
-        return 0;
-
-    /* Might not expose rdm. */
-    if (strategy == LIBXL_RDM_RESERVE_STRATEGY_IGNORE &&
-        !d_config->num_pcidevs)
-        return 0;
-
-    /* Query all RDM entries in this platform */
-    if (strategy == LIBXL_RDM_RESERVE_STRATEGY_HOST) {
-        unsigned int nr_entries;
-
-        /* Collect all rdm info if exist. */
-        rc = libxl__xc_device_get_rdm(gc, XENMEM_RDM_ALL,
-                                      0, 0, 0, &nr_entries, &xrdm);
-        if (rc)
-            goto out;
-        if (!nr_entries)
-            return 0;
-
-        assert(xrdm);
-
-        for (i = 0; i < nr_entries; i++)
-        {
-            add_rdm_entry(gc, d_config,
-                          pfn_to_paddr(xrdm[i].start_pfn),
-                          pfn_to_paddr(xrdm[i].nr_pages),
-                          d_config->b_info.u.hvm.rdm.policy);
-        }
-    }
-
-    /* Query RDM entries per-device */
-    for (i = 0; i < d_config->num_pcidevs; i++) {
-        unsigned int n, nr_entries;
-
-        seg = d_config->pcidevs[i].domain;
-        bus = d_config->pcidevs[i].bus;
-        devfn = PCI_DEVFN(d_config->pcidevs[i].dev,
-                          d_config->pcidevs[i].func);
-        nr_entries = 0;
-        rc = libxl__xc_device_get_rdm(gc, 0,
-                                      seg, bus, devfn, &nr_entries, &xrdm);
-        if (rc)
-            goto out;
-        /* No RDM to associated with this device. */
-        if (!nr_entries)
-            continue;
-
-        assert(xrdm);
-
-        rc = libxl__device_pci_setdefault(gc, DOMID_INVALID,
-                                          &d_config->pcidevs[i], false);
-        if (rc)
-            goto out;
-
-        for (n = 0; n < nr_entries; ++n) {
-            bool new = true;
-
-            /*
-             * Need to check whether this entry is already saved in the
-             * array. This could come from two cases:
-             *
-             *   - user may configure to get all RDMs in this platform,
-             *   which is already queried before this point
-             *   - or two assigned devices may share one RDM entry
-             *
-             * Different policies may be configured on the same RDM due to
-             * above two cases. But we don't allow to assign such a group
-             * of devices right now so it doesn't come true in our case.
-             */
-            for (j = 0; j < d_config->num_rdms; j++) {
-                if (d_config->rdms[j].start
-                    == pfn_to_paddr(xrdm[n].start_pfn))
-                {
-                    /*
-                     * So the per-device policy always override the
-                     * global policy in this case.
-                     */
-                    d_config->rdms[j].policy
-                        = d_config->pcidevs[i].rdm_policy;
-                    new = false;
-                    break;
-                }
-            }
-
-            if (new)
-                add_rdm_entry(gc, d_config,
-                              pfn_to_paddr(xrdm[n].start_pfn),
-                              pfn_to_paddr(xrdm[n].nr_pages),
-                              d_config->pcidevs[i].rdm_policy);
-        }
-    }
-
-    /*
-     * Next step is to check and avoid potential conflict between RDM
-     * entries and guest RAM. To avoid intrusive impact to existing
-     * memory layout {lowmem, mmio, highmem} which is passed around
-     * various function blocks, below conflicts are not handled which
-     * are rare and handling them would lead to a more scattered
-     * layout:
-     *  - RDM  in highmem area (>4G)
-     *  - RDM lower than a defined memory boundary (e.g. 2G)
-     * Otherwise for conflicts between boundary and 4G, we'll simply
-     * move lowmem end below reserved region to solve conflict.
-     *
-     * If a conflict is detected on a given RDM entry, an error will
-     * be returned if 'strict' policy is specified. Instead, if
-     * 'relaxed' policy specified, this conflict is treated just as a
-     * warning, but we mark this RDM entry as INVALID to indicate that
-     * this entry shouldn't be exposed to hvmloader.
-     *
-     * Firstly we should check the case of rdm < 4G because we may
-     * need to expand highmem_end.
-     */
-    for (i = 0; i < d_config->num_rdms; i++) {
-        rdm_start = d_config->rdms[i].start;
-        rdm_size = d_config->rdms[i].size;
-        conflict = overlaps_rdm(0, dom->lowmem_end, rdm_start, rdm_size);
-
-        if (!conflict)
-            continue;
-
-        /* Just check if RDM > our memory boundary. */
-        if (rdm_start > rdm_mem_boundary) {
-            /*
-             * For HAP guests round down to a 2Mb boundary to allow use
-             * of large page mappings.
-             */
-            if (libxl_defbool_val(d_config->c_info.hap)
-                && rdm_start > MB(2))
-                rdm_start &= ~(MB(2) - 1);
-            /*
-             * We will move downwards lowmem_end so we have to expand
-             * highmem_end.
-             */
-            if (!highmem_end)
-                highmem_end = 1ull << 32;
-            highmem_end += (dom->lowmem_end - rdm_start);
-            /* Now move downwards lowmem_end. */
-            dom->lowmem_end = rdm_start;
-        }
-    }
-
-    /* Sync highmem_end. */
-    dom->highmem_end = highmem_end;
-
-    /*
-     * Finally we can take same policy to check lowmem(< 2G) and
-     * highmem adjusted above.
-     */
-    for (i = 0; i < d_config->num_rdms; i++) {
-        rdm_start = d_config->rdms[i].start;
-        rdm_size = d_config->rdms[i].size;
-        /* Does this entry conflict with lowmem? */
-        conflict = overlaps_rdm(0, dom->lowmem_end,
-                                rdm_start, rdm_size);
-        /* Does this entry conflict with highmem? */
-        if (highmem_end)
-            conflict |= overlaps_rdm((1ULL << 32),
-                                     highmem_end - (1ULL << 32),
-                                     rdm_start, rdm_size);
-
-        if (!conflict)
-            continue;
-
-        if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
-            LOG(ERROR, "RDM conflict at 0x%"PRIx64".\n",
-                       d_config->rdms[i].start);
-            goto out;
-        } else {
-            LOG(WARN, "Ignoring RDM conflict at 0x%"PRIx64".\n",
-                      d_config->rdms[i].start);
-
-            /*
-             * Then mask this INVALID to indicate we shouldn't expose this
-             * to hvmloader.
-             */
-            d_config->rdms[i].policy = LIBXL_RDM_RESERVE_POLICY_INVALID;
-        }
-    }
-
-    return 0;
-
- out:
-    return ERROR_FAIL;
-}
-
-/* XSA-180 / CVE-2014-3672
- *
- * The QEMU shipped with Xen has a bodge. It checks for
- * XEN_QEMU_CONSOLE_LIMIT to see how much data QEMU is allowed
- * to write to stderr. We set that to 1MB if it is not set by
- * system administrator.
- */
-static void libxl__set_qemu_env_for_xsa_180(libxl__gc *gc,
-                                            flexarray_t *dm_envs)
-{
-    if (getenv("XEN_QEMU_CONSOLE_LIMIT")) return;
-    flexarray_append_pair(dm_envs, "XEN_QEMU_CONSOLE_LIMIT", "1048576");
-}
-
-const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *guest_config)
-{
-    const libxl_vnc_info *vnc = NULL;
-    if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
-        vnc = &guest_config->b_info.u.hvm.vnc;
-    } else if (guest_config->num_vfbs > 0) {
-        vnc = &guest_config->vfbs[0].vnc;
-    }
-    return vnc && libxl_defbool_val(vnc->enable) ? vnc : NULL;
-}
-
-static const libxl_sdl_info *dm_sdl(const libxl_domain_config *guest_config)
-{
-    const libxl_sdl_info *sdl = NULL;
-    if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
-        sdl = &guest_config->b_info.u.hvm.sdl;
-    } else if (guest_config->num_vfbs > 0) {
-        sdl = &guest_config->vfbs[0].sdl;
-    }
-    return sdl && libxl_defbool_val(sdl->enable) ? sdl : NULL;
-}
-
-static const char *dm_keymap(const libxl_domain_config *guest_config)
-{
-    if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
-        return guest_config->b_info.u.hvm.keymap;
-    } else if (guest_config->num_vfbs > 0) {
-        return guest_config->vfbs[0].keymap;
-    } else
-        return NULL;
-}
-
-static int libxl__build_device_model_args_old(libxl__gc *gc,
-                                        const char *dm, int domid,
-                                        const libxl_domain_config *guest_config,
-                                        char ***args, char ***envs,
-                                        const libxl__domain_build_state *state)
-{
-    const libxl_domain_create_info *c_info = &guest_config->c_info;
-    const libxl_domain_build_info *b_info = &guest_config->b_info;
-    const libxl_device_nic *nics = guest_config->nics;
-    const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
-    const libxl_sdl_info *sdl = dm_sdl(guest_config);
-    const int num_nics = guest_config->num_nics;
-    const char *keymap = dm_keymap(guest_config);
-    int i;
-    flexarray_t *dm_args, *dm_envs;
-    dm_args = flexarray_make(gc, 16, 1);
-    dm_envs = flexarray_make(gc, 16, 1);
-
-    assert(state->dm_monitor_fd == -1);
-
-    libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
-
-    flexarray_vappend(dm_args, dm,
-                      "-d", GCSPRINTF("%d", domid), NULL);
-
-    if (c_info->name)
-        flexarray_vappend(dm_args, "-domain-name", c_info->name, NULL);
-
-    if (vnc) {
-        char *vncarg = NULL;
-
-        flexarray_append(dm_args, "-vnc");
-
-        /*
-         * If vnc->listen is present and contains a :, and
-         *  - vnc->display is 0, use vnc->listen
-         *  - vnc->display is non-zero, be confused
-         * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
-         * If vnc->listen is not present, use 127.0.0.1:vnc->display
-         * (Remembering that vnc->display already defaults to 0.)
-         */
-        if (vnc->listen) {
-            if (strchr(vnc->listen, ':') != NULL) {
-                if (vnc->display) {
-                    LOGD(ERROR, domid, "vncdisplay set, vnclisten contains display");
-                    return ERROR_INVAL;
-                }
-                vncarg = vnc->listen;
-            } else {
-                vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
-            }
-        } else
-            vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
-
-        if (vnc->passwd && vnc->passwd[0]) {
-            vncarg = GCSPRINTF("%s,password", vncarg);
-        }
-
-        flexarray_append(dm_args, vncarg);
-
-        if (libxl_defbool_val(vnc->findunused)) {
-            flexarray_append(dm_args, "-vncunused");
-        }
-    } else if (!sdl) {
-        /*
-         * VNC is not enabled by default by qemu-xen-traditional,
-         * however skipping -vnc causes SDL to be
-         * (unexpectedly) enabled by default. If undesired, disable graphics at
-         * all.
-         */
-        flexarray_append(dm_args, "-nographic");
-    }
-
-    if (sdl) {
-        flexarray_append(dm_args, "-sdl");
-        if (!libxl_defbool_val(sdl->opengl)) {
-            flexarray_append(dm_args, "-disable-opengl");
-        }
-        if (sdl->display)
-            flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
-        if (sdl->xauthority)
-            flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
-    }
-    if (keymap) {
-        flexarray_vappend(dm_args, "-k", keymap, NULL);
-    }
-    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        int ioemu_nics = 0;
-        int nr_set_cpus = 0;
-        char *s;
-
-        if (b_info->kernel) {
-            LOGD(ERROR, domid, "HVM direct kernel boot is not supported by "
-                 "qemu-xen-traditional");
-            return ERROR_INVAL;
-        }
-
-        if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
-            if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
-            {
-                LOGD(ERROR, domid, "Both serial and serial_list set");
-                return ERROR_INVAL;
-            }
-            if (b_info->u.hvm.serial) {
-                flexarray_vappend(dm_args,
-                                  "-serial", b_info->u.hvm.serial, NULL);
-            } else if (b_info->u.hvm.serial_list) {
-                char **p;
-                for (p = b_info->u.hvm.serial_list;
-                     *p;
-                     p++) {
-                    flexarray_vappend(dm_args,
-                                      "-serial",
-                                      *p, NULL);
-                }
-            }
-        }
-
-        if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
-            flexarray_append(dm_args, "-nographic");
-        }
-
-        if (b_info->video_memkb) {
-            flexarray_vappend(dm_args, "-videoram",
-                    GCSPRINTF("%d", libxl__sizekb_to_mb(b_info->video_memkb)),
-                    NULL);
-        }
-
-        switch (b_info->u.hvm.vga.kind) {
-        case LIBXL_VGA_INTERFACE_TYPE_STD:
-            flexarray_append(dm_args, "-std-vga");
-            break;
-        case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
-            break;
-        case LIBXL_VGA_INTERFACE_TYPE_NONE:
-            flexarray_append_pair(dm_args, "-vga", "none");
-            break;
-        case LIBXL_VGA_INTERFACE_TYPE_QXL:
-            break;
-        default:
-            LOGD(ERROR, domid, "Invalid emulated video card specified");
-            return ERROR_INVAL;
-        }
-
-        if (b_info->u.hvm.boot) {
-            flexarray_vappend(dm_args, "-boot", b_info->u.hvm.boot, NULL);
-        }
-        if (libxl_defbool_val(b_info->u.hvm.usb)
-            || b_info->u.hvm.usbdevice
-            || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
-            if (b_info->u.hvm.usbdevice
-                && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
-                LOGD(ERROR, domid, "Both usbdevice and usbdevice_list set");
-                return ERROR_INVAL;
-            }
-            flexarray_append(dm_args, "-usb");
-            if (b_info->u.hvm.usbdevice) {
-                flexarray_vappend(dm_args,
-                                  "-usbdevice", b_info->u.hvm.usbdevice, NULL);
-            } else if (b_info->u.hvm.usbdevice_list) {
-                char **p;
-                for (p = b_info->u.hvm.usbdevice_list;
-                     *p;
-                     p++) {
-                    flexarray_vappend(dm_args,
-                                      "-usbdevice",
-                                      *p, NULL);
-                }
-            }
-        }
-        if (b_info->u.hvm.soundhw) {
-            flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
-        }
-        if (libxl__acpi_defbool_val(b_info)) {
-            flexarray_append(dm_args, "-acpi");
-        }
-        if (b_info->max_vcpus > 1) {
-            flexarray_vappend(dm_args, "-vcpus",
-                              GCSPRINTF("%d", b_info->max_vcpus),
-                              NULL);
-        }
-
-        nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
-        s = libxl_bitmap_to_hex_string(CTX, &b_info->avail_vcpus);
-        flexarray_vappend(dm_args, "-vcpu_avail",
-                              GCSPRINTF("%s", s), NULL);
-        free(s);
-
-        for (i = 0; i < num_nics; i++) {
-            if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
-                char *smac = GCSPRINTF(
-                                   LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac));
-                const char *ifname = libxl__device_nic_devname(gc,
-                                                domid, nics[i].devid,
-                                                LIBXL_NIC_TYPE_VIF_IOEMU);
-                flexarray_vappend(dm_args,
-                                  "-net",
-                                  GCSPRINTF(
-                                      "nic,vlan=%d,macaddr=%s,model=%s",
-                                      nics[i].devid, smac, nics[i].model),
-                                  "-net",
-                                  GCSPRINTF(
-                                      "tap,vlan=%d,ifname=%s,bridge=%s,"
-                                      "script=%s,downscript=%s",
-                                      nics[i].devid, ifname, nics[i].bridge,
-                                      libxl_tapif_script(gc),
-                                      libxl_tapif_script(gc)),
-                                  NULL);
-                ioemu_nics++;
-            }
-        }
-        /* If we have no emulated nics, tell qemu not to create any */
-        if ( ioemu_nics == 0 ) {
-            flexarray_vappend(dm_args, "-net", "none", NULL);
-        }
-        if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
-            switch (b_info->u.hvm.gfx_passthru_kind) {
-            case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
-            case LIBXL_GFX_PASSTHRU_KIND_IGD:
-                flexarray_append(dm_args, "-gfx_passthru");
-                break;
-            default:
-                LOGD(ERROR, domid, "unsupported gfx_passthru_kind.");
-                return ERROR_INVAL;
-            }
-        }
-    } else {
-        if (!sdl && !vnc)
-            flexarray_append(dm_args, "-nographic");
-    }
-
-    if (libxl_defbool_val(b_info->dm_restrict)) {
-        LOGD(ERROR, domid,
-             "dm_restrict not supported by qemu-xen-traditional");
-        return ERROR_INVAL;
-    }
-
-    if (state->saved_state) {
-        flexarray_vappend(dm_args, "-loadvm", state->saved_state, NULL);
-    }
-    for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
-        flexarray_append(dm_args, b_info->extra[i]);
-    flexarray_append(dm_args, "-M");
-    switch (b_info->type) {
-    case LIBXL_DOMAIN_TYPE_PVH:
-    case LIBXL_DOMAIN_TYPE_PV:
-        flexarray_append(dm_args, "xenpv");
-        for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
-            flexarray_append(dm_args, b_info->extra_pv[i]);
-        break;
-    case LIBXL_DOMAIN_TYPE_HVM:
-        flexarray_append(dm_args, "xenfv");
-        for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
-            flexarray_append(dm_args, b_info->extra_hvm[i]);
-        break;
-    default:
-        abort();
-    }
-    flexarray_append(dm_args, NULL);
-    *args = (char **) flexarray_contents(dm_args);
-    flexarray_append(dm_envs, NULL);
-    if (envs)
-        *envs = (char **) flexarray_contents(dm_envs);
-    return 0;
-}
-
-static char *dm_spice_options(libxl__gc *gc,
-                                    const libxl_spice_info *spice)
-{
-    char *opt;
-
-    if (!spice->port && !spice->tls_port) {
-        LOG(ERROR,
-            "at least one of the spiceport or tls_port must be provided");
-        return NULL;
-    }
-
-    if (!libxl_defbool_val(spice->disable_ticketing)) {
-        if (!spice->passwd) {
-            LOG(ERROR, "spice ticketing is enabled but missing password");
-            return NULL;
-        }
-        else if (!spice->passwd[0]) {
-            LOG(ERROR, "spice password can't be empty");
-            return NULL;
-        }
-    }
-    opt = GCSPRINTF("port=%d,tls-port=%d", spice->port, spice->tls_port);
-    if (spice->host)
-        opt = GCSPRINTF("%s,addr=%s", opt, spice->host);
-    if (libxl_defbool_val(spice->disable_ticketing))
-        opt = GCSPRINTF("%s,disable-ticketing", opt);
-    else
-        opt = GCSPRINTF("%s,password=%s", opt, spice->passwd);
-    opt = GCSPRINTF("%s,agent-mouse=%s", opt,
-                    libxl_defbool_val(spice->agent_mouse) ? "on" : "off");
-
-    if (!libxl_defbool_val(spice->clipboard_sharing))
-        opt = GCSPRINTF("%s,disable-copy-paste", opt);
-
-    if (spice->image_compression)
-        opt = GCSPRINTF("%s,image-compression=%s", opt,
-                        spice->image_compression);
-
-    if (spice->streaming_video)
-        opt = GCSPRINTF("%s,streaming-video=%s", opt, spice->streaming_video);
-
-    return opt;
-}
-
-static enum libxl_gfx_passthru_kind
-libxl__detect_gfx_passthru_kind(libxl__gc *gc,
-                                const libxl_domain_config *guest_config)
-{
-    const libxl_domain_build_info *b_info = &guest_config->b_info;
-
-    if (b_info->u.hvm.gfx_passthru_kind != LIBXL_GFX_PASSTHRU_KIND_DEFAULT)
-        return b_info->u.hvm.gfx_passthru_kind;
-
-    if (libxl__is_igd_vga_passthru(gc, guest_config)) {
-        return LIBXL_GFX_PASSTHRU_KIND_IGD;
-    }
-
-    return LIBXL_GFX_PASSTHRU_KIND_DEFAULT;
-}
-
-/* colo mode */
-enum {
-    LIBXL__COLO_NONE = 0,
-    LIBXL__COLO_PRIMARY,
-    LIBXL__COLO_SECONDARY,
-};
-
-static char *qemu_disk_scsi_drive_string(libxl__gc *gc, const char *target_path,
-                                         int unit, const char *format,
-                                         const libxl_device_disk *disk,
-                                         int colo_mode, const char **id_ptr)
-{
-    char *drive = NULL;
-    char *common = GCSPRINTF("if=none,readonly=%s,cache=writeback",
-                             disk->readwrite ? "off" : "on");
-    const char *exportname = disk->colo_export;
-    const char *active_disk = disk->active_disk;
-    const char *hidden_disk = disk->hidden_disk;
-    const char *id;
-
-    switch (colo_mode) {
-    case LIBXL__COLO_NONE:
-        id = GCSPRINTF("scsi0-hd%d", unit);
-        drive = GCSPRINTF("file=%s,id=%s,format=%s,%s",
-                          target_path, id, format, common);
-        break;
-    case LIBXL__COLO_PRIMARY:
-        id = exportname;
-        drive = GCSPRINTF(
-            "%s,id=%s,driver=quorum,"
-            "children.0.file.filename=%s,"
-            "children.0.driver=%s,"
-            "read-pattern=fifo,"
-            "vote-threshold=1",
-            common, id, target_path, format);
-        break;
-    case LIBXL__COLO_SECONDARY:
-        id = "top-colo";
-        drive = GCSPRINTF(
-            "%s,id=%s,driver=replication,"
-            "mode=secondary,"
-            "top-id=top-colo,"
-            "file.driver=qcow2,"
-            "file.file.filename=%s,"
-            "file.backing.driver=qcow2,"
-            "file.backing.file.filename=%s,"
-            "file.backing.backing=%s",
-            common, id, active_disk, hidden_disk, exportname);
-        break;
-    default:
-        abort();
-    }
-
-    *id_ptr = id;
-
-    return drive;
-}
-
-static char *qemu_disk_ide_drive_string(libxl__gc *gc, const char *target_path,
-                                        int unit, const char *format,
-                                        const libxl_device_disk *disk,
-                                        int colo_mode)
-{
-    char *drive = NULL;
-    const char *exportname = disk->colo_export;
-    const char *active_disk = disk->active_disk;
-    const char *hidden_disk = disk->hidden_disk;
-
-    assert(disk->readwrite); /* should have been checked earlier */
-
-    switch (colo_mode) {
-    case LIBXL__COLO_NONE:
-        drive = GCSPRINTF
-            ("file=%s,if=ide,index=%d,media=disk,format=%s,cache=writeback",
-             target_path, unit, format);
-        break;
-    case LIBXL__COLO_PRIMARY:
-        /*
-         * primary:
-         *  -dirve if=ide,index=x,media=disk,cache=writeback,driver=quorum,\
-         *  id=exportname,\
-         *  children.0.file.filename=target_path,\
-         *  children.0.driver=format,\
-         *  read-pattern=fifo,\
-         *  vote-threshold=1
-         */
-        drive = GCSPRINTF(
-            "if=ide,index=%d,media=disk,cache=writeback,driver=quorum,"
-            "id=%s,"
-            "children.0.file.filename=%s,"
-            "children.0.driver=%s,"
-            "read-pattern=fifo,"
-            "vote-threshold=1",
-             unit, exportname, target_path, format);
-        break;
-    case LIBXL__COLO_SECONDARY:
-        /*
-         * secondary:
-         *  -drive if=ide,index=x,media=disk,cache=writeback,driver=replication,\
-         *  mode=secondary,\
-         *  file.driver=qcow2,\
-         *  file.file.filename=active_disk,\
-         *  file.backing.driver=qcow2,\
-         *  file.backing.file.filename=hidden_disk,\
-         *  file.backing.backing=exportname,
-         */
-        drive = GCSPRINTF(
-            "if=ide,index=%d,id=top-colo,media=disk,cache=writeback,"
-            "driver=replication,"
-            "mode=secondary,"
-            "top-id=top-colo,"
-            "file.driver=qcow2,"
-            "file.file.filename=%s,"
-            "file.backing.driver=qcow2,"
-            "file.backing.file.filename=%s,"
-            "file.backing.backing=%s",
-            unit, active_disk, hidden_disk, exportname);
-        break;
-    default:
-         abort();
-    }
-
-    return drive;
-}
-
-static int libxl__pre_open_qmp_socket(libxl__gc *gc, libxl_domid domid,
-                                      int *fd_r)
-{
-    int rc, r;
-    int fd;
-    struct sockaddr_un un;
-    const char *path = libxl__qemu_qmp_path(gc, domid);
-
-    fd = socket(AF_UNIX, SOCK_STREAM, 0);
-    if (fd < 0) {
-        LOGED(ERROR, domid, "socket() failed");
-        return ERROR_FAIL;
-    }
-
-    rc = libxl__prepare_sockaddr_un(gc, &un, path, "QEMU's QMP socket");
-    if (rc)
-        goto out;
-
-    rc = libxl__remove_file(gc, path);
-    if (rc)
-        goto out;
-
-    r = bind(fd, (struct sockaddr *) &un, sizeof(un));
-    if (r < 0) {
-        LOGED(ERROR, domid, "bind('%s') failed", path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    r = listen(fd, 1);
-    if (r < 0) {
-        LOGED(ERROR, domid, "listen() failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    *fd_r = fd;
-    rc = 0;
-
-out:
-    if (rc && fd >= 0)
-        close(fd);
-    return rc;
-}
-
-static int libxl__build_device_model_args_new(libxl__gc *gc,
-                                        const char *dm, int guest_domid,
-                                        const libxl_domain_config *guest_config,
-                                        char ***args, char ***envs,
-                                        const libxl__domain_build_state *state,
-                                        int *dm_state_fd)
-{
-    const libxl_domain_create_info *c_info = &guest_config->c_info;
-    const libxl_domain_build_info *b_info = &guest_config->b_info;
-    const libxl_device_disk *disks = guest_config->disks;
-    const libxl_device_nic *nics = guest_config->nics;
-    const int num_disks = guest_config->num_disks;
-    const int num_nics = guest_config->num_nics;
-    const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
-    const libxl_sdl_info *sdl = dm_sdl(guest_config);
-    const char *keymap = dm_keymap(guest_config);
-    char *machinearg;
-    flexarray_t *dm_args, *dm_envs;
-    int i, connection, devid;
-    uint64_t ram_size;
-    const char *path, *chardev;
-    bool is_stubdom = libxl_defbool_val(b_info->device_model_stubdomain);
-
-    dm_args = flexarray_make(gc, 16, 1);
-    dm_envs = flexarray_make(gc, 16, 1);
-
-    libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
-
-    flexarray_vappend(dm_args, dm,
-                      "-xen-domid",
-                      GCSPRINTF("%d", guest_domid), NULL);
-    flexarray_append(dm_args, "-no-shutdown");
-
-    /*
-     * QMP access to qemu running in stubdomain is done over vchan. The
-     * stubdomain init script adds the appropriate monitor options for
-     * vchan-socket-proxy.
-     */
-    if (!is_stubdom) {
-        flexarray_append(dm_args, "-chardev");
-        if (state->dm_monitor_fd >= 0) {
-            flexarray_append(dm_args,
-                GCSPRINTF("socket,id=libxl-cmd,fd=%d,server,nowait",
-                          state->dm_monitor_fd));
-
-            /*
-             * Start QEMU with its "CPU" paused, it will not start any emulation
-             * until the QMP command "cont" is used. This also prevent QEMU from
-             * writing "running" to the "state" xenstore node so we only use this
-             * flag when we have the QMP based startup notification.
-             * */
-            flexarray_append(dm_args, "-S");
-        } else {
-            flexarray_append(dm_args,
-                             GCSPRINTF("socket,id=libxl-cmd,"
-                                       "path=%s,server,nowait",
-                                       libxl__qemu_qmp_path(gc, guest_domid)));
-        }
-
-        flexarray_append(dm_args, "-mon");
-        flexarray_append(dm_args, "chardev=libxl-cmd,mode=control");
-
-        flexarray_append(dm_args, "-chardev");
-        flexarray_append(dm_args,
-                         GCSPRINTF("socket,id=libxenstat-cmd,"
-                                        "path=%s/qmp-libxenstat-%d,server,nowait",
-                                        libxl__run_dir_path(), guest_domid));
-
-        flexarray_append(dm_args, "-mon");
-        flexarray_append(dm_args, "chardev=libxenstat-cmd,mode=control");
-    }
-
-    for (i = 0; i < guest_config->num_channels; i++) {
-        connection = guest_config->channels[i].connection;
-        devid = guest_config->channels[i].devid;
-        switch (connection) {
-            case LIBXL_CHANNEL_CONNECTION_PTY:
-                chardev = GCSPRINTF("pty,id=libxl-channel%d", devid);
-                break;
-            case LIBXL_CHANNEL_CONNECTION_SOCKET:
-                path = guest_config->channels[i].u.socket.path;
-                chardev = GCSPRINTF("socket,id=libxl-channel%d,path=%s,"
-                                    "server,nowait", devid, path);
-                break;
-            default:
-                /* We've forgotten to add the clause */
-                LOGD(ERROR, guest_domid, "%s: unknown channel connection %d",
-                    __func__, connection);
-                return ERROR_INVAL;
-        }
-        flexarray_append(dm_args, "-chardev");
-        flexarray_append(dm_args, (void*)chardev);
-    }
-
-    /*
-     * Remove default devices created by qemu. Qemu will create only devices
-     * defined by xen, since the devices not defined by xen are not usable.
-     */
-    flexarray_append(dm_args, "-nodefaults");
-
-    /*
-     * Do not use any of the user-provided config files in sysconfdir,
-     * avoiding unknown and uncontrolled configuration.
-     */
-    flexarray_append(dm_args, "-no-user-config");
-
-    if (b_info->type != LIBXL_DOMAIN_TYPE_HVM) {
-        flexarray_append(dm_args, "-xen-attach");
-    }
-
-    if (c_info->name) {
-        flexarray_vappend(dm_args, "-name", c_info->name, NULL);
-    }
-
-    if (vnc && !is_stubdom) {
-        char *vncarg = NULL;
-
-        flexarray_append(dm_args, "-vnc");
-
-        /*
-         * If vnc->listen is present and contains a :, and
-         *  - vnc->display is 0, use vnc->listen
-         *  - vnc->display is non-zero, be confused
-         * If vnc->listen is present but doesn't, use vnc->listen:vnc->display.
-         * If vnc->listen is not present, use 127.0.0.1:vnc->display
-         * (Remembering that vnc->display already defaults to 0.)
-         */
-        if (vnc->listen) {
-            if (strchr(vnc->listen, ':') != NULL) {
-                if (vnc->display) {
-                    LOGD(ERROR, guest_domid,
-                         "vncdisplay set, vnclisten contains display");
-                    return ERROR_INVAL;
-                }
-                vncarg = vnc->listen;
-            } else {
-                vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display);
-            }
-        } else
-            vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display);
-
-        if (vnc->passwd && vnc->passwd[0]) {
-            vncarg = GCSPRINTF("%s,password", vncarg);
-        }
-
-        if (libxl_defbool_val(vnc->findunused)) {
-            /* This option asks to QEMU to try this number of port before to
-             * give up.  So QEMU will try ports between $display and $display +
-             * 99.  This option needs to be the last one of the vnc options. */
-            vncarg = GCSPRINTF("%s,to=99", vncarg);
-        }
-
-        flexarray_append(dm_args, vncarg);
-    } else if (!is_stubdom) {
-        /*
-         * Ensure that by default no vnc server is created.
-         */
-        flexarray_append_pair(dm_args, "-vnc", "none");
-    }
-
-    /*
-     * Ensure that by default no display backend is created. Further
-     * options given below might then enable more.
-     */
-    flexarray_append_pair(dm_args, "-display", "none");
-
-    if (sdl && !is_stubdom) {
-        flexarray_append(dm_args, "-sdl");
-        if (sdl->display)
-            flexarray_append_pair(dm_envs, "DISPLAY", sdl->display);
-        if (sdl->xauthority)
-            flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority);
-    }
-
-    if (keymap) {
-        flexarray_vappend(dm_args, "-k", keymap, NULL);
-    }
-
-    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        int ioemu_nics = 0;
-
-        if (b_info->kernel)
-            flexarray_vappend(dm_args, "-kernel", b_info->kernel, NULL);
-
-        if (b_info->ramdisk)
-            flexarray_vappend(dm_args, "-initrd", b_info->ramdisk, NULL);
-
-        if (b_info->cmdline)
-            flexarray_vappend(dm_args, "-append", b_info->cmdline, NULL);
-
-        /* Find out early if one of the disk is on the scsi bus and add a scsi
-         * controller. This is done ahead to keep the same behavior as previous
-         * version of QEMU (have the controller on the same PCI slot). */
-        for (i = 0; i < num_disks; i++) {
-            if (disks[i].is_cdrom) {
-                continue;
-            }
-            if (strncmp(disks[i].vdev, "sd", 2) == 0) {
-                flexarray_vappend(dm_args, "-device", "lsi53c895a", NULL);
-                break;
-            }
-        }
-
-        if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) {
-            if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list )
-            {
-                LOGD(ERROR, guest_domid, "Both serial and serial_list set");
-                return ERROR_INVAL;
-            } else {
-                if (b_info->u.hvm.serial) {
-                    if (is_stubdom) {
-                        /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
-                        flexarray_vappend(dm_args,
-                                          "-serial",
-                                          GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL),
-                                          NULL);
-                    } else {
-                        flexarray_vappend(dm_args,
-                                          "-serial", b_info->u.hvm.serial, NULL);
-                    }
-                } else if (b_info->u.hvm.serial_list) {
-                    char **p;
-                    /* see spawn_stub_launch_dm() for connecting STUBDOM_CONSOLE_SERIAL */
-                    for (p = b_info->u.hvm.serial_list, i = 0;
-                         *p;
-                         p++, i++) {
-                        if (is_stubdom)
-                            flexarray_vappend(dm_args,
-                                              "-serial",
-                                              GCSPRINTF("/dev/hvc%d", STUBDOM_CONSOLE_SERIAL + i),
-                                              NULL);
-                        else
-                            flexarray_vappend(dm_args,
-                                              "-serial",
-                                              *p, NULL);
-                    }
-                }
-            }
-        }
-
-        if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) {
-            flexarray_append(dm_args, "-nographic");
-        }
-
-        if (libxl_defbool_val(b_info->u.hvm.spice.enable) && !is_stubdom) {
-            const libxl_spice_info *spice = &b_info->u.hvm.spice;
-            char *spiceoptions = dm_spice_options(gc, spice);
-            if (!spiceoptions)
-                return ERROR_INVAL;
-
-            flexarray_append(dm_args, "-spice");
-            flexarray_append(dm_args, spiceoptions);
-            if (libxl_defbool_val(b_info->u.hvm.spice.vdagent)) {
-                flexarray_vappend(dm_args, "-device", "virtio-serial",
-                    "-chardev", "spicevmc,id=vdagent,name=vdagent", "-device",
-                    "virtserialport,chardev=vdagent,name=com.redhat.spice.0",
-                    NULL);
-            }
-        }
-
-        switch (b_info->u.hvm.vga.kind) {
-        case LIBXL_VGA_INTERFACE_TYPE_STD:
-            flexarray_append_pair(dm_args, "-device",
-                GCSPRINTF("VGA,vgamem_mb=%d",
-                libxl__sizekb_to_mb(b_info->video_memkb)));
-            break;
-        case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
-            flexarray_append_pair(dm_args, "-device",
-                GCSPRINTF("cirrus-vga,vgamem_mb=%d",
-                libxl__sizekb_to_mb(b_info->video_memkb)));
-            break;
-        case LIBXL_VGA_INTERFACE_TYPE_NONE:
-            break;
-        case LIBXL_VGA_INTERFACE_TYPE_QXL:
-            /* QXL have 2 ram regions, ram and vram */
-            flexarray_append_pair(dm_args, "-device",
-                GCSPRINTF("qxl-vga,vram_size_mb=%"PRIu64",ram_size_mb=%"PRIu64,
-                (b_info->video_memkb/2/1024), (b_info->video_memkb/2/1024) ) );
-            break;
-        default:
-            LOGD(ERROR, guest_domid, "Invalid emulated video card specified");
-            return ERROR_INVAL;
-        }
-
-        if (b_info->u.hvm.boot) {
-            flexarray_vappend(dm_args, "-boot",
-                    GCSPRINTF("order=%s", b_info->u.hvm.boot), NULL);
-        }
-        if (libxl_defbool_val(b_info->u.hvm.usb)
-            || b_info->u.hvm.usbdevice
-            || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
-            if (b_info->u.hvm.usbdevice
-                && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) {
-                LOGD(ERROR, guest_domid, "Both usbdevice and usbdevice_list set");
-                return ERROR_INVAL;
-            }
-            flexarray_append(dm_args, "-usb");
-            if (b_info->u.hvm.usbdevice) {
-                flexarray_vappend(dm_args,
-                                  "-usbdevice", b_info->u.hvm.usbdevice, NULL);
-            } else if (b_info->u.hvm.usbdevice_list) {
-                char **p;
-                for (p = b_info->u.hvm.usbdevice_list;
-                     *p;
-                     p++) {
-                    flexarray_vappend(dm_args,
-                                      "-usbdevice",
-                                      *p, NULL);
-                }
-            }
-        } else if (b_info->u.hvm.usbversion) {
-            switch (b_info->u.hvm.usbversion) {
-            case 1:
-                flexarray_vappend(dm_args,
-                    "-device", "piix3-usb-uhci,id=usb", NULL);
-                break;
-            case 2:
-                flexarray_append_pair(dm_args, "-device",
-                    "ich9-usb-ehci1,id=usb,addr=0x1d.0x7,multifunction=on");
-                for (i = 1; i < 4; i++)
-                    flexarray_append_pair(dm_args, "-device",
-                        GCSPRINTF("ich9-usb-uhci%d,masterbus=usb.0,"
-                        "firstport=%d,addr=0x1d.%#x,multifunction=on",
-                        i, 2*(i-1), i-1));
-                break;
-            case 3:
-                flexarray_vappend(dm_args,
-                    "-device", "nec-usb-xhci,id=usb", NULL);
-                break;
-            default:
-                LOGD(ERROR, guest_domid, "usbversion parameter is invalid, "
-                    "must be between 1 and 3");
-                return ERROR_INVAL;
-            }
-            if (b_info->u.hvm.spice.usbredirection >= 0 &&
-                b_info->u.hvm.spice.usbredirection < 5) {
-                for (i = 1; i <= b_info->u.hvm.spice.usbredirection; i++)
-                    flexarray_vappend(dm_args, "-chardev",
-                        GCSPRINTF("spicevmc,name=usbredir,id=usbrc%d", i),
-                        "-device",
-                        GCSPRINTF("usb-redir,chardev=usbrc%d,"
-                        "id=usbrc%d", i, i), NULL);
-            } else {
-                LOGD(ERROR, guest_domid, "usbredirection parameter is invalid, "
-                    "it must be between 1 and 4");
-                return ERROR_INVAL;
-            }
-        }
-        if (b_info->u.hvm.soundhw) {
-            flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL);
-        }
-        if (!libxl__acpi_defbool_val(b_info)) {
-            flexarray_append(dm_args, "-no-acpi");
-        }
-        if (b_info->max_vcpus > 1) {
-            flexarray_append(dm_args, "-smp");
-            if (b_info->avail_vcpus.size) {
-                int nr_set_cpus = 0;
-                nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus);
-
-                flexarray_append(dm_args, GCSPRINTF("%d,maxcpus=%d",
-                                                    nr_set_cpus,
-                                                    b_info->max_vcpus));
-            } else
-                flexarray_append(dm_args, GCSPRINTF("%d", b_info->max_vcpus));
-        }
-        for (i = 0; i < num_nics; i++) {
-            if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
-                char *smac = GCSPRINTF(LIBXL_MAC_FMT,
-                                       LIBXL_MAC_BYTES(nics[i].mac));
-                const char *ifname = libxl__device_nic_devname(gc,
-                                                guest_domid, nics[i].devid,
-                                                LIBXL_NIC_TYPE_VIF_IOEMU);
-                flexarray_append(dm_args, "-device");
-                flexarray_append(dm_args,
-                   GCSPRINTF("%s,id=nic%d,netdev=net%d,mac=%s",
-                             nics[i].model, nics[i].devid,
-                             nics[i].devid, smac));
-                flexarray_append(dm_args, "-netdev");
-                flexarray_append(dm_args,
-                                 GCSPRINTF("type=tap,id=net%d,ifname=%s,"
-                                           "script=%s,downscript=%s",
-                                           nics[i].devid, ifname,
-                                           libxl_tapif_script(gc),
-                                           libxl_tapif_script(gc)));
-
-                /* Userspace COLO Proxy need this */
-#define APPEND_COLO_SOCK_SERVER(sock_id, sock_ip, sock_port) ({             \
-    if (nics[i].colo_##sock_id &&                                           \
-        nics[i].colo_##sock_ip &&                                           \
-        nics[i].colo_##sock_port) {                                         \
-        flexarray_append(dm_args, "-chardev");                              \
-        flexarray_append(dm_args,                                           \
-            GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait",         \
-                      nics[i].colo_##sock_id,                               \
-                      nics[i].colo_##sock_ip,                               \
-                      nics[i].colo_##sock_port));                           \
-        }                                                                   \
-})
-
-#define APPEND_COLO_SOCK_CLIENT(sock_id, sock_ip, sock_port) ({             \
-    if (nics[i].colo_##sock_id &&                                           \
-        nics[i].colo_##sock_ip &&                                           \
-        nics[i].colo_##sock_port) {                                         \
-        flexarray_append(dm_args, "-chardev");                              \
-        flexarray_append(dm_args,                                           \
-            GCSPRINTF("socket,id=%s,host=%s,port=%s",                       \
-                      nics[i].colo_##sock_id,                               \
-                      nics[i].colo_##sock_ip,                               \
-                      nics[i].colo_##sock_port));                           \
-        }                                                                   \
-})
-
-                if (state->saved_state) {
-                    /* secondary colo run */
-
-                    APPEND_COLO_SOCK_CLIENT(sock_sec_redirector0_id,
-                                            sock_sec_redirector0_ip,
-                                            sock_sec_redirector0_port);
-
-                    APPEND_COLO_SOCK_CLIENT(sock_sec_redirector1_id,
-                                            sock_sec_redirector1_ip,
-                                            sock_sec_redirector1_port);
-
-                    if (nics[i].colo_filter_sec_redirector0_queue &&
-                        nics[i].colo_filter_sec_redirector0_indev) {
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args,
-                           GCSPRINTF("filter-redirector,id=rs1,netdev=net%d,queue=%s,indev=%s",
-                                     nics[i].devid,
-                                     nics[i].colo_filter_sec_redirector0_queue,
-                                     nics[i].colo_filter_sec_redirector0_indev));
-                    }
-                    if (nics[i].colo_filter_sec_redirector1_queue &&
-                        nics[i].colo_filter_sec_redirector1_outdev) {
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args,
-                           GCSPRINTF("filter-redirector,id=rs2,netdev=net%d,queue=%s,outdev=%s",
-                                     nics[i].devid,
-                                     nics[i].colo_filter_sec_redirector1_queue,
-                                     nics[i].colo_filter_sec_redirector1_outdev));
-                    }
-                    if (nics[i].colo_filter_sec_rewriter0_queue) {
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args,
-                           GCSPRINTF("filter-rewriter,id=rs3,netdev=net%d,queue=%s",
-                                     nics[i].devid,
-                                     nics[i].colo_filter_sec_rewriter0_queue));
-                    }
-                } else {
-                    /* primary colo run */
-
-                    APPEND_COLO_SOCK_SERVER(sock_mirror_id,
-                                            sock_mirror_ip,
-                                            sock_mirror_port);
-
-                    APPEND_COLO_SOCK_SERVER(sock_compare_pri_in_id,
-                                            sock_compare_pri_in_ip,
-                                            sock_compare_pri_in_port);
-
-                    APPEND_COLO_SOCK_SERVER(sock_compare_sec_in_id,
-                                            sock_compare_sec_in_ip,
-                                            sock_compare_sec_in_port);
-
-                    APPEND_COLO_SOCK_SERVER(sock_compare_notify_id,
-                                            sock_compare_notify_ip,
-                                            sock_compare_notify_port);
-
-                    APPEND_COLO_SOCK_SERVER(sock_redirector0_id,
-                                            sock_redirector0_ip,
-                                            sock_redirector0_port);
-
-                    APPEND_COLO_SOCK_CLIENT(sock_redirector1_id,
-                                            sock_redirector1_ip,
-                                            sock_redirector1_port);
-
-                    APPEND_COLO_SOCK_CLIENT(sock_redirector2_id,
-                                            sock_redirector2_ip,
-                                            sock_redirector2_port);
-
-                    if (nics[i].colo_filter_mirror_queue &&
-                        nics[i].colo_filter_mirror_outdev) {
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args,
-                           GCSPRINTF("filter-mirror,id=m1,netdev=net%d,queue=%s,outdev=%s",
-                                     nics[i].devid,
-                                     nics[i].colo_filter_mirror_queue,
-                                     nics[i].colo_filter_mirror_outdev));
-                    }
-                    if (nics[i].colo_filter_redirector0_queue &&
-                        nics[i].colo_filter_redirector0_indev) {
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args,
-                           GCSPRINTF("filter-redirector,id=r1,netdev=net%d,queue=%s,indev=%s",
-                                     nics[i].devid,
-                                     nics[i].colo_filter_redirector0_queue,
-                                     nics[i].colo_filter_redirector0_indev));
-                    }
-                    if (nics[i].colo_filter_redirector1_queue &&
-                        nics[i].colo_filter_redirector1_outdev) {
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args,
-                          GCSPRINTF("filter-redirector,id=r2,netdev=net%d,queue=%s,outdev=%s",
-                                     nics[i].devid,
-                                     nics[i].colo_filter_redirector1_queue,
-                                     nics[i].colo_filter_redirector1_outdev));
-                    }
-                    if (nics[i].colo_compare_pri_in &&
-                        nics[i].colo_compare_sec_in &&
-                        nics[i].colo_compare_out &&
-                        nics[i].colo_compare_notify_dev) {
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args, "iothread,id=colo-compare-iothread-1");
-                        flexarray_append(dm_args, "-object");
-                        flexarray_append(dm_args,
-                           GCSPRINTF("colo-compare,id=c1,primary_in=%s,secondary_in=%s,outdev=%s,notify_dev=%s,iothread=colo-compare-iothread-1",
-                                     nics[i].colo_compare_pri_in,
-                                     nics[i].colo_compare_sec_in,
-                                     nics[i].colo_compare_out,
-                                     nics[i].colo_compare_notify_dev));
-                    }
-                }
-                ioemu_nics++;
-
-#undef APPEND_COLO_SOCK_SERVER
-#undef APPEND_COLO_SOCK_CLIENT
-            }
-        }
-        /* If we have no emulated nics, tell qemu not to create any */
-        if ( ioemu_nics == 0 ) {
-            flexarray_append(dm_args, "-net");
-            flexarray_append(dm_args, "none");
-        }
-    } else {
-        if (!sdl && !vnc) {
-            flexarray_append(dm_args, "-nographic");
-        }
-    }
-
-    if (libxl_defbool_val(b_info->dm_restrict)) {
-        char *chroot_dir = GCSPRINTF("%s/qemu-root-%d",
-                                      libxl__run_dir_path(), guest_domid);
-        int r;
-
-        flexarray_append(dm_args, "-xen-domid-restrict");
-
-        /*
-         * Run QEMU in a chroot at XEN_RUN_DIR/qemu-root-<domid>
-         *
-         * There is no library function to do the equivalent of `rm
-         * -rf`.  However deprivileged QEMU in theory shouldn't be
-         * able to write any files, as the chroot would be owned by
-         * root, but it would be running as an unprivileged process.
-         * So in theory, old chroots should always be empty.
-         *
-         * rmdir the directory before attempting to create
-         * it; if it returns anything other than ENOENT, fail domain
-         * creation.
-         */
-        r = rmdir(chroot_dir);
-        if (r != 0 && errno != ENOENT) {
-            LOGED(ERROR, guest_domid,
-                  "failed to remove existing chroot dir %s", chroot_dir);
-            return ERROR_FAIL;
-        }
-
-        for (;;) {
-            r = mkdir(chroot_dir, 0000);
-            if (!r)
-                break;
-            if (errno == EINTR) continue;
-            LOGED(ERROR, guest_domid,
-                  "failed to create chroot dir %s", chroot_dir);
-            return ERROR_FAIL;
-        }
-
-        /* Add "-chroot [dir]" to command-line */
-        flexarray_append(dm_args, "-chroot");
-        flexarray_append(dm_args, chroot_dir);
-    }
-
-    if (state->saved_state) {
-        if (is_stubdom) {
-            /* Linux stubdomain must replace $STUBDOM_RESTORE_INCOMING_ARG
-             * with the approriate fd:$num argument for the
-             * STUBDOM_CONSOLE_RESTORE console 2.
-             */
-            flexarray_append(dm_args, "-incoming");
-            flexarray_append(dm_args, "$STUBDOM_RESTORE_INCOMING_ARG");
-        } else {
-            /* This file descriptor is meant to be used by QEMU */
-            *dm_state_fd = open(state->saved_state, O_RDONLY);
-            flexarray_append(dm_args, "-incoming");
-            flexarray_append(dm_args, GCSPRINTF("fd:%d",*dm_state_fd));
-        }
-    }
-    for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++)
-        flexarray_append(dm_args, b_info->extra[i]);
-
-    flexarray_append(dm_args, "-machine");
-    switch (b_info->type) {
-    case LIBXL_DOMAIN_TYPE_PVH:
-    case LIBXL_DOMAIN_TYPE_PV:
-        flexarray_append(dm_args, "xenpv");
-        for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++)
-            flexarray_append(dm_args, b_info->extra_pv[i]);
-        break;
-    case LIBXL_DOMAIN_TYPE_HVM:
-        if (!libxl_defbool_val(b_info->u.hvm.xen_platform_pci)) {
-            /* Switching here to the machine "pc" which does not add
-             * the xen-platform device instead of the default "xenfv" machine.
-             */
-            machinearg = libxl__strdup(gc, "pc,accel=xen");
-        } else {
-            machinearg = libxl__strdup(gc, "xenfv");
-        }
-        if (b_info->u.hvm.mmio_hole_memkb) {
-            uint64_t max_ram_below_4g = (1ULL << 32) -
-                (b_info->u.hvm.mmio_hole_memkb << 10);
-
-            if (max_ram_below_4g > HVM_BELOW_4G_MMIO_START) {
-                LOGD(WARN, guest_domid, "mmio_hole_memkb=%"PRIu64
-                     " invalid ignored.\n",
-                    b_info->u.hvm.mmio_hole_memkb);
-            } else {
-                machinearg = GCSPRINTF("%s,max-ram-below-4g=%"PRIu64,
-                                            machinearg, max_ram_below_4g);
-            }
-        }
-
-        if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) {
-            enum libxl_gfx_passthru_kind gfx_passthru_kind =
-                            libxl__detect_gfx_passthru_kind(gc, guest_config);
-            switch (gfx_passthru_kind) {
-            case LIBXL_GFX_PASSTHRU_KIND_IGD:
-                machinearg = GCSPRINTF("%s,igd-passthru=on", machinearg);
-                break;
-            case LIBXL_GFX_PASSTHRU_KIND_DEFAULT:
-                LOGD(ERROR, guest_domid, "unable to detect required gfx_passthru_kind");
-                return ERROR_FAIL;
-            default:
-                LOGD(ERROR, guest_domid, "invalid value for gfx_passthru_kind");
-                return ERROR_INVAL;
-            }
-        }
-
-        flexarray_append(dm_args, machinearg);
-        for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
-            flexarray_append(dm_args, b_info->extra_hvm[i]);
-        break;
-    default:
-        abort();
-    }
-
-    ram_size = libxl__sizekb_to_mb(b_info->max_memkb - b_info->video_memkb);
-    flexarray_append(dm_args, "-m");
-    flexarray_append(dm_args, GCSPRINTF("%"PRId64, ram_size));
-
-    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        if (b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI)
-            flexarray_append_pair(dm_args, "-device", "ahci,id=ahci0");
-        for (i = 0; i < num_disks; i++) {
-            int disk, part;
-            int dev_number =
-                libxl__device_disk_dev_number(disks[i].vdev, &disk, &part);
-            const char *format;
-            char *drive;
-            const char *target_path = NULL;
-            int colo_mode;
-
-            if (dev_number == -1) {
-                LOGD(WARN, guest_domid, "unable to determine"" disk number for %s",
-                     disks[i].vdev);
-                continue;
-            }
-
-            /*
-             * If qemu isn't doing the interpreting, the parameter is
-             * always raw
-             */
-            if (libxl_defbool_val(b_info->device_model_stubdomain))
-                format = "host_device";
-            else if (disks[i].backend == LIBXL_DISK_BACKEND_QDISK)
-                format = libxl__qemu_disk_format_string(disks[i].format);
-            else
-                format = libxl__qemu_disk_format_string(LIBXL_DISK_FORMAT_RAW);
-
-            if (disks[i].format == LIBXL_DISK_FORMAT_EMPTY) {
-                if (!disks[i].is_cdrom) {
-                    LOGD(WARN, guest_domid, "Cannot support empty disk format for %s",
-                         disks[i].vdev);
-                    continue;
-                }
-            } else if (libxl_defbool_val(b_info->device_model_stubdomain)) {
-                if (disk > 'z' - 'a') {
-                    LOGD(WARN, guest_domid,
-                            "Emulation of only first %d disks is supported with qemu-xen in stubdomain.\n"
-                            "Disk %d will be available via PV drivers but not as an emulated disk.",
-                            'z' - 'a',
-                            disk);
-                    continue;
-                }
-                target_path = GCSPRINTF("/dev/xvd%c", 'a' + disk);
-            } else {
-                if (format == NULL) {
-                    LOGD(WARN, guest_domid,
-                         "Unable to determine disk image format: %s\n"
-                         "Disk will be available via PV drivers but not as an"
-                         "emulated disk.",
-                         disks[i].vdev);
-                    continue;
-                }
-
-                assert(disks[i].backend != LIBXL_DISK_BACKEND_TAP);
-                target_path = libxl__device_disk_find_local_path(gc,
-                                    guest_domid, &disks[i], true);
-
-                if (!target_path) {
-                    LOGD(WARN, guest_domid, "No way to get local access disk to image: %s\n"
-                         "Disk will be available via PV drivers but not as an"
-                         "emulated disk.",
-                         disks[i].vdev);
-                    continue;
-                }
-            }
-
-            if (disks[i].is_cdrom) {
-                if (disk > 4) {
-                    LOGD(WARN, guest_domid, "Emulated CDROM can be only one of the first 4 disks.\n"
-                         "Disk %s will be available via PV drivers but not as an "
-                         "emulated disk.",
-                         disks[i].vdev);
-                    continue;
-                }
-                drive = libxl__sprintf(gc,
-                         "if=ide,index=%d,readonly=on,media=cdrom,id=ide-%i",
-                         disk, dev_number);
-
-                if (target_path)
-                    drive = libxl__sprintf(gc, "%s,file=%s,format=%s",
-                                           drive, target_path, format);
-            } else {
-                /*
-                 * Explicit sd disks are passed through as is.
-                 *
-                 * For other disks we translate devices 0..3 into
-                 * hd[a-d] and ignore the rest.
-                 */
-
-                if (libxl_defbool_val(disks[i].colo_enable)) {
-                    if (libxl_defbool_val(disks[i].colo_restore_enable))
-                        colo_mode = LIBXL__COLO_SECONDARY;
-                    else
-                        colo_mode = LIBXL__COLO_PRIMARY;
-                } else {
-                    colo_mode = LIBXL__COLO_NONE;
-                }
-
-                if (strncmp(disks[i].vdev, "sd", 2) == 0) {
-                    const char *drive_id;
-                    if (colo_mode == LIBXL__COLO_SECONDARY) {
-                        drive = libxl__sprintf
-                            (gc, "if=none,driver=%s,file=%s,id=%s,readonly=%s",
-                             format, target_path, disks[i].colo_export,
-                             disks[i].readwrite ? "off" : "on");
-
-                        flexarray_append(dm_args, "-drive");
-                        flexarray_append(dm_args, drive);
-                    }
-                    drive = qemu_disk_scsi_drive_string(gc, target_path, disk,
-                                                        format,
-                                                        &disks[i],
-                                                        colo_mode,
-                                                        &drive_id),
-                    flexarray_vappend(dm_args,
-                        "-drive", drive,
-                        "-device", GCSPRINTF("scsi-disk,drive=%s,scsi-id=%d",
-                                             drive_id, disk),
-                        NULL);
-                    continue;
-                } else if (disk < 6 && b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI) {
-                    if (!disks[i].readwrite) {
-                        LOGD(ERROR, guest_domid,
-                             "qemu-xen doesn't support read-only AHCI disk drivers");
-                        return ERROR_INVAL;
-                    }
-                    flexarray_vappend(dm_args, "-drive",
-                        GCSPRINTF("file=%s,if=none,id=ahcidisk-%d,format=%s,cache=writeback",
-                        target_path, disk, format),
-                        "-device", GCSPRINTF("ide-hd,bus=ahci0.%d,unit=0,drive=ahcidisk-%d",
-                        disk, disk), NULL);
-                    continue;
-                } else if (disk < 4) {
-                    if (!disks[i].readwrite) {
-                        LOGD(ERROR, guest_domid,
-                             "qemu-xen doesn't support read-only IDE disk drivers");
-                        return ERROR_INVAL;
-                    }
-                    if (colo_mode == LIBXL__COLO_SECONDARY) {
-                        drive = libxl__sprintf
-                            (gc, "if=none,driver=%s,file=%s,id=%s",
-                             format, target_path, disks[i].colo_export);
-
-                        flexarray_append(dm_args, "-drive");
-                        flexarray_append(dm_args, drive);
-                    }
-                    drive = qemu_disk_ide_drive_string(gc, target_path, disk,
-                                                       format,
-                                                       &disks[i],
-                                                       colo_mode);
-                } else {
-                    LOGD(WARN, guest_domid, "Only 4 emulated IDE disks are supported.\n"
-                         "Disk %s will be available via PV drivers but not as an "
-                         "emulated disk.",
-                         disks[i].vdev);
-                    continue; /* Do not emulate this disk */
-                }
-
-                if (!drive)
-                    continue;
-            }
-
-            flexarray_append(dm_args, "-drive");
-            flexarray_append(dm_args, drive);
-        }
-
-        switch (b_info->u.hvm.vendor_device) {
-        case LIBXL_VENDOR_DEVICE_XENSERVER:
-            flexarray_append(dm_args, "-device");
-            flexarray_append(dm_args, "xen-pvdevice,device-id=0xc000");
-            break;
-        default:
-            break;
-        }
-
-        if (state->dm_runas) {
-            flexarray_append(dm_args, "-runas");
-            flexarray_append(dm_args, state->dm_runas);
-        }
-    }
-    flexarray_append(dm_args, NULL);
-    *args = (char **) flexarray_contents(dm_args);
-    flexarray_append(dm_envs, NULL);
-    if (envs)
-        *envs = (char **) flexarray_contents(dm_envs);
-    return 0;
-}
-
-static int libxl__build_device_model_args(libxl__gc *gc,
-                                        const char *dm, int guest_domid,
-                                        const libxl_domain_config *guest_config,
-                                        char ***args, char ***envs,
-                                        const libxl__domain_build_state *state,
-                                        int *dm_state_fd)
-/* dm_state_fd may be NULL iff caller knows we are using stubdom
- * and therefore will be passing a filename rather than a fd. */
-{
-    switch (guest_config->b_info.device_model_version) {
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-        return libxl__build_device_model_args_old(gc, dm,
-                                                  guest_domid, guest_config,
-                                                  args, envs,
-                                                  state);
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-        if (!libxl_defbool_val(guest_config->b_info.device_model_stubdomain)) {
-            assert(dm_state_fd != NULL);
-            assert(*dm_state_fd < 0);
-        }
-        return libxl__build_device_model_args_new(gc, dm,
-                                                  guest_domid, guest_config,
-                                                  args, envs,
-                                                  state, dm_state_fd);
-    default:
-        LOGED(ERROR, guest_domid, "unknown device model version %d",
-              guest_config->b_info.device_model_version);
-        return ERROR_INVAL;
-    }
-}
-
-static void libxl__dm_vifs_from_hvm_guest_config(libxl__gc *gc,
-                                    libxl_domain_config * const guest_config,
-                                    libxl_domain_config *dm_config)
-{
-    int i, nr = guest_config->num_nics;
-
-    GCNEW_ARRAY(dm_config->nics, nr);
-
-    for (i=0; i<nr; i++) {
-        dm_config->nics[i] = guest_config->nics[i];
-        dm_config->nics[i].nictype = LIBXL_NIC_TYPE_VIF;
-        if (dm_config->nics[i].ifname)
-            dm_config->nics[i].ifname = GCSPRINTF("%s" TAP_DEVICE_SUFFIX,
-                                                  dm_config->nics[i].ifname);
-    }
-
-    dm_config->num_nics = nr;
-}
-
-static int libxl__vfb_and_vkb_from_hvm_guest_config(libxl__gc *gc,
-                                        const libxl_domain_config *guest_config,
-                                        libxl_device_vfb *vfb,
-                                        libxl_device_vkb *vkb)
-{
-    const libxl_domain_build_info *b_info = &guest_config->b_info;
-
-    if (b_info->type != LIBXL_DOMAIN_TYPE_HVM)
-        return ERROR_INVAL;
-
-    libxl_device_vfb_init(vfb);
-    libxl_device_vkb_init(vkb);
-
-    vfb->backend_domid = 0;
-    vfb->devid = 0;
-    vfb->vnc = b_info->u.hvm.vnc;
-    vfb->keymap = b_info->u.hvm.keymap;
-    vfb->sdl = b_info->u.hvm.sdl;
-
-    vkb->backend_domid = 0;
-    vkb->devid = 0;
-
-    return 0;
-}
-
-static int libxl__write_stub_dmargs(libxl__gc *gc,
-                                    int dm_domid, int guest_domid,
-                                    char **args, bool is_linux_stubdom)
-{
-    struct xs_permissions roperm[2];
-    xs_transaction_t t = XBT_NULL;
-    char *dmargs;
-    int rc;
-
-    roperm[0].id = 0;
-    roperm[0].perms = XS_PERM_NONE;
-    roperm[1].id = dm_domid;
-    roperm[1].perms = XS_PERM_READ;
-
-    if (!is_linux_stubdom) {
-        int dmargs_size = 0;
-        int i = 0;
-
-        while (args[i] != NULL) {
-            dmargs_size = dmargs_size + strlen(args[i]) + 1;
-            i++;
-        }
-
-        dmargs_size++;
-        dmargs = (char *) libxl__malloc(gc, dmargs_size);
-
-        i = 1;
-        dmargs[0] = '\0';
-        while (args[i] != NULL) {
-            if (strcmp(args[i], "-sdl") && strcmp(args[i], "-M") && strcmp(args[i], "xenfv")) {
-                strcat(dmargs, " ");
-                strcat(dmargs, args[i]);
-            }
-            i++;
-        }
-    }
-
-    for (;;) {
-        const char *vm_path;
-        char *path;
-
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        rc = libxl__xs_read_mandatory(gc, t,
-                                      GCSPRINTF("/local/domain/%d/vm",
-                                                guest_domid),
-                                      &vm_path);
-        if (rc) goto out;
-
-        if (is_linux_stubdom) {
-            int i;
-
-            path = GCSPRINTF("%s/image/dm-argv", vm_path);
-
-            rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
-            if (rc) goto out;
-
-            for (i=1; args[i] != NULL; i++) {
-                rc = libxl__xs_write_checked(gc, t,
-                                             GCSPRINTF("%s/%03d", path, i),
-                                             args[i]);
-                if (rc) goto out;
-            }
-        } else {
-            path = GCSPRINTF("%s/image/dmargs", vm_path);
-
-            rc = libxl__xs_mknod(gc, t, path, roperm, ARRAY_SIZE(roperm));
-            if (rc) goto out;
-
-            rc = libxl__xs_write_checked(gc, t, path, dmargs);
-            if (rc) goto out;
-
-            rc = libxl__xs_mknod(gc, t, GCSPRINTF("%s/rtc/timeoffset", vm_path),
-                                 roperm, ARRAY_SIZE(roperm));
-            if (rc) goto out;
-        }
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc<0) goto out;
-    }
-
-    return 0;
-
- out:
-    libxl__xs_transaction_abort(gc, &t);
-
-    return rc;
-}
-
-static int libxl__store_libxl_entry(libxl__gc *gc, uint32_t domid,
-                                    const char *name, const char *value)
-{
-    char *path = NULL;
-
-    path = libxl__xs_libxl_path(gc, domid);
-    path = libxl__sprintf(gc, "%s/%s", path, name);
-    return libxl__xs_printf(gc, XBT_NULL, path, "%s", value);
-}
-
-static void dmss_init(libxl__dm_spawn_state *dmss)
-{
-    libxl__ev_qmp_init(&dmss->qmp);
-    libxl__ev_time_init(&dmss->timeout);
-}
-
-static void dmss_dispose(libxl__gc *gc, libxl__dm_spawn_state *dmss)
-{
-    libxl__ev_qmp_dispose(gc, &dmss->qmp);
-    libxl__ev_time_deregister(gc, &dmss->timeout);
-}
-
-static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
-                                libxl__dm_spawn_state *stubdom_dmss,
-                                int rc);
-
-static void spawn_stub_launch_dm(libxl__egc *egc,
-                                 libxl__multidev *aodevs, int ret);
-
-static void stubdom_pvqemu_cb(libxl__egc *egc,
-                              libxl__multidev *aodevs,
-                              int rc);
-static void stubdom_pvqemu_unpaused(libxl__egc *egc,
-                                    libxl__dm_resume_state *dmrs,
-                                    int rc);
-
-static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
-                              int rc, const char *p);
-
-static void spawn_qmp_proxy(libxl__egc *egc,
-                            libxl__stub_dm_spawn_state *sdss);
-
-static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
-                              const char *xsdata);
-
-static void qmp_proxy_startup_failed(libxl__egc *egc,
-                                     libxl__spawn_state *spawn,
-                                     int rc);
-
-static void qmp_proxy_detached(libxl__egc *egc,
-                               libxl__spawn_state *spawn);
-
-static void qmp_proxy_spawn_outcome(libxl__egc *egc,
-                                    libxl__stub_dm_spawn_state *sdss,
-                                    int rc);
-
-char *libxl__stub_dm_name(libxl__gc *gc, const char *guest_name)
-{
-    return GCSPRINTF("%s-dm", guest_name);
-}
-
-void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state *sdss)
-{
-    STATE_AO_GC(sdss->dm.spawn.ao);
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int ret;
-    libxl_device_vfb *vfb;
-    libxl_device_vkb *vkb;
-    char **args;
-    struct xs_permissions perm[2];
-    xs_transaction_t t;
-
-    /* convenience aliases */
-    libxl_domain_config *const dm_config = &sdss->dm_config;
-    libxl_domain_config *const guest_config = sdss->dm.guest_config;
-    const int guest_domid = sdss->dm.guest_domid;
-    libxl__domain_build_state *const d_state = sdss->dm.build_state;
-    libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
-
-    /* Initialise private part of sdss */
-    libxl__domain_build_state_init(stubdom_state);
-    dmss_init(&sdss->dm);
-    dmss_init(&sdss->pvqemu);
-    libxl__xswait_init(&sdss->xswait);
-
-    assert(libxl_defbool_val(guest_config->b_info.device_model_stubdomain));
-
-    sdss->pvqemu.guest_domid = INVALID_DOMID;
-
-    libxl_domain_create_info_init(&dm_config->c_info);
-    dm_config->c_info.type = LIBXL_DOMAIN_TYPE_PV;
-    dm_config->c_info.name = libxl__stub_dm_name(gc,
-                                    libxl__domid_to_name(gc, guest_domid));
-    /* When we are here to launch stubdom, ssidref is a valid value
-     * already, no need to parse it again.
-     */
-    dm_config->c_info.ssidref = guest_config->b_info.device_model_ssidref;
-    dm_config->c_info.ssid_label = NULL;
-
-    libxl_uuid_generate(&dm_config->c_info.uuid);
-
-    libxl_domain_build_info_init(&dm_config->b_info);
-    libxl_domain_build_info_init_type(&dm_config->b_info, LIBXL_DOMAIN_TYPE_PV);
-
-    dm_config->b_info.shadow_memkb = 0;
-    dm_config->b_info.max_vcpus = 1;
-    dm_config->b_info.max_memkb = guest_config->b_info.stubdomain_memkb;
-    dm_config->b_info.max_memkb += guest_config->b_info.video_memkb;
-    dm_config->b_info.target_memkb = dm_config->b_info.max_memkb;
-
-    dm_config->b_info.max_grant_frames = guest_config->b_info.max_grant_frames;
-    dm_config->b_info.max_maptrack_frames = 0;
-
-    dm_config->b_info.u.pv.features = "";
-
-    dm_config->b_info.device_model_version =
-        guest_config->b_info.device_model_version;
-    dm_config->b_info.device_model =
-        guest_config->b_info.device_model;
-    dm_config->b_info.extra = guest_config->b_info.extra;
-    dm_config->b_info.extra_pv = guest_config->b_info.extra_pv;
-    dm_config->b_info.extra_hvm = guest_config->b_info.extra_hvm;
-
-    dm_config->disks = guest_config->disks;
-    dm_config->num_disks = guest_config->num_disks;
-
-    libxl__dm_vifs_from_hvm_guest_config(gc, guest_config, dm_config);
-
-    dm_config->c_info.run_hotplug_scripts =
-        guest_config->c_info.run_hotplug_scripts;
-
-    ret = libxl__domain_config_setdefault(gc, dm_config, guest_domid);
-    if (ret) goto out;
-
-    if (libxl_defbool_val(guest_config->b_info.u.hvm.vnc.enable)
-        || libxl_defbool_val(guest_config->b_info.u.hvm.spice.enable)
-        || libxl_defbool_val(guest_config->b_info.u.hvm.sdl.enable)) {
-        GCNEW(vfb);
-        GCNEW(vkb);
-        libxl__vfb_and_vkb_from_hvm_guest_config(gc, guest_config, vfb, vkb);
-        dm_config->vfbs = vfb;
-        dm_config->num_vfbs = 1;
-        dm_config->vkbs = vkb;
-        dm_config->num_vkbs = 1;
-    }
-
-    if (guest_config->b_info.stubdomain_kernel &&
-        access(guest_config->b_info.stubdomain_kernel, R_OK) != 0) {
-        LOGED(ERROR, guest_domid, "could not access stubdomain kernel %s",
-              guest_config->b_info.stubdomain_kernel);
-        ret = ERROR_INVAL;
-        goto out;
-    }
-
-    if (guest_config->b_info.stubdomain_ramdisk &&
-        access(guest_config->b_info.stubdomain_ramdisk, R_OK) != 0) {
-        LOGED(ERROR, guest_domid, "could not access stubdomain ramdisk %s",
-              guest_config->b_info.stubdomain_ramdisk);
-        ret = ERROR_INVAL;
-        goto out;
-    }
-
-    stubdom_state->pv_kernel.path = guest_config->b_info.stubdomain_kernel;
-    stubdom_state->pv_ramdisk.path = guest_config->b_info.stubdomain_ramdisk;
-
-    /* fixme: this function can leak the stubdom if it fails */
-    ret = libxl__domain_make(gc, dm_config, stubdom_state,
-                             &sdss->pvqemu.guest_domid, false);
-    if (ret)
-        goto out;
-    uint32_t dm_domid = sdss->pvqemu.guest_domid;
-    ret = libxl__domain_build(gc, dm_config, dm_domid, stubdom_state);
-    if (ret)
-        goto out;
-
-    ret = libxl__build_device_model_args(gc, "stubdom-dm", guest_domid,
-                                         guest_config, &args, NULL,
-                                         d_state, NULL);
-    if (ret) {
-        ret = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl__store_libxl_entry(gc, guest_domid, "dm-version",
-        libxl_device_model_version_to_string(dm_config->b_info.device_model_version));
-
-    libxl__write_stub_dmargs(gc, dm_domid, guest_domid, args,
-                             libxl__stubdomain_is_linux(&guest_config->b_info));
-    libxl__xs_printf(gc, XBT_NULL,
-                     GCSPRINTF("%s/image/device-model-domid",
-                               libxl__xs_get_dompath(gc, guest_domid)),
-                     "%d", dm_domid);
-    libxl__xs_printf(gc, XBT_NULL,
-                     GCSPRINTF("%s/target",
-                               libxl__xs_get_dompath(gc, dm_domid)),
-                     "%d", guest_domid);
-    if (guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
-        /* qemu-xen is used as a dm in the stubdomain, so we set the bios
-         * accroding to this */
-        libxl__xs_printf(gc, XBT_NULL,
-                        libxl__sprintf(gc, "%s/hvmloader/bios",
-                                       libxl__xs_get_dompath(gc, guest_domid)),
-                        "%s",
-                        libxl_bios_type_to_string(guest_config->b_info.u.hvm.bios));
-    }
-    ret = xc_domain_set_target(ctx->xch, dm_domid, guest_domid);
-    if (ret<0) {
-        LOGED(ERROR, guest_domid, "setting target domain %d -> %d",
-              dm_domid, guest_domid);
-        ret = ERROR_FAIL;
-        goto out;
-    }
-    xs_set_target(ctx->xsh, dm_domid, guest_domid);
-
-    perm[0].id = dm_domid;
-    perm[0].perms = XS_PERM_NONE;
-    perm[1].id = guest_domid;
-    perm[1].perms = XS_PERM_READ;
-retry_transaction:
-    t = xs_transaction_start(ctx->xsh);
-    xs_mkdir(ctx->xsh, t, DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""));
-    xs_set_permissions(ctx->xsh, t,
-                       DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""),
-                       perm, ARRAY_SIZE(perm));
-    if (!xs_transaction_end(ctx->xsh, t, 0))
-        if (errno == EAGAIN)
-            goto retry_transaction;
-
-    libxl__multidev_begin(ao, &sdss->multidev);
-    sdss->multidev.callback = spawn_stub_launch_dm;
-    libxl__add_disks(egc, ao, dm_domid, dm_config, &sdss->multidev);
-    libxl__multidev_prepared(egc, &sdss->multidev, 0);
-
-    return;
-
-out:
-    assert(ret);
-    spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, ret);
-}
-
-static void spawn_stub_launch_dm(libxl__egc *egc,
-                                 libxl__multidev *multidev, int ret)
-{
-    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
-    STATE_AO_GC(sdss->dm.spawn.ao);
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int i, num_console = STUBDOM_SPECIAL_CONSOLES;
-    libxl__device_console *console;
-
-    /* convenience aliases */
-    libxl_domain_config *const dm_config = &sdss->dm_config;
-    libxl_domain_config *const guest_config = sdss->dm.guest_config;
-    const int guest_domid = sdss->dm.guest_domid;
-    libxl__domain_build_state *const d_state = sdss->dm.build_state;
-    libxl__domain_build_state *const stubdom_state = &sdss->dm_state;
-    uint32_t dm_domid = sdss->pvqemu.guest_domid;
-    int need_qemu;
-
-    if (ret) {
-        LOGD(ERROR, guest_domid, "error connecting disk devices");
-        goto out;
-     }
-
-    for (i = 0; i < dm_config->num_nics; i++) {
-         /* We have to init the nic here, because we still haven't
-         * called libxl_device_nic_add at this point, but qemu needs
-         * the nic information to be complete.
-         */
-        ret = libxl__nic_devtype.set_default(gc, dm_domid, &dm_config->nics[i],
-                                             false);
-        if (ret)
-            goto out;
-    }
-    if (dm_config->num_vfbs) {
-        ret = libxl__device_add(gc, dm_domid, &libxl__vfb_devtype,
-                                &dm_config->vfbs[0]);
-        if (ret) goto out;
-    }
-    if (dm_config->num_vkbs) {
-        ret = libxl__device_add(gc, dm_domid, &libxl__vkb_devtype,
-                                &dm_config->vkbs[0]);
-        if (ret) goto out;
-    }
-
-    if (guest_config->b_info.u.hvm.serial) {
-        num_console++;
-    } else if (guest_config->b_info.u.hvm.serial_list) {
-        char **serial = guest_config->b_info.u.hvm.serial_list;
-        while (*(serial++))
-            num_console++;
-    }
-
-    console = libxl__calloc(gc, num_console, sizeof(libxl__device_console));
-
-    for (i = 0; i < num_console; i++) {
-        console[i].devid = i;
-        console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
-        /* STUBDOM_CONSOLE_LOGGING (console 0) is for minios logging
-         * STUBDOM_CONSOLE_SAVE (console 1) is for writing the save file
-         * STUBDOM_CONSOLE_RESTORE (console 2) is for reading the save file
-         */
-        switch (i) {
-            char *filename;
-            char *name;
-            case STUBDOM_CONSOLE_LOGGING:
-                name = GCSPRINTF("qemu-dm-%s",
-                                 libxl_domid_to_name(ctx, guest_domid));
-                ret = libxl_create_logfile(ctx, name, &filename);
-                if (ret) goto out;
-                console[i].output = GCSPRINTF("file:%s", filename);
-                free(filename);
-                /* will be changed back to LIBXL__CONSOLE_BACKEND_IOEMU if qemu
-                 * will be in use */
-                console[i].consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
-                break;
-            case STUBDOM_CONSOLE_SAVE:
-                console[i].output = GCSPRINTF("file:%s",
-                                libxl__device_model_savefile(gc, guest_domid));
-                break;
-            case STUBDOM_CONSOLE_RESTORE:
-                if (d_state->saved_state)
-                    console[i].output =
-                        GCSPRINTF("pipe:%s", d_state->saved_state);
-                break;
-            case STUBDOM_CONSOLE_SERIAL:
-                if (guest_config->b_info.u.hvm.serial) {
-                    console[i].output = guest_config->b_info.u.hvm.serial;
-                    break;
-                }
-                /* fall-through */
-            default:
-                /* Serial_list is set, as otherwise num_consoles would be
-                 * smaller and consoles 0-2 are handled above. */
-                assert(guest_config->b_info.u.hvm.serial_list);
-                console[i].output = guest_config->b_info.u.hvm.serial_list[
-                    i-STUBDOM_CONSOLE_SERIAL];
-                break;
-        }
-    }
-
-    /*
-     * Until xenconsoled learns how to handle multiple consoles, require qemu
-     * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
-     */
-    need_qemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
-
-    for (i = 0; i < num_console; i++) {
-        libxl__device device;
-        if (need_qemu)
-            console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
-        ret = libxl__device_console_add(gc, dm_domid, &console[i],
-                        i == STUBDOM_CONSOLE_LOGGING ? stubdom_state : NULL,
-                        &device);
-        if (ret)
-            goto out;
-    }
-
-    sdss->qmp_proxy_spawn.ao = ao;
-    if (libxl__stubdomain_is_linux(&guest_config->b_info)) {
-        spawn_qmp_proxy(egc, sdss);
-    } else {
-        qmp_proxy_spawn_outcome(egc, sdss, 0);
-    }
-
-    return;
-
-out:
-    assert(ret);
-    qmp_proxy_spawn_outcome(egc, sdss, ret);
-}
-
-static void spawn_qmp_proxy(libxl__egc *egc,
-                            libxl__stub_dm_spawn_state *sdss)
-{
-    STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
-    const uint32_t guest_domid = sdss->dm.guest_domid;
-    const uint32_t dm_domid = sdss->pvqemu.guest_domid;
-    const char *dom_path = libxl__xs_get_dompath(gc, dm_domid);
-    char **args;
-    int nr = 0;
-    int rc, logfile_w, null;
-
-    if (access(STUBDOM_QMP_PROXY_PATH, X_OK) < 0) {
-        LOGED(ERROR, guest_domid, "qmp proxy %s is not executable", STUBDOM_QMP_PROXY_PATH);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    sdss->qmp_proxy_spawn.what = GCSPRINTF("domain %d device model qmp proxy", guest_domid);
-    sdss->qmp_proxy_spawn.pidpath = GCSPRINTF("%s/image/qmp-proxy-pid", dom_path);
-    sdss->qmp_proxy_spawn.xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID,
-                                                        dm_domid, "/qmp-proxy-state");
-    sdss->qmp_proxy_spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
-    sdss->qmp_proxy_spawn.midproc_cb = libxl__spawn_record_pid;
-    sdss->qmp_proxy_spawn.confirm_cb = qmp_proxy_confirm;
-    sdss->qmp_proxy_spawn.failure_cb = qmp_proxy_startup_failed;
-    sdss->qmp_proxy_spawn.detached_cb = qmp_proxy_detached;
-
-    const int arraysize = 6;
-    GCNEW_ARRAY(args, arraysize);
-    args[nr++] = STUBDOM_QMP_PROXY_PATH;
-    args[nr++] = GCSPRINTF("--state-path=%s", sdss->qmp_proxy_spawn.xspath);
-    args[nr++] = GCSPRINTF("%u", dm_domid);
-    args[nr++] = GCSPRINTF("%s/device-model/%u/qmp-vchan", dom_path, guest_domid);
-    args[nr++] = (char*)libxl__qemu_qmp_path(gc, guest_domid);
-    args[nr++] = NULL;
-    assert(nr == arraysize);
-
-    logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qmp-proxy-%s",
-                                                         sdss->dm_config.c_info.name));
-    if (logfile_w < 0) {
-        rc = logfile_w;
-        goto out;
-    }
-    null = open("/dev/null", O_RDWR);
-    if (null < 0) {
-        LOGED(ERROR, guest_domid, "unable to open /dev/null");
-        rc = ERROR_FAIL;
-        goto out_close;
-    }
-
-    rc = libxl__spawn_spawn(egc, &sdss->qmp_proxy_spawn);
-    if (rc < 0)
-        goto out_close;
-    if (!rc) { /* inner child */
-        setsid();
-        libxl__exec(gc, null, null, logfile_w, STUBDOM_QMP_PROXY_PATH, args, NULL);
-        /* unreachable */
-    }
-
-    rc = 0;
-
-out_close:
-    if (logfile_w >= 0)
-        close(logfile_w);
-    if (null >= 0)
-        close(null);
-out:
-    if (rc)
-        qmp_proxy_spawn_outcome(egc, sdss, rc);
-}
-
-static void qmp_proxy_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
-                              const char *xsdata)
-{
-    STATE_AO_GC(spawn->ao);
-
-    if (!xsdata)
-        return;
-
-    if (strcmp(xsdata, "running"))
-        return;
-
-    libxl__spawn_initiate_detach(gc, spawn);
-}
-
-static void qmp_proxy_startup_failed(libxl__egc *egc,
-                                     libxl__spawn_state *spawn,
-                                     int rc)
-{
-    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
-    qmp_proxy_spawn_outcome(egc, sdss, rc);
-}
-
-static void qmp_proxy_detached(libxl__egc *egc,
-                               libxl__spawn_state *spawn)
-{
-    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(spawn, *sdss, qmp_proxy_spawn);
-    qmp_proxy_spawn_outcome(egc, sdss, 0);
-}
-
-static void qmp_proxy_spawn_outcome(libxl__egc *egc,
-                                    libxl__stub_dm_spawn_state *sdss,
-                                    int rc)
-{
-    STATE_AO_GC(sdss->qmp_proxy_spawn.ao);
-    /*
-     * Until xenconsoled learns how to handle multiple consoles, require qemu
-     * in dom0 to serve consoles for a stubdomain - it require at least 3 of them.
-     */
-    int need_pvqemu = 1 || libxl__need_xenpv_qemu(gc, &sdss->dm_config);
-
-    if (rc) goto out;
-
-    if (need_pvqemu < 0) {
-        rc = need_pvqemu;
-        goto out;
-    }
-
-    sdss->pvqemu.spawn.ao = ao;
-    sdss->pvqemu.guest_config = &sdss->dm_config;
-    sdss->pvqemu.build_state = &sdss->dm_state;
-    sdss->pvqemu.callback = spawn_stubdom_pvqemu_cb;
-    if (need_pvqemu) {
-        libxl__spawn_local_dm(egc, &sdss->pvqemu);
-    } else {
-        /* If dom0 qemu not needed, do not launch it */
-        spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, 0);
-    }
-
-    return;
-
-out:
-    assert(rc);
-    spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, rc);
-}
-
-static void spawn_stubdom_pvqemu_cb(libxl__egc *egc,
-                                libxl__dm_spawn_state *stubdom_dmss,
-                                int rc)
-{
-    libxl__stub_dm_spawn_state *sdss =
-        CONTAINER_OF(stubdom_dmss, *sdss, pvqemu);
-    STATE_AO_GC(sdss->dm.spawn.ao);
-    uint32_t dm_domid = sdss->pvqemu.guest_domid;
-    libxl_domain_config *d_config = stubdom_dmss->guest_config;
-
-    if (rc) goto out;
-
-    if (d_config->num_nics > 0) {
-        libxl__multidev_begin(ao, &sdss->multidev);
-        sdss->multidev.callback = stubdom_pvqemu_cb;
-        libxl__add_nics(egc, ao, dm_domid, d_config, &sdss->multidev);
-        libxl__multidev_prepared(egc, &sdss->multidev, 0);
-        return;
-    }
-
-out:
-    stubdom_pvqemu_cb(egc, &sdss->multidev, rc);
-}
-
-static void stubdom_pvqemu_cb(libxl__egc *egc,
-                              libxl__multidev *multidev,
-                              int rc)
-{
-    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev);
-    STATE_AO_GC(sdss->dm.spawn.ao);
-    uint32_t dm_domid = sdss->pvqemu.guest_domid;
-
-    if (rc) {
-        LOGED(ERROR, sdss->dm.guest_domid,
-              "error connecting nics devices");
-        goto out;
-    }
-
-    sdss->pvqemu.dmrs.ao = ao;
-    sdss->pvqemu.dmrs.domid = dm_domid;
-    sdss->pvqemu.dmrs.callback = stubdom_pvqemu_unpaused;
-    libxl__domain_unpause(egc, &sdss->pvqemu.dmrs); /* must be last */
-    return;
-out:
-    stubdom_pvqemu_unpaused(egc, &sdss->pvqemu.dmrs, rc);
-}
-
-static void stubdom_pvqemu_unpaused(libxl__egc *egc,
-                                    libxl__dm_resume_state *dmrs,
-                                    int rc)
-{
-    libxl__stub_dm_spawn_state *sdss =
-        CONTAINER_OF(dmrs, *sdss, pvqemu.dmrs);
-    STATE_AO_GC(sdss->dm.spawn.ao);
-    uint32_t dm_domid = sdss->pvqemu.guest_domid;
-
-    if (rc) goto out;
-
-    sdss->xswait.ao = ao;
-    sdss->xswait.what = GCSPRINTF("Stubdom %u for %u startup",
-                                  dm_domid, sdss->dm.guest_domid);
-    sdss->xswait.path = DEVICE_MODEL_XS_PATH(gc, dm_domid, sdss->dm.guest_domid,
-                                             "/state");
-    sdss->xswait.timeout_ms = LIBXL_STUBDOM_START_TIMEOUT * 1000;
-    sdss->xswait.callback = stubdom_xswait_cb;
-    rc = libxl__xswait_start(gc, &sdss->xswait);
-    if (rc) goto out;
-
-    return;
-
- out:
-    stubdom_xswait_cb(egc, &sdss->xswait, rc, NULL);
-}
-
-static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait,
-                              int rc, const char *p)
-{
-    EGC_GC;
-    libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(xswait, *sdss, xswait);
-
-    if (rc) {
-        if (rc == ERROR_TIMEDOUT)
-            LOGD(ERROR, sdss->dm.guest_domid,
-                 "%s: startup timed out", xswait->what);
-        goto out;
-    }
-
-    if (!p) return;
-
-    if (strcmp(p, "running"))
-        return;
- out:
-    libxl__domain_build_state_dispose(&sdss->dm_state);
-    libxl__xswait_stop(gc, xswait);
-    dmss_dispose(gc, &sdss->dm);
-    dmss_dispose(gc, &sdss->pvqemu);
-    sdss->callback(egc, &sdss->dm, rc);
-}
-
-/* callbacks passed to libxl__spawn_spawn */
-static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
-                                 const char *xsdata);
-static void device_model_startup_failed(libxl__egc *egc,
-                                        libxl__spawn_state *spawn,
-                                        int rc);
-static void device_model_detached(libxl__egc *egc,
-                                  libxl__spawn_state *spawn);
-static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
-                                const libxl__json_object *response,
-                                int rc);
-
-/* our "next step" function, called from those callbacks and elsewhere */
-static void device_model_spawn_outcome(libxl__egc *egc,
-                                       libxl__dm_spawn_state *dmss,
-                                       int rc);
-static void device_model_postconfig_chardev(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void device_model_postconfig_vnc(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void devise_model_postconfig_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_model_postconfig_done(libxl__egc *egc,
-    libxl__dm_spawn_state *dmss, int rc);
-
-void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss)
-{
-    /* convenience aliases */
-    const int domid = dmss->guest_domid;
-    libxl__domain_build_state *const state = dmss->build_state;
-    libxl__spawn_state *const spawn = &dmss->spawn;
-
-    STATE_AO_GC(dmss->spawn.ao);
-
-    libxl_ctx *ctx = CTX;
-    libxl_domain_config *guest_config = dmss->guest_config;
-    const libxl_domain_create_info *c_info = &guest_config->c_info;
-    const libxl_domain_build_info *b_info = &guest_config->b_info;
-    const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
-    char *path;
-    int logfile_w, null;
-    int rc;
-    char **args, **arg, **envs;
-    xs_transaction_t t;
-    char *vm_path;
-    char **pass_stuff;
-    const char *dm;
-    int dm_state_fd = -1;
-
-    dmss_init(dmss);
-
-    if (libxl_defbool_val(b_info->device_model_stubdomain)) {
-        abort();
-    }
-
-    dm = libxl__domain_device_model(gc, b_info);
-    if (!dm) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (access(dm, X_OK) < 0) {
-        LOGED(ERROR, domid, "device model %s is not executable", dm);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__domain_get_device_model_uid(gc, dmss);
-    if (rc)
-        goto out;
-
-    if (b_info->device_model_version
-            == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
-        libxl_defbool_val(b_info->dm_restrict)) {
-        /* If we have to use dm_restrict, QEMU needs to be new enough
-         * and will have the new interface where we can pre-open the
-         * QMP socket. */
-        rc = libxl__pre_open_qmp_socket(gc, domid, &state->dm_monitor_fd);
-        if (rc) goto out;
-    }
-
-    rc = libxl__build_device_model_args(gc, dm, domid, guest_config,
-                                          &args, &envs, state,
-                                          &dm_state_fd);
-    if (rc)
-        goto out;
-
-    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        path = xs_get_domain_path(ctx->xsh, domid);
-        libxl__xs_printf(gc, XBT_NULL,
-                         GCSPRINTF("%s/hvmloader/bios", path),
-                         "%s", libxl_bios_type_to_string(b_info->u.hvm.bios));
-        /* Disable relocating memory to make the MMIO hole larger
-         * unless we're running qemu-traditional and vNUMA is not
-         * configured. */
-        libxl__xs_printf(gc, XBT_NULL,
-                         GCSPRINTF("%s/hvmloader/allow-memory-relocate", path),
-                         "%d",
-                         b_info->device_model_version==LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL &&
-                         !libxl__vnuma_configured(b_info));
-        free(path);
-    }
-
-    path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
-    xs_mkdir(ctx->xsh, XBT_NULL, path);
-
-    if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
-        b_info->device_model_version
-        == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL)
-        libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/disable_pf", path),
-                         "%d", !libxl_defbool_val(b_info->u.hvm.xen_platform_pci));
-
-    logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qemu-dm-%s",
-                                                         c_info->name));
-    if (logfile_w < 0) {
-        rc = logfile_w;
-        goto out;
-    }
-    null = open("/dev/null", O_RDONLY);
-    if (null < 0) {
-        LOGED(ERROR, domid, "unable to open /dev/null");
-        rc = ERROR_FAIL;
-        goto out_close;
-    }
-
-    const char *dom_path = libxl__xs_get_dompath(gc, domid);
-
-    /*
-     * If we're starting the dm with a non-root UID, save the UID so
-     * that we can reliably kill it and any subprocesses
-     */
-    if (state->dm_kill_uid)
-        libxl__xs_printf(gc, XBT_NULL,
-                         GCSPRINTF("%s/image/device-model-kill-uid", dom_path),
-                         "%s", state->dm_kill_uid);
-
-    if (vnc && vnc->passwd) {
-        /* This xenstore key will only be used by qemu-xen-traditionnal.
-         * The code to supply vncpasswd to qemu-xen is later. */
-retry_transaction:
-        /* Find uuid and the write the vnc password to xenstore for qemu. */
-        t = xs_transaction_start(ctx->xsh);
-        vm_path = libxl__xs_read(gc,t,GCSPRINTF("%s/vm", dom_path));
-        if (vm_path) {
-            /* Now write the vncpassword into it. */
-            pass_stuff = libxl__calloc(gc, 3, sizeof(char *));
-            pass_stuff[0] = "vncpasswd";
-            pass_stuff[1] = vnc->passwd;
-            libxl__xs_writev(gc,t,vm_path,pass_stuff);
-            if (!xs_transaction_end(ctx->xsh, t, 0))
-                if (errno == EAGAIN)
-                    goto retry_transaction;
-        }
-    }
-
-    LOGD(DEBUG, domid, "Spawning device-model %s with arguments:", dm);
-    for (arg = args; *arg; arg++)
-        LOGD(DEBUG, domid, "  %s", *arg);
-    if (*envs) {
-        LOGD(DEBUG, domid, "Spawning device-model %s with additional environment:", dm);
-        for (arg = envs; *arg; arg += 2)
-            LOGD(DEBUG, domid, "  %s=%s", arg[0], arg[1]);
-    }
-
-    spawn->what = GCSPRINTF("domain %d device model", domid);
-    spawn->xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid,
-                                         "/state");
-    spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
-    spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path);
-    spawn->midproc_cb = libxl__spawn_record_pid;
-    spawn->confirm_cb = device_model_confirm;
-    spawn->failure_cb = device_model_startup_failed;
-    spawn->detached_cb = device_model_detached;
-
-    if (state->dm_monitor_fd >= 0) {
-        /* There is a valid QMP socket available now,
-         * use it to find out when QEMU is ready */
-        dmss->qmp.ao = ao;
-        dmss->qmp.callback = device_model_qmp_cb;
-        dmss->qmp.domid = domid;
-        dmss->qmp.payload_fd = -1;
-        rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-status", NULL);
-        if (rc) goto out_close;
-    }
-
-    rc = libxl__spawn_spawn(egc, spawn);
-    if (rc < 0)
-        goto out_close;
-    if (!rc) { /* inner child */
-        setsid();
-        if (libxl_defbool_val(b_info->dm_restrict)) {
-            rc = libxl__local_dm_preexec_restrict(gc);
-            if (rc)
-                _exit(-1);
-        }
-        libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
-    }
-
-    rc = 0;
-
-out_close:
-    if (null >= 0) close(null);
-    if (logfile_w >= 0) close(logfile_w);
-out:
-    if (dm_state_fd >= 0) close(dm_state_fd);
-    if (rc)
-        device_model_spawn_outcome(egc, dmss, rc);
-}
-
-bool libxl__query_qemu_backend(libxl__gc *gc, uint32_t domid,
-                               uint32_t backend_id, const char *type, bool def)
-{
-    char *path;
-    char **dir;
-    unsigned int n;
-
-    path = GCSPRINTF("%s/device-model/%u/backends",
-                     libxl__xs_get_dompath(gc, backend_id), domid);
-    dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
-    if (!dir)
-        return def;
-
-    path = GCSPRINTF("%s/device-model/%u/backends/%s",
-                     libxl__xs_get_dompath(gc, backend_id), domid, type);
-    dir = libxl__xs_directory(gc, XBT_NULL, path, &n);
-
-    return !!dir;
-}
-
-static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn,
-                                 const char *xsdata)
-{
-    STATE_AO_GC(spawn->ao);
-
-    if (!xsdata)
-        return;
-
-    if (strcmp(xsdata, "running"))
-        return;
-
-    libxl__spawn_initiate_detach(gc, spawn);
-}
-
-static void device_model_startup_failed(libxl__egc *egc,
-                                        libxl__spawn_state *spawn,
-                                        int rc)
-{
-    libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
-    device_model_spawn_outcome(egc, dmss, rc);
-}
-
-static void device_model_detached(libxl__egc *egc,
-                                  libxl__spawn_state *spawn)
-{
-    libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn);
-    device_model_spawn_outcome(egc, dmss, 0);
-}
-
-static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev,
-                                const libxl__json_object *response,
-                                int rc)
-{
-    EGC_GC;
-    libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, qmp);
-    const libxl__json_object *o;
-    const char *status;
-    const char *expected_state;
-
-    libxl__ev_qmp_dispose(gc, ev);
-
-    if (rc)
-        goto failed;
-
-    o = libxl__json_map_get("status", response, JSON_STRING);
-    if (!o) {
-        LOGD(ERROR, ev->domid,
-             "Missing 'status' in response to 'query-status'");
-        LOGD(DEBUG, ev->domid, ".. instead, got: %s", JSON(response));
-        rc = ERROR_QEMU_API;
-        goto failed;
-    }
-    status = libxl__json_object_get_string(o);
-    if (!dmss->build_state->saved_state)
-        expected_state = "prelaunch";
-    else
-        expected_state = "paused";
-    if (strcmp(status, expected_state)) {
-        LOGD(ERROR, ev->domid, "Unexpected QEMU status: %s", status);
-        rc = ERROR_NOT_READY;
-        goto failed;
-    }
-
-    libxl__spawn_initiate_detach(gc, &dmss->spawn);
-    return;
-
-failed:
-    LOGD(ERROR, ev->domid, "QEMU did not start properly, rc=%d", rc);
-    libxl__spawn_initiate_failure(egc, &dmss->spawn, rc);
-}
-
-static void device_model_spawn_outcome(libxl__egc *egc,
-                                       libxl__dm_spawn_state *dmss,
-                                       int rc)
-{
-    STATE_AO_GC(dmss->spawn.ao);
-    int ret2;
-
-    /* Convenience aliases */
-    libxl_domain_config *const d_config = dmss->guest_config;
-
-    if (rc)
-        LOGD(ERROR, dmss->guest_domid,
-             "%s: spawn failed (rc=%d)", dmss->spawn.what, rc);
-
-    libxl__domain_build_state *state = dmss->build_state;
-
-    if (state->saved_state) {
-        ret2 = unlink(state->saved_state);
-        if (ret2) {
-            LOGED(ERROR, dmss->guest_domid, "%s: failed to remove device-model state %s",
-                 dmss->spawn.what, state->saved_state);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    /* Check if spawn failed */
-    if (rc) goto out;
-
-    if (d_config->b_info.device_model_version
-            == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
-        rc = libxl__ev_time_register_rel(ao, &dmss->timeout,
-                                         devise_model_postconfig_timeout,
-                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
-        if (rc) goto out;
-        dmss->qmp.ao = ao;
-        dmss->qmp.domid = dmss->guest_domid;
-        dmss->qmp.payload_fd = -1;
-        dmss->qmp.callback = device_model_postconfig_chardev;
-        rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-chardev", NULL);
-        if (rc) goto out;
-        return;
-    }
-
- out:
-    device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-static void device_model_postconfig_chardev(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
-    const libxl__json_object *item = NULL;
-    const libxl__json_object *o = NULL;
-    int i = 0;
-    const char *label;
-    const char *filename;
-    int port;
-    char *endptr;
-    const char *dompath;
-    const char serial[] = "serial";
-    const size_t seriall = sizeof(serial) - 1;
-    const char pty[] = "pty:";
-    const size_t ptyl = sizeof(pty) - 1;
-
-    if (rc) goto out;
-
-    /*
-     * query-chardev response:
-     * [{ 'label': 'str',
-     *    'filename': 'str',
-     *    'frontend-open': 'bool' }, ...]
-     */
-
-    for (i = 0; (item = libxl__json_array_get(response, i)); i++) {
-        o = libxl__json_map_get("label", item, JSON_STRING);
-        if (!o) goto protocol_error;
-        label = libxl__json_object_get_string(o);
-
-        /* Check if the "label" start with "serial". */
-        if (!label || strncmp(label, serial, seriall))
-            continue;
-        port = strtol(label + seriall, &endptr, 10);
-        if (*(label + seriall) == '\0' || *endptr != '\0') {
-            LOGD(ERROR, qmp->domid,
-                 "Invalid serial port number: %s", label);
-            rc = ERROR_QEMU_API;
-            goto out;
-        }
-
-        o = libxl__json_map_get("filename", item, JSON_STRING);
-        if (!o) goto protocol_error;
-        filename = libxl__json_object_get_string(o);
-
-        /* Check if filename start with "pty:" */
-        if (!filename || strncmp(filename, pty, ptyl))
-            continue;
-
-        dompath = libxl__xs_get_dompath(gc, qmp->domid);
-        if (!dompath) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        rc = libxl__xs_printf(gc, XBT_NULL,
-                              GCSPRINTF("%s/serial/%d/tty", dompath, port),
-                              "%s", filename + ptyl);
-        if (rc) goto out;
-    }
-
-    qmp->callback = device_model_postconfig_vnc;
-    rc = libxl__ev_qmp_send(egc, qmp, "query-vnc", NULL);
-    if (rc) goto out;
-    return;
-
-protocol_error:
-    rc = ERROR_QEMU_API;
-    LOGD(ERROR, qmp->domid,
-         "unexpected response to QMP cmd 'query-chardev', received:\n%s",
-         JSON(response));
-out:
-    device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-static void device_model_postconfig_vnc(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
-    const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
-    const libxl__json_object *o;
-    libxl__json_object *args = NULL;
-
-    if (rc) goto out;
-
-    /*
-     * query-vnc response:
-     * { 'enabled': 'bool', '*host': 'str', '*service': 'str' }
-     */
-
-    o = libxl__json_map_get("enabled", response, JSON_BOOL);
-    if (!o) goto protocol_error;
-    if (libxl__json_object_get_bool(o)) {
-        const char *addr, *port;
-        const char *dompath;
-
-        o = libxl__json_map_get("host", response, JSON_STRING);
-        if (!o) goto protocol_error;
-        addr = libxl__json_object_get_string(o);
-        o = libxl__json_map_get("service", response, JSON_STRING);
-        if (!o) goto protocol_error;
-        port = libxl__json_object_get_string(o);
-
-        dompath = libxl__xs_get_dompath(gc, qmp->domid);
-        if (!dompath) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        rc = libxl__xs_printf(gc, XBT_NULL,
-                              GCSPRINTF("%s/console/vnc-listen", dompath),
-                              "%s", addr);
-        if (rc) goto out;
-        rc = libxl__xs_printf(gc, XBT_NULL,
-                              GCSPRINTF("%s/console/vnc-port", dompath),
-                              "%s", port);
-        if (rc) goto out;
-    }
-
-    if (vnc && vnc->passwd && vnc->passwd[0]) {
-        qmp->callback = device_model_postconfig_vnc_passwd;
-        libxl__qmp_param_add_string(gc, &args, "password", vnc->passwd);
-        rc = libxl__ev_qmp_send(egc, qmp, "change-vnc-password", args);
-        if (rc) goto out;
-        return;
-    }
-
-    rc = 0;
-    goto out;
-
-protocol_error:
-    rc = ERROR_QEMU_API;
-    LOGD(ERROR, qmp->domid,
-         "unexpected response to QMP cmd 'query-vnc', received:\n%s",
-         JSON(response));
-out:
-    device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-static void device_model_postconfig_vnc_passwd(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp);
-    const libxl_vnc_info *vnc = libxl__dm_vnc(dmss->guest_config);
-    const char *dompath;
-
-    if (rc) goto out;
-
-    dompath = libxl__xs_get_dompath(gc, qmp->domid);
-    if (!dompath) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = libxl__xs_printf(gc, XBT_NULL,
-                          GCSPRINTF("%s/console/vnc-pass", dompath),
-                          "%s", vnc->passwd);
-
-out:
-    device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-void devise_model_postconfig_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                                     const struct timeval *requested_abs,
-                                     int rc)
-{
-    libxl__dm_spawn_state *dmss = CONTAINER_OF(ev, *dmss, timeout);
-    device_model_postconfig_done(egc, dmss, rc); /* must be last */
-}
-
-
-static void device_model_postconfig_done(libxl__egc *egc,
-                                         libxl__dm_spawn_state *dmss,
-                                         int rc)
-{
-    EGC_GC;
-
-    if (rc)
-        LOGD(ERROR, dmss->guest_domid,
-             "Post DM startup configs failed, rc=%d", rc);
-    dmss_dispose(gc, dmss);
-    dmss->callback(egc, dmss, rc);
-}
-
-void libxl__spawn_qdisk_backend(libxl__egc *egc, libxl__dm_spawn_state *dmss)
-{
-    STATE_AO_GC(dmss->spawn.ao);
-    flexarray_t *dm_args, *dm_envs;
-    char **args, **envs;
-    const char *dm;
-    int logfile_w, null = -1, rc;
-    uint32_t domid = dmss->guest_domid;
-
-    dmss_init(dmss);
-
-    /* Always use qemu-xen as device model */
-    dm = qemu_xen_path(gc);
-
-    dm_args = flexarray_make(gc, 15, 1);
-    dm_envs = flexarray_make(gc, 1, 1);
-
-    flexarray_vappend(dm_args, dm, "-xen-domid",
-                      GCSPRINTF("%d", domid), NULL);
-    flexarray_append(dm_args, "-xen-attach");
-    flexarray_vappend(dm_args, "-name",
-                      GCSPRINTF("domain-%u", domid), NULL);
-    flexarray_append(dm_args, "-nographic");
-    flexarray_vappend(dm_args, "-M", "xenpv", NULL);
-    flexarray_vappend(dm_args, "-monitor", "/dev/null", NULL);
-    flexarray_vappend(dm_args, "-serial", "/dev/null", NULL);
-    flexarray_vappend(dm_args, "-parallel", "/dev/null", NULL);
-    flexarray_append(dm_args, NULL);
-    args = (char **) flexarray_contents(dm_args);
-
-    libxl__set_qemu_env_for_xsa_180(gc, dm_envs);
-    envs = (char **) flexarray_contents(dm_envs);
-
-    logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qdisk-%u", domid));
-    if (logfile_w < 0) {
-        rc = logfile_w;
-        goto out;
-    }
-    null = open("/dev/null", O_RDONLY);
-    if (null < 0) {
-       rc = ERROR_FAIL;
-       goto out;
-    }
-
-    dmss->guest_config = NULL;
-    /*
-     * Clearly specify Qemu not using a saved state, so
-     * device_model_spawn_outcome doesn't try to unlink it.
-     */
-    dmss->build_state = libxl__zalloc(gc, sizeof(*dmss->build_state));
-    dmss->build_state->saved_state = 0;
-
-    dmss->spawn.what = GCSPRINTF("domain %u Qdisk backend", domid);
-    dmss->spawn.xspath = GCSPRINTF("device-model/%u/state", domid);
-    dmss->spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
-    /*
-     * We cannot save Qemu pid anywhere in the xenstore guest dir,
-     * because we will call this from unprivileged driver domains,
-     * so save it in the current domain libxl private dir.
-     */
-    dmss->spawn.pidpath = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
-    dmss->spawn.midproc_cb = libxl__spawn_record_pid;
-    dmss->spawn.confirm_cb = device_model_confirm;
-    dmss->spawn.failure_cb = device_model_startup_failed;
-    dmss->spawn.detached_cb = device_model_detached;
-    rc = libxl__spawn_spawn(egc, &dmss->spawn);
-    if (rc < 0)
-        goto out;
-    if (!rc) { /* inner child */
-        setsid();
-        libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs);
-    }
-
-    rc = 0;
-out:
-    dmss_dispose(gc, dmss);
-    if (logfile_w >= 0) close(logfile_w);
-    if (null >= 0) close(null);
-    /* callback on error only, success goes via dmss->spawn.*_cb */
-    if (rc) dmss->callback(egc, dmss, rc);
-    return;
-}
-
-/* Generic function to signal a Qemu instance to exit */
-static int kill_device_model(libxl__gc *gc, const char *xs_path_pid)
-{
-    return libxl__kill_xs_path(gc, xs_path_pid, "Device Model");
-}
-
-/* Helper to destroy a Qdisk backend */
-int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid)
-{
-    char *pid_path;
-    int rc;
-
-    pid_path = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid);
-
-    rc = kill_device_model(gc, pid_path);
-    if (rc)
-        goto out;
-
-    libxl__xs_rm_checked(gc, XBT_NULL, pid_path);
-    libxl__xs_rm_checked(gc, XBT_NULL,
-                         GCSPRINTF("device-model/%u", domid));
-
-out:
-    return rc;
-}
-
-/* Asynchronous device model destroy functions */
-
-static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
-                                       const char *dm_kill_uid_str);
-
-static void kill_device_model_uid_cb(libxl__egc *egc,
-                                     libxl__ev_child *destroyer,
-                                     pid_t pid, int status);
-
-/*
- * If we have a uid, we shouldn't kill by pid.  This is because a
- * hostile QEMU might have exited, in which case the pid we have may
- * be that of another process.
- *
- * The running devicemodel has permission over a specific domain id;
- * this means that ideally we wouldn't the domain in question (freeing
- * up the domain id for reuse) until we're confident that we've killed
- * the domain.
- *
- * In general, destroy as much as we can; but return an error if there
- * are any errors, so that the domain destroy will be aborted, and the
- * domain itself will remain, giving the admin an opportunity to fix
- * any issues and re-try the domain destroy.
- */
-void libxl__destroy_device_model(libxl__egc *egc,
-                                 libxl__destroy_devicemodel_state *ddms)
-{
-    STATE_AO_GC(ddms->ao);
-    int rc;
-    int domid = ddms->domid;
-    char *path;
-    const char *dm_kill_uid_str = NULL;
-    int reaper_pid;
-
-    ddms->rc = 0;
-
-    path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "");
-    rc = libxl__xs_rm_checked(gc, XBT_NULL, path);
-    if (rc) {
-        ACCUMULATE_RC(ddms->rc);
-        LOGD(ERROR, domid, "xs_rm failed for %s", path);
-    }
-
-    /*
-     * See if we should try to kill by uid
-     */
-    path = GCSPRINTF("/local/domain/%d/image/device-model-kill-uid", domid);
-    rc = libxl__xs_read_checked(gc, XBT_NULL, path, &dm_kill_uid_str);
-
-    /*
-     * If there was an error here, accumulate the error and fall back
-     * to killing by pid.
-     */
-    if (rc) {
-        /* 
-         * Technically the state of the string passed to libxl__xs_read_checked() is
-         * "undefined" in the case rc == 0 (according to libxl_internal.h).  Set it to
-         * NULL to prevent undefined behavior.
-         */
-        dm_kill_uid_str = NULL;
-        ACCUMULATE_RC(ddms->rc);
-        LOGD(ERROR, domid, "Reading dm UID path failed for %s", path);
-    }
-
-    /* The DM has its own uid; Attempt to kill all processes with that UID */
-    if (dm_kill_uid_str) {
-        LOGD(DEBUG, domid, "Found DM uid %s, destroying by uid",
-             dm_kill_uid_str);
-
-        reaper_pid = libxl__ev_child_fork(gc, &ddms->destroyer,
-                                          kill_device_model_uid_cb);
-        if (reaper_pid < 0) {
-            rc = ERROR_FAIL;
-            ACCUMULATE_RC(ddms->rc);
-            /*
-             * Note that if this fails, we still don't kill by pid, to
-             * make sure that an untrusted DM has not "maliciously"
-             * exited (potentially causing us to kill an unrelated
-             * process which happened to get the same pid).
-             */
-            goto out;
-        }
-
-        if (!reaper_pid) {  /* child */
-            rc = kill_device_model_uid_child(ddms, dm_kill_uid_str);
-            assert(rc <= 0 && rc >= -125);
-            _exit(-rc);
-        }
-
-        /*
-         * Parent of successful fork; execution will pick up in
-         * kill_device_model_uid_cb when child exits
-         */
-        return;
-    }
-
-    /*
-     * No uid to kill; attept to kill by pid.
-     */
-    LOGD(DEBUG, domid, "Didn't find dm UID; destroying by pid");
-
-    path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
-    rc = kill_device_model(gc, path);
-
-    if (rc) {
-        ACCUMULATE_RC(ddms->rc);
-        LOGD(ERROR, domid, "Killing device model pid from path %s", path);
-    }
-
-out:
-    /*
-     * NB that we always pass '0' here for the "status of exited
-     * process"; since there is no process, it always "succeeds".
-     * Errors are accumulated in ddms->rc and will be handled
-     * correctly.
-     */
-    kill_device_model_uid_cb(egc, &ddms->destroyer, -1, 0);
-    return;
-}
-
-/* 
- * Note that this attempts to grab a file lock, so must be called from
- * a sub-process.
- */
-static int get_reaper_lock_and_uid(libxl__destroy_devicemodel_state *ddms,
-                                   uid_t *reaper_uid)
-{
-    STATE_AO_GC(ddms->ao);
-    int domid = ddms->domid;
-    int r;
-    const char * lockfile;
-    int fd; /* "leaked" into the general process context (even on failure) */
-
-    /* Try to lock the "reaper uid" */
-    lockfile = GCSPRINTF("%s/dm-reaper-lock", libxl__run_dir_path());
-
-    /*
-     * NB that since we've just forked, we can't have any
-     * threads; so we don't need the libxl__carefd
-     * infrastructure here.
-     */
-    fd = open(lockfile, O_RDWR|O_CREAT, 0644);
-    if (fd < 0) {
-        LOGED(ERROR, domid,
-              "unexpected error while trying to open lockfile %s, errno=%d",
-              lockfile, errno);
-        return ERROR_FAIL;
-    }
-
-    /* Try to lock the file, retrying on EINTR */
-    for (;;) {
-        r = flock(fd, LOCK_EX);
-        if (!r)
-            break;
-        if (errno != EINTR) {
-            /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
-            LOGED(ERROR, domid,
-                  "unexpected error while trying to lock %s, fd=%d, errno=%d",
-                  lockfile, fd, errno);
-            return ERROR_FAIL;
-        }
-    }
-
-    /*
-     * Get reaper_uid.  If we can't find such a uid, return an error.
-     */
-    return libxl__get_reaper_uid(gc, reaper_uid);
-}
-
-
-/*
- * Destroy all processes of the given uid by setresuid to the
- * specified uid and kill(-1).  NB this MUST BE CALLED FROM A SEPARATE
- * PROCESS from the normal libxl process, and should exit immediately
- * after return.  Returns a libxl-style error code that is guaranteed
- * to be >= -125.
- */
-static int kill_device_model_uid_child(libxl__destroy_devicemodel_state *ddms,
-                                       const char *dm_kill_uid_str) {
-    STATE_AO_GC(ddms->ao);
-    int domid = ddms->domid;
-    int r, rc = 0;
-    uid_t dm_kill_uid = atoi(dm_kill_uid_str);
-    uid_t reaper_uid;
-
-    /*
-     * Try to kill the devicemodel by uid.  The safest way to do this
-     * is to set euid == dm_uid, but the ruid to something else.  If
-     * we can't get a separate ruid, carry on trying to kill the
-     * process anyway using dm_uid for the ruid.  This is racy (the dm
-     * may be able to kill(-1) us before we kill them), but worth
-     * trying.
-     *
-     * NB: Even if we don't have a separate reaper_uid, the parent can
-     * know whether we won the race by looking at the status variable;
-     * so we don't strictly need to return failure in this case.  But
-     * if there's a misconfiguration, it's better to alert the
-     * administator sooner rather than later; so if we fail to get a
-     * reaper uid, report an error even if the kill succeeds.
-     */
-    rc = get_reaper_lock_and_uid(ddms, &reaper_uid);
-    if (rc) {
-        reaper_uid = dm_kill_uid;
-        LOGD(WARN, domid, "Couldn't get separate reaper uid;"
-            "carrying on with unsafe kill");
-    }
-
-    /*
-     * Should never happen; but if it does, better to have the
-     * toolstack crash with an error than nuking dom0.
-      */
-    assert(reaper_uid);
-    assert(dm_kill_uid);
-
-    LOGD(DEBUG, domid, "DM reaper: calling setresuid(%d, %d, 0)",
-         reaper_uid, dm_kill_uid);
-    r = setresuid(reaper_uid, dm_kill_uid, 0);
-    if (r) {
-        LOGED(ERROR, domid, "setresuid to (%d, %d, 0)",
-              reaper_uid, dm_kill_uid);
-        rc = rc ?: ERROR_FAIL;
-        goto out;
-    }
-
-    /*
-     * And kill everyone but me.
-     *
-     * NB that it's not clear from either POSIX or the Linux man page
-     * that ESRCH would be returned with a pid value of -1, but it
-     * doesn't hurt to check.
-     */
-    r = kill(-1, 9);
-    if (r && errno != ESRCH) {
-        LOGED(ERROR, domid, "kill(-1,9)");
-        rc = rc ?: ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static void kill_device_model_uid_cb(libxl__egc *egc,
-                                    libxl__ev_child *destroyer,
-                                    pid_t pid, int status)
-{
-    libxl__destroy_devicemodel_state *ddms = CONTAINER_OF(destroyer, *ddms, destroyer);
-    STATE_AO_GC(ddms->ao);
-
-    if (status) {
-        int rc = ERROR_FAIL;
-
-        if (WIFEXITED(status) && WEXITSTATUS(status) <= 125)
-            rc = -WEXITSTATUS(status);
-
-        ACCUMULATE_RC(ddms->rc);
-        libxl_report_child_exitstatus(CTX, XTL_ERROR,
-                                      "async domain destroy", pid, status);
-    }
-
-    /* Always try to clean up qmp, even if something went wrong */
-    libxl__qmp_cleanup(gc, ddms->domid);
-
-    ddms->callback(egc, ddms, ddms->rc);
-}
-
-/* Return 0 if no dm needed, 1 if needed and <0 if error. */
-int libxl__need_xenpv_qemu(libxl__gc *gc, libxl_domain_config *d_config)
-{
-    int idx, i, ret, num;
-    uint32_t domid;
-    const libxl__device_type *dt;
-
-    ret = libxl__get_domid(gc, &domid);
-    if (ret) {
-        LOG(ERROR, "unable to get domain id");
-        goto out;
-    }
-
-    if (d_config->num_vfbs > 0 || d_config->num_p9s > 0) {
-        ret = 1;
-        goto out;
-    }
-
-    for (idx = 0;; idx++) {
-        dt = device_type_tbl[idx];
-        if (!dt)
-            break;
-
-        num = *libxl__device_type_get_num(dt, d_config);
-        if (!dt->dm_needed || !num)
-            continue;
-
-        for (i = 0; i < num; i++) {
-            if (dt->dm_needed(libxl__device_type_get_elem(dt, d_config, i),
-                              domid)) {
-                ret = 1;
-                goto out;
-            }
-        }
-    }
-
-    for (i = 0; i < d_config->num_channels; i++) {
-        if (d_config->channels[i].backend_domid == domid) {
-            /* xenconsoled is limited to the first console only.
-               Until this restriction is removed we must use qemu for
-               secondary consoles which includes all channels. */
-            ret = 1;
-            goto out;
-        }
-    }
-
-out:
-    return ret;
-}
-
-int libxl__dm_active(libxl__gc *gc, uint32_t domid)
-{
-    char *pid, *dm_domid, *path;
-
-    path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid);
-    pid = libxl__xs_read(gc, XBT_NULL, path);
-
-    if (pid)
-        return true;
-
-    path = GCSPRINTF("/local/domain/%d/image/device-model-domid", domid);
-    dm_domid = libxl__xs_read(gc, XBT_NULL, path);
-
-    return dm_domid != NULL;
-}
-
-int libxl__dm_check_start(libxl__gc *gc, libxl_domain_config *d_config,
-                          uint32_t domid)
-{
-    int rc;
-
-    if (libxl__dm_active(gc, domid))
-        return 0;
-
-    rc = libxl__need_xenpv_qemu(gc, d_config);
-    if (rc < 0)
-        goto out;
-
-    if (!rc)
-        return 0;
-
-    LOGD(ERROR, domid, "device model required but not running");
-    rc = ERROR_FAIL;
-
-out:
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_dom.c b/tools/libxl/libxl_dom.c
deleted file mode 100644 (file)
index 597a682..0000000
+++ /dev/null
@@ -1,1469 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <glob.h>
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-#include <xen/hvm/hvm_info_table.h>
-#include <xen/hvm/hvm_xs_strings.h>
-#include <xen/hvm/e820.h>
-
-#include "_paths.h"
-
-//#define DEBUG 1
-
-libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    xc_domaininfo_t info;
-    int ret;
-
-    ret = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
-    if (ret != 1 || info.domain != domid) {
-        LOG(ERROR, "unable to get domain type for domid=%"PRIu32, domid);
-        return LIBXL_DOMAIN_TYPE_INVALID;
-    }
-    if (info.flags & XEN_DOMINF_hvm_guest) {
-        const char *type_path = GCSPRINTF("%s/type",
-                                          libxl__xs_libxl_path(gc, domid));
-        const char *type;
-        libxl_domain_type t;
-        int rc;
-
-        rc = libxl__xs_read_mandatory(gc, XBT_NULL, type_path, &type);
-        if (rc) {
-            LOG(WARN,
-            "unable to get domain type for domid=%"PRIu32", assuming HVM",
-                domid);
-            return LIBXL_DOMAIN_TYPE_HVM;
-        }
-
-        rc = libxl_domain_type_from_string(type, &t);
-        if (rc) {
-            LOG(WARN,
-            "unable to get domain type for domid=%"PRIu32", assuming HVM",
-                domid);
-            return LIBXL_DOMAIN_TYPE_HVM;
-        }
-
-        return t;
-    } else
-        return LIBXL_DOMAIN_TYPE_PV;
-}
-
-int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid)
-{
-    xc_domaininfo_t info;
-    int ret;
-
-    ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
-    if (ret != 1)
-    {
-        LOGE(ERROR, "getinfolist failed %d", ret);
-        return ERROR_FAIL;
-    }
-    if (info.domain != domid)
-    {
-        LOGE(ERROR, "got info for dom%d, wanted dom%d\n", info.domain, domid);
-        return ERROR_FAIL;
-    }
-    return info.cpupool;
-}
-
-libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid)
-{
-    int cpupool = libxl__domain_cpupool(gc, domid);
-    libxl_cpupoolinfo poolinfo;
-    libxl_scheduler sched = LIBXL_SCHEDULER_UNKNOWN;
-    int rc;
-
-    if (cpupool < 0)
-        return sched;
-
-    libxl_cpupoolinfo_init(&poolinfo);
-    rc = libxl_cpupool_info(CTX, &poolinfo, cpupool);
-    if (rc < 0)
-        goto out;
-
-    sched = poolinfo.sched;
-
-out:
-    libxl_cpupoolinfo_dispose(&poolinfo);
-    return sched;
-}
-
-/*
- * Two NUMA placement candidates are compared by means of the following
- * heuristics:
-
- *  - the number of vcpus runnable on the candidates is considered, and
- *    candidates with fewer of them are preferred. If two candidate have
- *    the same number of runnable vcpus,
- *  - the amount of free memory in the candidates is considered, and the
- *    candidate with greater amount of it is preferred.
- *
- * In fact, leaving larger memory holes, maximizes the probability of being
- * able to put other domains on the node. That hopefully means many domains
- * will benefit from local memory accesses, but also introduces the risk of
- * overloading large (from a memory POV) nodes. That's right the effect
- * that counting the vcpus able to run on the nodes tries to prevent.
- *
- * Note that this completely ignore the number of nodes each candidate span,
- * as the fact that fewer nodes is better is already accounted for in the
- * algorithm.
- */
-static int numa_cmpf(const libxl__numa_candidate *c1,
-                     const libxl__numa_candidate *c2)
-{
-    if (c1->nr_vcpus != c2->nr_vcpus)
-        return c1->nr_vcpus - c2->nr_vcpus;
-
-    return c2->free_memkb - c1->free_memkb;
-}
-
-/* The actual automatic NUMA placement routine */
-static int numa_place_domain(libxl__gc *gc, uint32_t domid,
-                             libxl_domain_config *d_config)
-{
-    libxl_domain_build_info *info = &d_config->b_info;
-    int found;
-    libxl__numa_candidate candidate;
-    libxl_bitmap cpumap, cpupool_nodemap, *map;
-    libxl_cpupoolinfo cpupool_info;
-    int i, cpupool, rc = 0;
-    uint64_t memkb;
-
-    libxl__numa_candidate_init(&candidate);
-    libxl_bitmap_init(&cpumap);
-    libxl_bitmap_init(&cpupool_nodemap);
-    libxl_cpupoolinfo_init(&cpupool_info);
-
-    /*
-     * Extract the cpumap from the cpupool the domain belong to. In fact,
-     * it only makes sense to consider the cpus/nodes that are in there
-     * for placement.
-     */
-    rc = cpupool = libxl__domain_cpupool(gc, domid);
-    if (rc < 0)
-        goto out;
-    rc = libxl_cpupool_info(CTX, &cpupool_info, cpupool);
-    if (rc)
-        goto out;
-    map = &cpupool_info.cpumap;
-
-    /*
-     * If there's a well defined hard affinity mask (i.e., the same one for all
-     * the vcpus), we can try to run the placement considering only the pcpus
-     * within such mask.
-     */
-    if (info->num_vcpu_hard_affinity)
-    {
-#ifdef DEBUG
-        int j;
-
-        for (j = 0; j < info->num_vcpu_hard_affinity; j++)
-            assert(libxl_bitmap_equal(&info->vcpu_hard_affinity[0],
-                                      &info->vcpu_hard_affinity[j], 0));
-#endif /* DEBUG */
-
-        rc = libxl_bitmap_and(CTX, &cpumap, &info->vcpu_hard_affinity[0],
-                              &cpupool_info.cpumap);
-        if (rc)
-            goto out;
-
-        /* Hard affinity must contain at least one cpu of our cpupool */
-        if (libxl_bitmap_is_empty(&cpumap)) {
-            LOG(ERROR, "Hard affinity completely outside of domain's cpupool!");
-            rc = ERROR_INVAL;
-            goto out;
-        }
-    }
-
-    rc = libxl__domain_need_memory_calculate(gc, info, &memkb);
-    if (rc)
-        goto out;
-    if (libxl_node_bitmap_alloc(CTX, &cpupool_nodemap, 0)) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /* Find the best candidate with enough free memory and at least
-     * as much pcpus as the domain has vcpus.  */
-    rc = libxl__get_numa_candidate(gc, memkb, info->max_vcpus,
-                                   0, 0, map, numa_cmpf, &candidate, &found);
-    if (rc)
-        goto out;
-
-    /* Not even a suitable placement candidate! Let's just don't touch the
-     * domain's info->cpumap. It will have affinity with all nodes/cpus. */
-    if (found == 0)
-        goto out;
-
-    /* Map the candidate's node map to the domain's info->nodemap */
-    libxl__numa_candidate_get_nodemap(gc, &candidate, &info->nodemap);
-
-    /* Avoid trying to set the affinity to nodes that might be in the
-     * candidate's nodemap but out of our cpupool. */
-    rc = libxl_cpumap_to_nodemap(CTX, &cpupool_info.cpumap,
-                                 &cpupool_nodemap);
-    if (rc)
-        goto out;
-
-    libxl_for_each_set_bit(i, info->nodemap) {
-        if (!libxl_bitmap_test(&cpupool_nodemap, i))
-            libxl_bitmap_reset(&info->nodemap, i);
-    }
-
-    LOG(DETAIL, "NUMA placement candidate with %d nodes, %d cpus and "
-                "%"PRIu64" KB free selected", candidate.nr_nodes,
-                candidate.nr_cpus, candidate.free_memkb / 1024);
-
- out:
-    libxl__numa_candidate_dispose(&candidate);
-    libxl_bitmap_dispose(&cpupool_nodemap);
-    libxl_bitmap_dispose(&cpumap);
-    libxl_cpupoolinfo_dispose(&cpupool_info);
-    return rc;
-}
-
-int libxl__build_pre(libxl__gc *gc, uint32_t domid,
-              libxl_domain_config *d_config, libxl__domain_build_state *state)
-{
-    libxl_domain_build_info *const info = &d_config->b_info;
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *xs_domid, *con_domid;
-    int rc;
-    uint64_t size;
-
-    if (xc_domain_max_vcpus(ctx->xch, domid, info->max_vcpus) != 0) {
-        LOG(ERROR, "Couldn't set max vcpu count");
-        return ERROR_FAIL;
-    }
-
-    /*
-     * Check if the domain has any CPU or node affinity already. If not, try
-     * to build up the latter via automatic NUMA placement. In fact, in case
-     * numa_place_domain() manage to find a placement, in info->nodemap is
-     * updated accordingly; if it does not manage, info->nodemap is just left
-     * alone. It is then the the subsequent call to
-     * libxl_domain_set_nodeaffinity() that enacts the actual placement.
-     *
-     * As far as scheduling is concerned, we achieve NUMA-aware scheduling
-     * by having the results of placement affect the soft affinity of all
-     * the vcpus of the domain. Of course, we want that iff placement is
-     * enabled and actually happens, so we only change info->cpumap_soft to
-     * reflect the placement result if that is the case
-     */
-    if (libxl_defbool_val(info->numa_placement)) {
-        if (info->cpumap.size || info->num_vcpu_soft_affinity)
-            LOG(WARN, "Can't run NUMA placement, as a soft "
-                      "affinity has been specified explicitly");
-        else if (info->nodemap.size)
-            LOG(WARN, "Can't run NUMA placement, as the domain has "
-                      "NUMA node affinity set already");
-        else {
-            libxl_bitmap cpumap_soft;
-
-            rc = libxl_node_bitmap_alloc(ctx, &info->nodemap, 0);
-            if (rc)
-                return rc;
-            libxl_bitmap_set_any(&info->nodemap);
-
-            rc = libxl_cpu_bitmap_alloc(ctx, &cpumap_soft, 0);
-            if (rc)
-                return rc;
-
-            rc = numa_place_domain(gc, domid, d_config);
-            if (rc) {
-                libxl_bitmap_dispose(&cpumap_soft);
-                return rc;
-            }
-
-            /*
-             * All we need to do now is converting the result of automatic
-             * placement from nodemap to cpumap, and then use such cpumap
-             * as the soft affinity for all the vcpus of the domain.
-             *
-             * When calling libxl_set_vcpuaffinity_all(), it is ok to use
-             * NULL as hard affinity, as we know we don't have one, or we
-             * won't be here.
-             */
-            libxl_nodemap_to_cpumap(ctx, &info->nodemap, &cpumap_soft);
-            libxl_set_vcpuaffinity_all(ctx, domid, info->max_vcpus,
-                                       NULL, &cpumap_soft);
-
-            libxl_bitmap_dispose(&cpumap_soft);
-
-            /*
-             * Placement has run, so avoid for it to be re-run, if this
-             * same config we are using and building here is ever re-used.
-             * This means that people re-using configs will get the same
-             * results, consistently, across every re-use, which is what
-             * we expect most people to want.
-             */
-            libxl_defbool_set(&info->numa_placement, false);
-        }
-    }
-
-    if (info->nodemap.size)
-        libxl_domain_set_nodeaffinity(ctx, domid, &info->nodemap);
-
-    if (info->num_vcpu_hard_affinity || info->num_vcpu_soft_affinity) {
-        libxl_bitmap *hard_affinity, *soft_affinity;
-        int i, n_vcpus;
-
-        n_vcpus = info->num_vcpu_hard_affinity > info->num_vcpu_soft_affinity ?
-            info->num_vcpu_hard_affinity : info->num_vcpu_soft_affinity;
-
-        for (i = 0; i < n_vcpus; i++) {
-            /*
-             * Prepare hard and soft affinity pointers in a way that allows
-             * us to issue only one call to libxl_set_vcpuaffinity(), setting,
-             * for each vcpu, both hard and soft affinity "atomically".
-             */
-            hard_affinity = NULL;
-            if (info->num_vcpu_hard_affinity &&
-                i < info->num_vcpu_hard_affinity)
-                hard_affinity = &info->vcpu_hard_affinity[i];
-
-            soft_affinity = NULL;
-            if (info->num_vcpu_soft_affinity &&
-                i < info->num_vcpu_soft_affinity)
-                soft_affinity = &info->vcpu_soft_affinity[i];
-
-            if (libxl_set_vcpuaffinity(ctx, domid, i,
-                                       hard_affinity, soft_affinity)) {
-                LOG(ERROR, "setting affinity failed on vcpu `%d'", i);
-                return ERROR_FAIL;
-            }
-        }
-    }
-
-
-    rc = libxl__arch_extra_memory(gc, info, &size);
-    if (rc < 0) {
-        LOGE(ERROR, "Couldn't get arch extra constant memory size");
-        return ERROR_FAIL;
-    }
-
-    if (xc_domain_setmaxmem(ctx->xch, domid, info->target_memkb + size) < 0) {
-        LOGE(ERROR, "Couldn't set max memory");
-        return ERROR_FAIL;
-    }
-
-    xs_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenstored/domid", NULL);
-    state->store_domid = xs_domid ? atoi(xs_domid) : 0;
-    free(xs_domid);
-
-    con_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenconsoled/domid", NULL);
-    state->console_domid = con_domid ? atoi(con_domid) : 0;
-    free(con_domid);
-
-    state->store_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->store_domid);
-    state->console_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->console_domid);
-
-    rc = libxl__arch_domain_create(gc, d_config, domid);
-
-    /* Construct a CPUID policy, but only for brand new domains.  Domains
-     * being migrated-in/restored have CPUID handled during the
-     * static_data_done() callback. */
-    if (!state->restore)
-        libxl__cpuid_legacy(ctx, domid, false, info);
-
-    return rc;
-}
-
-static int set_vnuma_affinity(libxl__gc *gc, uint32_t domid,
-                              libxl_domain_build_info *info)
-{
-    libxl_bitmap cpumap;
-    libxl_vnode_info *v;
-    unsigned int i, j;
-    int rc = 0;
-
-    libxl_bitmap_init(&cpumap);
-
-    rc = libxl_cpu_bitmap_alloc(CTX, &cpumap, 0);
-    if (rc) {
-        LOG(ERROR, "Can't allocate nodemap");
-        goto out;
-    }
-
-    /*
-     * For each vcpu in each vnode, set its soft affinity to
-     * the pcpus belonging to the pnode the vnode is on
-     */
-    for (i = 0; i < info->num_vnuma_nodes; i++) {
-        v = &info->vnuma_nodes[i];
-
-        rc = libxl_node_to_cpumap(CTX, v->pnode, &cpumap);
-        if (rc) {
-            LOG(ERROR, "Can't get cpumap for vnode %d", i);
-            goto out;
-        }
-
-        libxl_for_each_set_bit(j, v->vcpus) {
-            rc = libxl_set_vcpuaffinity(CTX, domid, j, NULL, &cpumap);
-            if (rc) {
-                LOG(ERROR, "Can't set cpu affinity for %d", j);
-                goto out;
-            }
-        }
-    }
-
-out:
-    libxl_bitmap_dispose(&cpumap);
-    return rc;
-}
-
-int libxl__build_post(libxl__gc *gc, uint32_t domid,
-                      libxl_domain_build_info *info,
-                      libxl__domain_build_state *state,
-                      char **vms_ents, char **local_ents)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *dom_path, *vm_path;
-    xs_transaction_t t;
-    char **ents;
-    int i, rc;
-
-    if (info->num_vnuma_nodes && !info->num_vcpu_soft_affinity) {
-        rc = set_vnuma_affinity(gc, domid, info);
-        if (rc)
-            return rc;
-    }
-
-    rc = libxl_domain_sched_params_set(CTX, domid, &info->sched_params);
-    if (rc)
-        return rc;
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM
-        && !libxl_ms_vm_genid_is_zero(&info->u.hvm.ms_vm_genid)) {
-        rc = libxl__ms_vm_genid_set(gc, domid,
-                                    &info->u.hvm.ms_vm_genid);
-        if (rc) {
-            LOG(ERROR, "Failed to set VM Generation ID");
-            return rc;
-        }
-    }
-
-    ents = libxl__calloc(gc, 12 + (info->max_vcpus * 2) + 2, sizeof(char *));
-    ents[0] = "memory/static-max";
-    ents[1] = GCSPRINTF("%"PRId64, info->max_memkb);
-    ents[2] = "memory/target";
-    ents[3] = GCSPRINTF("%"PRId64, info->target_memkb -
-                        libxl__get_targetmem_fudge(gc, info));
-    ents[4] = "memory/videoram";
-    ents[5] = GCSPRINTF("%"PRId64, info->video_memkb);
-    ents[6] = "domid";
-    ents[7] = GCSPRINTF("%d", domid);
-    ents[8] = "store/port";
-    ents[9] = GCSPRINTF("%"PRIu32, state->store_port);
-    ents[10] = "store/ring-ref";
-    ents[11] = GCSPRINTF("%lu", state->store_mfn);
-    for (i = 0; i < info->max_vcpus; i++) {
-        ents[12+(i*2)]   = GCSPRINTF("cpu/%d/availability", i);
-        ents[12+(i*2)+1] = libxl_bitmap_test(&info->avail_vcpus, i)
-                            ? "online" : "offline";
-    }
-
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path) {
-        return ERROR_FAIL;
-    }
-
-    vm_path = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/vm", dom_path), NULL);
-retry_transaction:
-    t = xs_transaction_start(ctx->xsh);
-
-    libxl__xs_writev(gc, t, dom_path, ents);
-    libxl__xs_writev(gc, t, dom_path, local_ents);
-    libxl__xs_writev(gc, t, vm_path, vms_ents);
-
-    if (!xs_transaction_end(ctx->xsh, t, 0))
-        if (errno == EAGAIN)
-            goto retry_transaction;
-    xs_introduce_domain(ctx->xsh, domid, state->store_mfn, state->store_port);
-    free(vm_path);
-    return 0;
-}
-
-static int set_vnuma_info(libxl__gc *gc, uint32_t domid,
-                          const libxl_domain_build_info *info,
-                          const libxl__domain_build_state *state)
-{
-    int rc = 0;
-    unsigned int i, nr_vdistance;
-    unsigned int *vcpu_to_vnode, *vnode_to_pnode, *vdistance = NULL;
-
-    vcpu_to_vnode = libxl__calloc(gc, info->max_vcpus,
-                                  sizeof(unsigned int));
-    vnode_to_pnode = libxl__calloc(gc, info->num_vnuma_nodes,
-                                   sizeof(unsigned int));
-
-    nr_vdistance = info->num_vnuma_nodes * info->num_vnuma_nodes;
-    vdistance = libxl__calloc(gc, nr_vdistance, sizeof(unsigned int));
-
-    for (i = 0; i < info->num_vnuma_nodes; i++) {
-        libxl_vnode_info *v = &info->vnuma_nodes[i];
-        int j;
-
-        /* vnode to pnode mapping */
-        vnode_to_pnode[i] = v->pnode;
-
-        /* vcpu to vnode mapping */
-        libxl_for_each_set_bit(j, v->vcpus)
-            vcpu_to_vnode[j] = i;
-
-        /* node distances */
-        assert(info->num_vnuma_nodes == v->num_distances);
-        memcpy(vdistance + (i * info->num_vnuma_nodes),
-               v->distances,
-               v->num_distances * sizeof(unsigned int));
-    }
-
-    if (xc_domain_setvnuma(CTX->xch, domid, info->num_vnuma_nodes,
-                           state->num_vmemranges, info->max_vcpus,
-                           state->vmemranges, vdistance,
-                           vcpu_to_vnode, vnode_to_pnode) < 0) {
-        LOGE(ERROR, "xc_domain_setvnuma failed");
-        rc = ERROR_FAIL;
-    }
-
-    return rc;
-}
-
-static int libxl__build_dom(libxl__gc *gc, uint32_t domid,
-             libxl_domain_config *d_config, libxl__domain_build_state *state,
-             struct xc_dom_image *dom)
-{
-    libxl_domain_build_info *const info = &d_config->b_info;
-    uint64_t mem_kb;
-    int ret;
-
-    if ( (ret = xc_dom_boot_xen_init(dom, CTX->xch, domid)) != 0 ) {
-        LOGE(ERROR, "xc_dom_boot_xen_init failed");
-        goto out;
-    }
-#ifdef GUEST_RAM_BASE
-    if ( (ret = xc_dom_rambase_init(dom, GUEST_RAM_BASE)) != 0 ) {
-        LOGE(ERROR, "xc_dom_rambase failed");
-        goto out;
-    }
-#endif
-    if ( (ret = xc_dom_parse_image(dom)) != 0 ) {
-        LOG(ERROR, "xc_dom_parse_image failed");
-        goto out;
-    }
-    if ( (ret = libxl__arch_domain_init_hw_description(gc, info, state, dom)) != 0 ) {
-        LOGE(ERROR, "libxl__arch_domain_init_hw_description failed");
-        goto out;
-    }
-
-    mem_kb = dom->container_type == XC_DOM_HVM_CONTAINER ?
-             (info->max_memkb - info->video_memkb) : info->target_memkb;
-    if ( (ret = xc_dom_mem_init(dom, mem_kb / 1024)) != 0 ) {
-        LOGE(ERROR, "xc_dom_mem_init failed");
-        goto out;
-    }
-    if ( (ret = xc_dom_boot_mem_init(dom)) != 0 ) {
-        LOGE(ERROR, "xc_dom_boot_mem_init failed");
-        goto out;
-    }
-    if ( (ret = libxl__arch_domain_finalise_hw_description(gc, domid, d_config, dom)) != 0 ) {
-        LOGE(ERROR, "libxl__arch_domain_finalise_hw_description failed");
-        goto out;
-    }
-    if ( (ret = xc_dom_build_image(dom)) != 0 ) {
-        LOGE(ERROR, "xc_dom_build_image failed");
-        goto out;
-    }
-    if ( (ret = xc_dom_boot_image(dom)) != 0 ) {
-        LOGE(ERROR, "xc_dom_boot_image failed");
-        goto out;
-    }
-    if ( (ret = xc_dom_gnttab_init(dom)) != 0 ) {
-        LOGE(ERROR, "xc_dom_gnttab_init failed");
-        goto out;
-    }
-    if ((ret = libxl__arch_build_dom_finish(gc, info, dom, state)) != 0) {
-        LOGE(ERROR, "libxl__arch_build_dom_finish failed");
-        goto out;
-    }
-
-out:
-    return ret != 0 ? ERROR_FAIL : 0;
-}
-
-int libxl__build_pv(libxl__gc *gc, uint32_t domid,
-             libxl_domain_config *d_config, libxl__domain_build_state *state)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    libxl_domain_build_info *const info = &d_config->b_info;
-    struct xc_dom_image *dom;
-    int ret;
-    int flags = 0;
-
-    xc_dom_loginit(ctx->xch);
-
-    dom = xc_dom_allocate(ctx->xch, state->pv_cmdline, info->u.pv.features);
-    if (!dom) {
-        LOGE(ERROR, "xc_dom_allocate failed");
-        return ERROR_FAIL;
-    }
-
-    dom->container_type = XC_DOM_PV_CONTAINER;
-
-    LOG(DEBUG, "pv kernel mapped %d path %s", state->pv_kernel.mapped, state->pv_kernel.path);
-
-    if (state->pv_kernel.mapped) {
-        ret = xc_dom_kernel_mem(dom,
-                                state->pv_kernel.data,
-                                state->pv_kernel.size);
-        if ( ret != 0) {
-            LOGE(ERROR, "xc_dom_kernel_mem failed");
-            goto out;
-        }
-    } else {
-        ret = xc_dom_kernel_file(dom, state->pv_kernel.path);
-        if ( ret != 0) {
-            LOGE(ERROR, "xc_dom_kernel_file failed");
-            goto out;
-        }
-    }
-
-    if ( state->pv_ramdisk.path && strlen(state->pv_ramdisk.path) ) {
-        if (state->pv_ramdisk.mapped) {
-            if ( (ret = xc_dom_module_mem(dom, state->pv_ramdisk.data, state->pv_ramdisk.size, NULL)) != 0 ) {
-                LOGE(ERROR, "xc_dom_ramdisk_mem failed");
-                goto out;
-            }
-        } else {
-            if ( (ret = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL)) != 0 ) {
-                LOGE(ERROR, "xc_dom_ramdisk_file failed");
-                goto out;
-            }
-        }
-    }
-
-    dom->flags = flags;
-    dom->console_evtchn = state->console_port;
-    dom->console_domid = state->console_domid;
-    dom->xenstore_evtchn = state->store_port;
-    dom->xenstore_domid = state->store_domid;
-    dom->claim_enabled = libxl_defbool_val(info->claim_mode);
-    dom->max_vcpus = info->max_vcpus;
-
-    if (info->num_vnuma_nodes != 0) {
-        unsigned int i;
-
-        ret = libxl__vnuma_build_vmemrange_pv(gc, domid, info, state);
-        if (ret) {
-            LOGE(ERROR, "cannot build vmemranges");
-            goto out;
-        }
-        ret = libxl__vnuma_config_check(gc, info, state);
-        if (ret) goto out;
-
-        ret = set_vnuma_info(gc, domid, info, state);
-        if (ret) goto out;
-
-        dom->nr_vmemranges = state->num_vmemranges;
-        dom->vmemranges = xc_dom_malloc(dom, sizeof(*dom->vmemranges) *
-                                        dom->nr_vmemranges);
-
-        for (i = 0; i < dom->nr_vmemranges; i++) {
-            dom->vmemranges[i].start = state->vmemranges[i].start;
-            dom->vmemranges[i].end   = state->vmemranges[i].end;
-            dom->vmemranges[i].flags = state->vmemranges[i].flags;
-            dom->vmemranges[i].nid   = state->vmemranges[i].nid;
-        }
-
-        dom->nr_vnodes = info->num_vnuma_nodes;
-        dom->vnode_to_pnode = xc_dom_malloc(dom, sizeof(*dom->vnode_to_pnode) *
-                                            dom->nr_vnodes);
-        for (i = 0; i < info->num_vnuma_nodes; i++)
-            dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
-    }
-
-    ret = libxl__build_dom(gc, domid, d_config, state, dom);
-    if (ret != 0)
-        goto out;
-
-    if (xc_dom_translated(dom)) {
-        state->console_mfn = dom->console_pfn;
-        state->store_mfn = dom->xenstore_pfn;
-        state->vuart_gfn = dom->vuart_gfn;
-    } else {
-        state->console_mfn = xc_dom_p2m(dom, dom->console_pfn);
-        state->store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn);
-    }
-
-    ret = 0;
-out:
-    xc_dom_release(dom);
-    return ret == 0 ? 0 : ERROR_FAIL;
-}
-
-static int hvm_build_set_params(xc_interface *handle, uint32_t domid,
-                                libxl_domain_build_info *info,
-                                int store_evtchn, unsigned long *store_mfn,
-                                int console_evtchn, unsigned long *console_mfn,
-                                domid_t store_domid, domid_t console_domid)
-{
-    struct hvm_info_table *va_hvm;
-    uint8_t *va_map, sum;
-    uint64_t str_mfn, cons_mfn;
-    int i;
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        va_map = xc_map_foreign_range(handle, domid,
-                                      XC_PAGE_SIZE, PROT_READ | PROT_WRITE,
-                                      HVM_INFO_PFN);
-        if (va_map == NULL)
-            return ERROR_FAIL;
-
-        va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET);
-        va_hvm->apic_mode = libxl_defbool_val(info->apic);
-        va_hvm->nr_vcpus = info->max_vcpus;
-        memset(va_hvm->vcpu_online, 0, sizeof(va_hvm->vcpu_online));
-        memcpy(va_hvm->vcpu_online, info->avail_vcpus.map, info->avail_vcpus.size);
-        for (i = 0, sum = 0; i < va_hvm->length; i++)
-            sum += ((uint8_t *) va_hvm)[i];
-        va_hvm->checksum -= sum;
-        munmap(va_map, XC_PAGE_SIZE);
-    }
-
-    xc_hvm_param_get(handle, domid, HVM_PARAM_STORE_PFN, &str_mfn);
-    xc_hvm_param_get(handle, domid, HVM_PARAM_CONSOLE_PFN, &cons_mfn);
-    xc_hvm_param_set(handle, domid, HVM_PARAM_STORE_EVTCHN, store_evtchn);
-    xc_hvm_param_set(handle, domid, HVM_PARAM_CONSOLE_EVTCHN, console_evtchn);
-
-    *store_mfn = str_mfn;
-    *console_mfn = cons_mfn;
-
-    return 0;
-}
-
-static int hvm_build_set_xs_values(libxl__gc *gc,
-                                   uint32_t domid,
-                                   struct xc_dom_image *dom,
-                                   const libxl_domain_build_info *info)
-{
-    char *path = NULL;
-    int ret = 0;
-
-    if (dom->smbios_module.guest_addr_out) {
-        path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_ADDRESS, domid);
-
-        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
-                               dom->smbios_module.guest_addr_out);
-        if (ret)
-            goto err;
-
-        path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_LENGTH, domid);
-
-        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
-                               dom->smbios_module.length);
-        if (ret)
-            goto err;
-    }
-
-    /* Only one module can be passed. PVHv2 guests do not support this. */
-    if (dom->acpi_modules[0].guest_addr_out && 
-        info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_ADDRESS, domid);
-
-        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
-                               dom->acpi_modules[0].guest_addr_out);
-        if (ret)
-            goto err;
-
-        path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_LENGTH, domid);
-
-        ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x",
-                               dom->acpi_modules[0].length);
-        if (ret)
-            goto err;
-    }
-
-    return 0;
-
-err:
-    LOG(ERROR, "failed to write firmware xenstore value, err: %d", ret);
-    return ret;
-}
-
-static int libxl__load_hvm_firmware_module(libxl__gc *gc,
-                                           const char *filename,
-                                           const char *what,
-                                           struct xc_hvm_firmware_module *m)
-{
-    int datalen = 0;
-    void *data = NULL;
-    int r, rc;
-
-    LOG(DEBUG, "Loading %s: %s", what, filename);
-    r = libxl_read_file_contents(CTX, filename, &data, &datalen);
-    if (r) {
-        /*
-         * Print a message only on ENOENT, other errors are logged by the
-         * function libxl_read_file_contents().
-         */
-        if (r == ENOENT)
-            LOGEV(ERROR, r, "failed to read %s file", what);
-        rc =  ERROR_FAIL;
-        goto out;
-    }
-    libxl__ptr_add(gc, data);
-    if (datalen) {
-        /* Only accept non-empty files */
-        m->data = data;
-        m->length = datalen;
-    } else {
-        LOG(ERROR, "file %s for %s is empty", filename, what);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    rc = 0;
-out:
-    return rc;
-}
-
-static int libxl__domain_firmware(libxl__gc *gc,
-                                  libxl_domain_build_info *info,
-                                  libxl__domain_build_state *state,
-                                  struct xc_dom_image *dom)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    const char *firmware = NULL;
-    int e, rc;
-    int datalen = 0;
-    void *data;
-    const char *bios_filename = NULL;
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
-        if (info->u.hvm.firmware) {
-            firmware = info->u.hvm.firmware;
-        } else {
-            switch (info->device_model_version)
-            {
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-                firmware = "hvmloader";
-                break;
-            default:
-                LOG(ERROR, "invalid device model version %d",
-                    info->device_model_version);
-                rc = ERROR_FAIL;
-                goto out;
-            }
-        }
-    }
-
-    if (state->pv_kernel.path != NULL &&
-        info->type == LIBXL_DOMAIN_TYPE_PVH) {
-
-        if (state->shim_path) {
-            rc = xc_dom_kernel_file(dom, state->shim_path);
-            if (rc) {
-                LOGE(ERROR, "xc_dom_kernel_file failed");
-                goto out;
-            }
-
-            /* We've loaded the shim, so load the kernel as a secondary module */
-            if (state->pv_kernel.mapped) {
-                LOG(DEBUG, "xc_dom_module_mem, cmdline %s",
-                    state->pv_cmdline);
-                rc = xc_dom_module_mem(dom, state->pv_kernel.data,
-                                       state->pv_kernel.size, state->pv_cmdline);
-                if (rc) {
-                    LOGE(ERROR, "xc_dom_kernel_mem failed");
-                    goto out;
-                }
-            } else {
-                LOG(DEBUG, "xc_dom_module_file, path %s cmdline %s",
-                    state->pv_kernel.path, state->pv_cmdline);
-                rc = xc_dom_module_file(dom, state->pv_kernel.path, state->pv_cmdline);
-                if (rc) {
-                    LOGE(ERROR, "xc_dom_kernel_file failed");
-                    goto out;
-                }
-            }
-        } else {
-            /* No shim, so load the kernel directly */
-            if (state->pv_kernel.mapped) {
-                rc = xc_dom_kernel_mem(dom, state->pv_kernel.data,
-                                       state->pv_kernel.size);
-                if (rc) {
-                    LOGE(ERROR, "xc_dom_kernel_mem failed");
-                    goto out;
-                }
-            } else {
-                rc = xc_dom_kernel_file(dom, state->pv_kernel.path);
-                if (rc) {
-                    LOGE(ERROR, "xc_dom_kernel_file failed");
-                    goto out;
-                }
-            }
-        }
-
-        if (state->pv_ramdisk.path && strlen(state->pv_ramdisk.path)) {
-            if (state->pv_ramdisk.mapped) {
-                rc = xc_dom_module_mem(dom, state->pv_ramdisk.data,
-                                       state->pv_ramdisk.size, NULL);
-                if (rc) {
-                    LOGE(ERROR, "xc_dom_ramdisk_mem failed");
-                    goto out;
-                }
-            } else {
-                rc = xc_dom_module_file(dom, state->pv_ramdisk.path, NULL);
-                if (rc) {
-                    LOGE(ERROR, "xc_dom_ramdisk_file failed");
-                    goto out;
-                }
-            }
-        }
-    } else {
-        /*
-         * Only HVM guests should get here, PVH should always have a set
-         * kernel at this point.
-         */
-        assert(info->type == LIBXL_DOMAIN_TYPE_HVM);
-        rc = xc_dom_kernel_file(dom, libxl__abs_path(gc, firmware,
-                                                 libxl__xenfirmwaredir_path()));
-    }
-
-    if (rc != 0) {
-        LOGE(ERROR, "xc_dom_{kernel_file/ramdisk_file} failed");
-        goto out;
-    }
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
-        info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
-        if (info->u.hvm.system_firmware) {
-            bios_filename = info->u.hvm.system_firmware;
-        } else {
-            switch (info->u.hvm.bios) {
-            case LIBXL_BIOS_TYPE_SEABIOS:
-                bios_filename = libxl__seabios_path();
-                break;
-            case LIBXL_BIOS_TYPE_OVMF:
-                bios_filename = libxl__ovmf_path();
-                break;
-            case LIBXL_BIOS_TYPE_ROMBIOS:
-            default:
-                abort();
-            }
-        }
-    }
-
-    if (bios_filename) {
-        rc = libxl__load_hvm_firmware_module(gc, bios_filename, "BIOS",
-                                             &dom->system_firmware_module);
-        if (rc) goto out;
-    }
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
-        info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS &&
-        libxl__ipxe_path()) {
-        const char *fp = libxl__ipxe_path();
-        rc = xc_dom_module_file(dom, fp, "ipxe");
-
-        if (rc) {
-            LOGE(ERROR, "failed to load IPXE %s (%d)", fp, rc);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
-        info->u.hvm.smbios_firmware) {
-        data = NULL;
-        e = libxl_read_file_contents(ctx, info->u.hvm.smbios_firmware,
-                                     &data, &datalen);
-        if (e) {
-            LOGEV(ERROR, e, "failed to read SMBIOS firmware file %s",
-                info->u.hvm.smbios_firmware);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        libxl__ptr_add(gc, data);
-        if (datalen) {
-            /* Only accept non-empty files */
-            dom->smbios_module.data = data;
-            dom->smbios_module.length = (uint32_t)datalen;
-        }
-    }
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
-        info->u.hvm.acpi_firmware) {
-        data = NULL;
-        e = libxl_read_file_contents(ctx, info->u.hvm.acpi_firmware,
-                                     &data, &datalen);
-        if (e) {
-            LOGEV(ERROR, e, "failed to read ACPI firmware file %s",
-                info->u.hvm.acpi_firmware);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        libxl__ptr_add(gc, data);
-        if (datalen) {
-            /* Only accept a non-empty file */
-            dom->acpi_modules[0].data = data;
-            dom->acpi_modules[0].length = (uint32_t)datalen;
-        }
-    }
-
-    return 0;
-out:
-    assert(rc != 0);
-    return rc;
-}
-
-int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
-              libxl_domain_config *d_config,
-              libxl__domain_build_state *state)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int rc;
-    uint64_t mmio_start, lowmem_end, highmem_end, mem_size;
-    libxl_domain_build_info *const info = &d_config->b_info;
-    struct xc_dom_image *dom = NULL;
-    bool device_model = info->type == LIBXL_DOMAIN_TYPE_HVM ? true : false;
-
-    xc_dom_loginit(ctx->xch);
-
-    /*
-     * If PVH and we have a shim override, use the shim cmdline.
-     * If PVH and no shim override, use the pv cmdline.
-     * If not PVH, use info->cmdline.
-     */
-    dom = xc_dom_allocate(ctx->xch, info->type == LIBXL_DOMAIN_TYPE_PVH ?
-                          (state->shim_path ? state->shim_cmdline : state->pv_cmdline) :
-                          info->cmdline, NULL);
-    if (!dom) {
-        LOGE(ERROR, "xc_dom_allocate failed");
-        rc = ERROR_NOMEM;
-        goto out;
-    }
-
-    dom->container_type = XC_DOM_HVM_CONTAINER;
-
-    /* The params from the configuration file are in Mb, which are then
-     * multiplied by 1 Kb. This was then divided off when calling
-     * the old xc_hvm_build_target_mem() which then turned them to bytes.
-     * Do all this in one step here...
-     */
-    mem_size = (uint64_t)(info->max_memkb - info->video_memkb) << 10;
-    dom->target_pages = (uint64_t)(info->target_memkb - info->video_memkb) >> 2;
-    dom->claim_enabled = libxl_defbool_val(info->claim_mode);
-    if (info->u.hvm.mmio_hole_memkb) {
-        uint64_t max_ram_below_4g = (1ULL << 32) -
-            (info->u.hvm.mmio_hole_memkb << 10);
-
-        if (max_ram_below_4g < HVM_BELOW_4G_MMIO_START)
-            dom->mmio_size = info->u.hvm.mmio_hole_memkb << 10;
-    }
-
-    rc = libxl__domain_firmware(gc, info, state, dom);
-    if (rc != 0) {
-        LOG(ERROR, "initializing domain firmware failed");
-        goto out;
-    }
-
-    if (dom->target_pages == 0)
-        dom->target_pages = mem_size >> XC_PAGE_SHIFT;
-    if (dom->mmio_size == 0 && device_model)
-        dom->mmio_size = HVM_BELOW_4G_MMIO_LENGTH;
-    else if (dom->mmio_size == 0 && !device_model) {
-#if defined(__i386__) || defined(__x86_64__)
-        /*
-         * Make sure the local APIC page, the ACPI tables and the special pages
-         * are inside the MMIO hole.
-         */
-        xen_paddr_t start =
-            (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES) <<
-            XC_PAGE_SHIFT;
-
-        start = min_t(xen_paddr_t, start, LAPIC_BASE_ADDRESS);
-        start = min_t(xen_paddr_t, start, ACPI_INFO_PHYSICAL_ADDRESS);
-        dom->mmio_size = GB(4) - start;
-#else
-        assert(1);
-#endif
-    }
-    lowmem_end = mem_size;
-    highmem_end = 0;
-    mmio_start = (1ull << 32) - dom->mmio_size;
-    if (lowmem_end > mmio_start)
-    {
-        highmem_end = (1ull << 32) + (lowmem_end - mmio_start);
-        lowmem_end = mmio_start;
-    }
-    dom->lowmem_end = lowmem_end;
-    dom->highmem_end = highmem_end;
-    dom->mmio_start = mmio_start;
-    dom->vga_hole_size = device_model ? LIBXL_VGA_HOLE_SIZE : 0;
-    dom->device_model = device_model;
-    dom->max_vcpus = info->max_vcpus;
-    dom->console_domid = state->console_domid;
-    dom->xenstore_domid = state->store_domid;
-
-    rc = libxl__domain_device_construct_rdm(gc, d_config,
-                                            info->u.hvm.rdm_mem_boundary_memkb*1024,
-                                            dom);
-    if (rc) {
-        LOG(ERROR, "checking reserved device memory failed");
-        goto out;
-    }
-
-    if (info->num_vnuma_nodes != 0) {
-        int i;
-
-        rc = libxl__vnuma_build_vmemrange_hvm(gc, domid, info, state, dom);
-        if (rc != 0) {
-            LOG(ERROR, "hvm build vmemranges failed");
-            goto out;
-        }
-        rc = libxl__vnuma_config_check(gc, info, state);
-        if (rc != 0) goto out;
-        rc = set_vnuma_info(gc, domid, info, state);
-        if (rc != 0) goto out;
-
-        dom->nr_vmemranges = state->num_vmemranges;
-        dom->vmemranges = libxl__malloc(gc, sizeof(*dom->vmemranges) *
-                                        dom->nr_vmemranges);
-
-        for (i = 0; i < dom->nr_vmemranges; i++) {
-            dom->vmemranges[i].start = state->vmemranges[i].start;
-            dom->vmemranges[i].end   = state->vmemranges[i].end;
-            dom->vmemranges[i].flags = state->vmemranges[i].flags;
-            dom->vmemranges[i].nid   = state->vmemranges[i].nid;
-        }
-
-        dom->nr_vnodes = info->num_vnuma_nodes;
-        dom->vnode_to_pnode = libxl__malloc(gc, sizeof(*dom->vnode_to_pnode) *
-                                            dom->nr_vnodes);
-        for (i = 0; i < dom->nr_vnodes; i++)
-            dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode;
-    }
-
-    rc = libxl__build_dom(gc, domid, d_config, state, dom);
-    if (rc != 0)
-        goto out;
-
-    rc = hvm_build_set_params(ctx->xch, domid, info, state->store_port,
-                               &state->store_mfn, state->console_port,
-                               &state->console_mfn, state->store_domid,
-                               state->console_domid);
-    if (rc != 0) {
-        LOG(ERROR, "hvm build set params failed");
-        goto out;
-    }
-
-    rc = hvm_build_set_xs_values(gc, domid, dom, info);
-    if (rc != 0) {
-        LOG(ERROR, "hvm build set xenstore values failed");
-        goto out;
-    }
-
-    xc_dom_release(dom);
-    return 0;
-
-out:
-    assert(rc != 0);
-    if (dom != NULL) xc_dom_release(dom);
-    return rc;
-}
-
-int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
-                                const char *cmd)
-{
-    char *path = NULL;
-    uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/command");
-    return libxl__xs_printf(gc, XBT_NULL, path, "%s", cmd);
-}
-
-/*==================== Miscellaneous ====================*/
-
-char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid)
-{
-    return GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(uuid));
-}
-
-const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
-                                 const char *userdata_userid,
-                                 const char *wh)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *uuid_string, *path;
-    libxl_dominfo info;
-    int rc;
-
-    libxl_dominfo_init(&info);
-
-    rc = libxl_domain_info(ctx, &info, domid);
-    if (rc) {
-        LOGE(ERROR, "unable to find domain info for domain %"PRIu32, domid);
-        path = NULL;
-        goto out;
-    }
-    uuid_string = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
-    path = GCSPRINTF(XEN_LIB_DIR "/userdata-%s.%u.%s.%s",
-                     wh, domid, uuid_string, userdata_userid);
-
- out:
-    libxl_dominfo_dispose(&info);
-    return path;
-}
-
-static int userdata_delete(libxl__gc *gc, const char *path)
-{
-    int r;
-    r = unlink(path);
-    if (r) {
-        LOGE(ERROR, "remove failed for %s", path);
-        return errno;
-    }
-    return 0;
-}
-
-void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid)
-{
-    const char *pattern;
-    glob_t gl;
-    int r, i;
-
-    pattern = libxl__userdata_path(gc, domid, "*", "?");
-    if (!pattern)
-        goto out;
-
-    gl.gl_pathc = 0;
-    gl.gl_pathv = 0;
-    gl.gl_offs = 0;
-    r = glob(pattern, GLOB_ERR|GLOB_NOSORT|GLOB_MARK, 0, &gl);
-    if (r == GLOB_NOMATCH)
-        goto out;
-    if (r)
-        LOGE(ERROR, "glob failed for %s", pattern);
-
-    /* Note: don't delete domain-userdata-lock, it will be handled by
-     * unlock function.
-     */
-    for (i=0; i<gl.gl_pathc; i++) {
-        if (!strstr(gl.gl_pathv[i], "domain-userdata-lock"))
-            userdata_delete(gc, gl.gl_pathv[i]);
-    }
-    globfree(&gl);
-out:
-    return;
-}
-
-int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
-                          const char *userdata_userid,
-                          const uint8_t *data, int datalen)
-{
-    const char *filename;
-    const char *newfilename;
-    int e, rc;
-    int fd = -1;
-
-    filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
-    if (!filename) {
-        rc = ERROR_NOMEM;
-        goto out;
-    }
-
-    if (!datalen) {
-        rc = userdata_delete(gc, filename);
-        goto out;
-    }
-
-    newfilename = libxl__userdata_path(gc, domid, userdata_userid, "n");
-    if (!newfilename) {
-        rc = ERROR_NOMEM;
-        goto out;
-    }
-
-    rc = ERROR_FAIL;
-
-    fd = open(newfilename, O_RDWR | O_CREAT | O_TRUNC, 0600);
-    if (fd < 0)
-        goto err;
-
-    if (libxl_write_exactly(CTX, fd, data, datalen, "userdata", newfilename))
-        goto err;
-
-    if (close(fd) < 0) {
-        fd = -1;
-        goto err;
-    }
-    fd = -1;
-
-    if (rename(newfilename, filename))
-        goto err;
-
-    rc = 0;
-
-err:
-    if (fd >= 0) {
-        e = errno;
-        close(fd);
-        errno = e;
-    }
-
-    if (rc)
-        LOGE(ERROR, "cannot write/rename %s for %s", newfilename, filename);
-out:
-    return rc;
-}
-
-int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid,
-                              const char *userdata_userid,
-                              const uint8_t *data, int datalen)
-{
-    GC_INIT(ctx);
-    int rc;
-    libxl__flock *lock;
-
-    CTX_LOCK;
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    rc = libxl__userdata_store(gc, domid, userdata_userid,
-                               data, datalen);
-
-    libxl__unlock_file(lock);
-
-out:
-    CTX_UNLOCK;
-    GC_FREE;
-    return rc;
-}
-
-int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
-                             const char *userdata_userid,
-                             uint8_t **data_r, int *datalen_r)
-{
-    const char *filename;
-    int e, rc;
-    int datalen = 0;
-    void *data = 0;
-
-    filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
-    if (!filename) {
-        rc = ERROR_NOMEM;
-        goto out;
-    }
-
-    e = libxl_read_file_contents(CTX, filename, data_r ? &data : 0, &datalen);
-    if (e && errno != ENOENT) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (!e && !datalen) {
-        LOG(ERROR, "userdata file %s is empty", filename);
-        if (data_r) assert(!*data_r);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (data_r) *data_r = data;
-    if (datalen_r) *datalen_r = datalen;
-    rc = 0;
-
-out:
-    return rc;
-}
-
-int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid,
-                                 const char *userdata_userid,
-                                 uint8_t **data_r, int *datalen_r)
-{
-    GC_INIT(ctx);
-    int rc;
-    libxl__flock *lock;
-
-    CTX_LOCK;
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    rc = libxl__userdata_retrieve(gc, domid, userdata_userid,
-                                  data_r, datalen_r);
-
-
-    libxl__unlock_file(lock);
-out:
-    CTX_UNLOCK;
-    GC_FREE;
-    return rc;
-}
-
-int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid,
-                          const char *userdata_userid)
-{
-    GC_INIT(ctx);
-    CTX_LOCK;
-
-    int rc;
-    libxl__flock *lock = NULL;
-    const char *filename;
-
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    filename = libxl__userdata_path(gc, domid, userdata_userid, "d");
-    if (!filename) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (unlink(filename)) {
-        LOGE(ERROR, "error deleting userdata file: %s", filename);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-out:
-    if (lock)
-        libxl__unlock_file(lock);
-    CTX_UNLOCK;
-    GC_FREE;
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_dom_save.c b/tools/libxl/libxl_dom_save.c
deleted file mode 100644 (file)
index 32e3cb5..0000000
+++ /dev/null
@@ -1,564 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <xen/errno.h>
-
-/*========================= Domain save ============================*/
-
-static void stream_done(libxl__egc *egc,
-                        libxl__stream_write_state *sws, int rc);
-static void domain_save_done(libxl__egc *egc,
-                             libxl__domain_save_state *dss, int rc);
-
-/*----- complicated callback, called by xc_domain_save -----*/
-
-/*
- * We implement the other end of protocol for controlling qemu-dm's
- * logdirty.  There is no documentation for this protocol, but our
- * counterparty's implementation is in
- * qemu-xen-traditional.git:xenstore.c in the function
- * xenstore_process_logdirty_event
- */
-
-static void domain_suspend_switch_qemu_xen_traditional_logdirty
-                               (libxl__egc *egc, int domid, unsigned enable,
-                                libxl__logdirty_switch *lds);
-static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch*,
-                            const char *watch_path, const char *event_path);
-static void domain_suspend_switch_qemu_xen_logdirty
-                               (libxl__egc *egc, int domid, unsigned enable,
-                                libxl__logdirty_switch *lds);
-static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
-                                          libxl__ev_qmp *qmp,
-                                          const libxl__json_object *,
-                                          int rc);
-static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                                    const struct timeval *requested_abs,
-                                    int rc);
-static void switch_logdirty_done(libxl__egc *egc,
-                                 libxl__logdirty_switch *lds, int rc);
-
-void libxl__logdirty_init(libxl__logdirty_switch *lds)
-{
-    lds->cmd_path = 0;
-    libxl__ev_xswatch_init(&lds->watch);
-    libxl__ev_time_init(&lds->timeout);
-    libxl__ev_qmp_init(&lds->qmp);
-}
-
-void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
-                                               int domid, unsigned enable,
-                                               libxl__logdirty_switch *lds)
-{
-    STATE_AO_GC(lds->ao);
-
-    switch (libxl__device_model_version_running(gc, domid)) {
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-        domain_suspend_switch_qemu_xen_traditional_logdirty(egc, domid, enable,
-                                                            lds);
-        break;
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-        domain_suspend_switch_qemu_xen_logdirty(egc, domid, enable, lds);
-        break;
-    default:
-        LOGD(ERROR, domid, "logdirty switch failed"
-             ", no valid device model version found, abandoning suspend");
-        lds->callback(egc, lds, ERROR_FAIL);
-    }
-}
-
-static void domain_suspend_switch_qemu_xen_traditional_logdirty
-                               (libxl__egc *egc, int domid, unsigned enable,
-                                libxl__logdirty_switch *lds)
-{
-    STATE_AO_GC(lds->ao);
-    int rc;
-    xs_transaction_t t = 0;
-    const char *got;
-
-    if (!lds->cmd_path) {
-        uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-        lds->cmd_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
-                                             "/logdirty/cmd");
-        lds->ret_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid,
-                                             "/logdirty/ret");
-    }
-    lds->cmd = enable ? "enable" : "disable";
-
-    rc = libxl__ev_xswatch_register(gc, &lds->watch,
-                                switch_logdirty_xswatch, lds->ret_path);
-    if (rc) goto out;
-
-    rc = libxl__ev_time_register_rel(ao, &lds->timeout,
-                                switch_logdirty_timeout, 10*1000);
-    if (rc) goto out;
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        rc = libxl__xs_read_checked(gc, t, lds->cmd_path, &got);
-        if (rc) goto out;
-
-        if (got) {
-            const char *got_ret;
-            rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got_ret);
-            if (rc) goto out;
-
-            if (!got_ret || strcmp(got, got_ret)) {
-                LOGD(ERROR, domid, "controlling logdirty: qemu was already sent"
-                     " command `%s' (xenstore path `%s') but result is `%s'",
-                     got, lds->cmd_path, got_ret ? got_ret : "<none>");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-            rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
-            if (rc) goto out;
-        }
-
-        rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
-        if (rc) goto out;
-
-        rc = libxl__xs_write_checked(gc, t, lds->cmd_path, lds->cmd);
-        if (rc) goto out;
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc<0) goto out;
-    }
-
-    /* OK, wait for some callback */
-    return;
-
- out:
-    LOGD(ERROR, domid, "logdirty switch failed (rc=%d), abandoning suspend",rc);
-    libxl__xs_transaction_abort(gc, &t);
-    switch_logdirty_done(egc,lds,rc);
-}
-
-static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch *watch,
-                            const char *watch_path, const char *event_path)
-{
-    libxl__logdirty_switch *lds = CONTAINER_OF(watch, *lds, watch);
-    STATE_AO_GC(lds->ao);
-    const char *got;
-    xs_transaction_t t = 0;
-    int rc;
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got);
-        if (rc) goto out;
-
-        if (!got) {
-            rc = +1;
-            goto out;
-        }
-
-        if (strcmp(got, lds->cmd)) {
-            LOG(ERROR,"logdirty switch: sent command `%s' but got reply `%s'"
-                " (xenstore paths `%s' / `%s')", lds->cmd, got,
-                lds->cmd_path, lds->ret_path);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        rc = libxl__xs_rm_checked(gc, t, lds->cmd_path);
-        if (rc) goto out;
-
-        rc = libxl__xs_rm_checked(gc, t, lds->ret_path);
-        if (rc) goto out;
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc<0) goto out;
-    }
-
- out:
-    /* rc < 0: error
-     * rc == 0: ok, we are done
-     * rc == +1: need to keep waiting
-     */
-    libxl__xs_transaction_abort(gc, &t);
-
-    if (rc <= 0) {
-        if (rc < 0)
-            LOG(ERROR,"logdirty switch: failed (rc=%d)",rc);
-        switch_logdirty_done(egc,lds,rc);
-    }
-}
-
-static void domain_suspend_switch_qemu_xen_logdirty
-                               (libxl__egc *egc, int domid, unsigned enable,
-                                libxl__logdirty_switch *lds)
-{
-    STATE_AO_GC(lds->ao);
-    int rc;
-    libxl__json_object *args = NULL;
-
-    /* Convenience aliases. */
-    libxl__ev_qmp *const qmp = &lds->qmp;
-
-    rc = libxl__ev_time_register_rel(ao, &lds->timeout,
-                                     switch_logdirty_timeout, 10 * 1000);
-    if (rc) goto out;
-
-    qmp->ao = ao;
-    qmp->domid = domid;
-    qmp->payload_fd = -1;
-    qmp->callback = switch_qemu_xen_logdirty_done;
-    libxl__qmp_param_add_bool(gc, &args, "enable", enable);
-    rc = libxl__ev_qmp_send(egc, qmp, "xen-set-global-dirty-log", args);
-    if (rc) goto out;
-
-    return;
-out:
-    switch_qemu_xen_logdirty_done(egc, qmp, NULL, rc);
-}
-
-static void switch_qemu_xen_logdirty_done(libxl__egc *egc,
-                                          libxl__ev_qmp *qmp,
-                                          const libxl__json_object *r,
-                                          int rc)
-{
-    EGC_GC;
-    libxl__logdirty_switch *lds = CONTAINER_OF(qmp, *lds, qmp);
-
-    if (rc)
-        LOGD(ERROR, qmp->domid,
-             "logdirty switch failed (rc=%d), abandoning suspend",rc);
-    switch_logdirty_done(egc, lds, rc);
-}
-
-static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                                    const struct timeval *requested_abs,
-                                    int rc)
-{
-    libxl__logdirty_switch *lds = CONTAINER_OF(ev, *lds, timeout);
-    STATE_AO_GC(lds->ao);
-    LOG(ERROR,"logdirty switch: wait for device model timed out");
-    switch_logdirty_done(egc,lds,ERROR_FAIL);
-}
-
-static void switch_logdirty_done(libxl__egc *egc,
-                                 libxl__logdirty_switch *lds,
-                                 int rc)
-{
-    STATE_AO_GC(lds->ao);
-
-    libxl__ev_xswatch_deregister(gc, &lds->watch);
-    libxl__ev_time_deregister(gc, &lds->timeout);
-    libxl__ev_qmp_dispose(gc, &lds->qmp);
-
-    lds->callback(egc, lds, rc);
-}
-
-static void domain_suspend_switch_qemu_logdirty_done
-                        (libxl__egc *egc, libxl__logdirty_switch *lds, int rc);
-
-void libxl__domain_suspend_common_switch_qemu_logdirty
-                               (uint32_t domid, unsigned enable, void *user)
-{
-    libxl__save_helper_state *shs = user;
-    libxl__egc *egc = shs->egc;
-    libxl__domain_save_state *dss = shs->caller_state;
-
-    /* Convenience aliases. */
-    libxl__logdirty_switch *const lds = &dss->logdirty;
-
-    if (dss->type == LIBXL_DOMAIN_TYPE_PVH) {
-        domain_suspend_switch_qemu_logdirty_done(egc, lds, 0);
-        return;
-    }
-
-    lds->callback = domain_suspend_switch_qemu_logdirty_done;
-    libxl__domain_common_switch_qemu_logdirty(egc, domid, enable, lds);
-}
-
-static void domain_suspend_switch_qemu_logdirty_done
-                        (libxl__egc *egc, libxl__logdirty_switch *lds, int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(lds, *dss, logdirty);
-
-    if (rc) {
-        dss->rc = rc;
-        libxl__xc_domain_saverestore_async_callback_done(egc,
-                                                         &dss->sws.shs, -1);
-    } else
-        libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-/*----- callbacks, called by xc_domain_save -----*/
-
-/*
- * Expand the buffer 'buf' of length 'len', to append 'str' including its NUL
- * terminator.
- */
-static void append_string(libxl__gc *gc, char **buf, uint32_t *len,
-                          const char *str)
-{
-    size_t extralen = strlen(str) + 1;
-    char *new = libxl__realloc(gc, *buf, *len + extralen);
-
-    *buf = new;
-    memcpy(new + *len, str, extralen);
-    *len += extralen;
-}
-
-int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
-                                       char **callee_buf,
-                                       uint32_t *callee_len)
-{
-    STATE_AO_GC(dss->ao);
-    const char *xs_root;
-    char **entries, *buf = NULL;
-    unsigned int nr_entries, i, j, len = 0;
-    int rc;
-
-    const uint32_t domid = dss->domid;
-    const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-
-    xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
-
-    entries = libxl__xs_directory(gc, 0, GCSPRINTF("%s/physmap", xs_root),
-                                  &nr_entries);
-    if (!entries || nr_entries == 0) { rc = 0; goto out; }
-
-    for (i = 0; i < nr_entries; ++i) {
-        static const char *const physmap_subkeys[] = {
-            "start_addr", "size", "name"
-        };
-
-        for (j = 0; j < ARRAY_SIZE(physmap_subkeys); ++j) {
-            const char *key = GCSPRINTF("physmap/%s/%s",
-                                        entries[i], physmap_subkeys[j]);
-
-            const char *val =
-                libxl__xs_read(gc, XBT_NULL,
-                               GCSPRINTF("%s/%s", xs_root, key));
-
-            if (!val) { rc = ERROR_FAIL; goto out; }
-
-            append_string(gc, &buf, &len, key);
-            append_string(gc, &buf, &len, val);
-        }
-    }
-
-    rc = 0;
-
- out:
-    if (!rc) {
-        *callee_buf = buf;
-        *callee_len = len;
-    }
-
-    return rc;
-}
-
-/*----- main code for saving, in order of execution -----*/
-
-void libxl__domain_save(libxl__egc *egc, libxl__domain_save_state *dss)
-{
-    STATE_AO_GC(dss->ao);
-    int rc, ret;
-
-    /* Convenience aliases */
-    const uint32_t domid = dss->domid;
-    const libxl_domain_type type = dss->type;
-    const int live = dss->live;
-    const int debug = dss->debug;
-    const libxl_domain_remus_info *const r_info = dss->remus;
-    libxl__srm_save_autogen_callbacks *const callbacks =
-        &dss->sws.shs.callbacks.save.a;
-    unsigned int nr_vnodes = 0, nr_vmemranges = 0, nr_vcpus = 0;
-    libxl__domain_suspend_state *dsps = &dss->dsps;
-
-    if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE && !r_info) {
-        LOGD(ERROR, domid, "Migration stream is checkpointed, but there's no "
-                           "checkpoint info!");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    dss->rc = 0;
-    libxl__logdirty_init(&dss->logdirty);
-    dss->logdirty.ao = ao;
-
-    dsps->ao = ao;
-    dsps->domid = domid;
-    dsps->live = !!live;
-    rc = libxl__domain_suspend_init(egc, dsps, type);
-    if (rc) goto out;
-
-    dss->xcflags = (live ? XCFLAGS_LIVE : 0)
-          | (debug ? XCFLAGS_DEBUG : 0);
-
-    /* Disallow saving a guest with vNUMA configured because migration
-     * stream does not preserve node information.
-     *
-     * Reject any domain which has vnuma enabled, even if the
-     * configuration is empty. Only domains which have no vnuma
-     * configuration at all are supported.
-     */
-    ret = xc_domain_getvnuma(CTX->xch, domid, &nr_vnodes, &nr_vmemranges,
-                             &nr_vcpus, NULL, NULL, NULL);
-    if (ret != -1 || errno != EOPNOTSUPP) {
-        LOGD(ERROR, domid, "Cannot save a guest with vNUMA configured");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (dss->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_NONE)
-        callbacks->suspend = libxl__domain_suspend_callback;
-
-    callbacks->switch_qemu_logdirty = libxl__domain_suspend_common_switch_qemu_logdirty;
-
-    dss->sws.ao  = dss->ao;
-    dss->sws.dss = dss;
-    dss->sws.fd  = dss->fd;
-    dss->sws.back_channel = false;
-    dss->sws.completion_callback = stream_done;
-
-    libxl__stream_write_start(egc, &dss->sws);
-    return;
-
- out:
-    domain_save_done(egc, dss, rc);
-}
-
-static void stream_done(libxl__egc *egc,
-                        libxl__stream_write_state *sws, int rc)
-{
-    domain_save_done(egc, sws->dss, rc);
-}
-
-static void domain_save_done(libxl__egc *egc,
-                             libxl__domain_save_state *dss, int rc)
-{
-    STATE_AO_GC(dss->ao);
-
-    /* Convenience aliases */
-    const uint32_t domid = dss->domid;
-    libxl__domain_suspend_state *dsps = &dss->dsps;
-
-    libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
-
-    if (dsps->guest_evtchn.port > 0)
-        xc_suspend_evtchn_release(CTX->xch, CTX->xce, domid,
-                        dsps->guest_evtchn.port, &dsps->guest_evtchn_lockfd);
-
-    if (dss->remus) {
-        /*
-         * With Remus/COLO, if we reach this point, it means either
-         * backup died or some network error occurred preventing us
-         * from sending checkpoints. Teardown the network buffers and
-         * release netlink resources.  This is an async op.
-         */
-        if (libxl_defbool_val(dss->remus->colo))
-            libxl__colo_save_teardown(egc, &dss->css, rc);
-        else
-            libxl__remus_teardown(egc, &dss->rs, rc);
-        return;
-    }
-
-    dss->callback(egc, dss, rc);
-}
-
-/*========================= Domain restore ============================*/
-
-/*
- * Inspect the buffer between start and end, and return a pointer to the
- * character following the NUL terminator of start, or NULL if start is not
- * terminated before end.
- */
-static const char *next_string(const char *start, const char *end)
-{
-    if (start >= end) return NULL;
-
-    size_t total_len = end - start;
-    size_t len = strnlen(start, total_len);
-
-    if (len == total_len)
-        return NULL;
-    else
-        return start + len + 1;
-}
-
-int libxl__restore_emulator_xenstore_data(libxl__domain_create_state *dcs,
-                                          const char *ptr, uint32_t size)
-{
-    STATE_AO_GC(dcs->ao);
-    const char *next = ptr, *end = ptr + size, *key, *val;
-    int rc;
-
-    const uint32_t domid = dcs->guest_domid;
-    const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-    const char *xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "");
-
-    while (next < end) {
-        key = next;
-        next = next_string(next, end);
-
-        /* Sanitise 'key'. */
-        if (!next) {
-            rc = ERROR_FAIL;
-            LOGD(ERROR, domid, "Key in xenstore data not NUL terminated");
-            goto out;
-        }
-        if (key[0] == '\0') {
-            rc = ERROR_FAIL;
-            LOGD(ERROR, domid, "empty key found in xenstore data");
-            goto out;
-        }
-        if (key[0] == '/') {
-            rc = ERROR_FAIL;
-            LOGD(ERROR, domid, "Key in xenstore data not relative");
-            goto out;
-        }
-
-        val = next;
-        next = next_string(next, end);
-
-        /* Sanitise 'val'. */
-        if (!next) {
-            rc = ERROR_FAIL;
-            LOGD(ERROR, domid, "Val in xenstore data not NUL terminated");
-            goto out;
-        }
-
-        libxl__xs_printf(gc, XBT_NULL,
-                         GCSPRINTF("%s/%s", xs_root, key),
-                         "%s", val);
-    }
-
-    rc = 0;
-
- out:
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c
deleted file mode 100644 (file)
index 25d1571..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*====================== Domain suspend =======================*/
-
-int libxl__domain_suspend_init(libxl__egc *egc,
-                               libxl__domain_suspend_state *dsps,
-                               libxl_domain_type type)
-{
-    STATE_AO_GC(dsps->ao);
-    int rc = ERROR_FAIL;
-    int port;
-
-    /* Convenience aliases */
-    const uint32_t domid = dsps->domid;
-
-    libxl__xswait_init(&dsps->pvcontrol);
-    libxl__ev_evtchn_init(&dsps->guest_evtchn);
-    libxl__ev_xswatch_init(&dsps->guest_watch);
-    libxl__ev_time_init(&dsps->guest_timeout);
-    libxl__ev_qmp_init(&dsps->qmp);
-
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) goto out;
-    dsps->type = type;
-
-    dsps->guest_evtchn.port = -1;
-    dsps->guest_evtchn_lockfd = -1;
-    dsps->guest_responded = 0;
-    dsps->dm_savefile = libxl__device_model_savefile(gc, domid);
-
-    port = xs_suspend_evtchn_port(domid);
-
-    if (port >= 0) {
-        rc = libxl__ctx_evtchn_init(gc);
-        if (rc) goto out;
-
-        dsps->guest_evtchn.port =
-            xc_suspend_evtchn_init_exclusive(CTX->xch, CTX->xce,
-                                    domid, port, &dsps->guest_evtchn_lockfd);
-
-        if (dsps->guest_evtchn.port < 0) {
-            LOGD(WARN, domid, "Suspend event channel initialization failed");
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-/*----- callbacks, called by xc_domain_save -----*/
-
-void libxl__domain_suspend_device_model(libxl__egc *egc,
-                                       libxl__domain_suspend_state *dsps)
-{
-    STATE_AO_GC(dsps->ao);
-    int rc = 0;
-    uint32_t const domid = dsps->domid;
-    const char *const filename = dsps->dm_savefile;
-
-    switch (libxl__device_model_version_running(gc, domid)) {
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
-        LOGD(DEBUG, domid, "Saving device model state to %s", filename);
-        libxl__qemu_traditional_cmd(gc, domid, "save");
-        libxl__wait_for_device_model_deprecated(gc, domid, "paused", NULL, NULL, NULL);
-        break;
-    }
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-        /* calls dsps->callback_device_model_done when done */
-        libxl__qmp_suspend_save(egc, dsps); /* must be last */
-        return;
-    default:
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-out:
-    if (rc)
-        LOGD(ERROR, dsps->domid,
-             "failed to suspend device model, rc=%d", rc);
-    dsps->callback_device_model_done(egc, dsps, rc); /* must be last */
-}
-
-static void domain_suspend_common_wait_guest(libxl__egc *egc,
-                                             libxl__domain_suspend_state *dsps);
-static void domain_suspend_common_guest_suspended(libxl__egc *egc,
-                                         libxl__domain_suspend_state *dsps);
-
-static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
-      libxl__xswait_state *xswa, int rc, const char *state);
-static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
-        libxl__ev_evtchn *evev);
-static void suspend_common_wait_guest_watch(libxl__egc *egc,
-      libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path);
-static void suspend_common_wait_guest_check(libxl__egc *egc,
-        libxl__domain_suspend_state *dsps);
-static void suspend_common_wait_guest_timeout(libxl__egc *egc,
-      libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-
-static void domain_suspend_common_done(libxl__egc *egc,
-                                       libxl__domain_suspend_state *dsps,
-                                       int rc);
-
-static void domain_suspend_callback_common(libxl__egc *egc,
-                                           libxl__domain_suspend_state *dsps);
-static void domain_suspend_callback_common_done(libxl__egc *egc,
-                                libxl__domain_suspend_state *dsps, int rc);
-
-/* calls dsps->callback_common_done when done */
-void libxl__domain_suspend(libxl__egc *egc,
-                           libxl__domain_suspend_state *dsps)
-{
-    domain_suspend_callback_common(egc, dsps);
-}
-
-static bool domain_suspend_pvcontrol_acked(const char *state) {
-    /* any value other than "suspend", including ENOENT (i.e. !state), is OK */
-    if (!state) return 1;
-    return strcmp(state,"suspend");
-}
-
-/* calls dsps->callback_common_done when done */
-static void domain_suspend_callback_common(libxl__egc *egc,
-                                           libxl__domain_suspend_state *dsps)
-{
-    STATE_AO_GC(dsps->ao);
-    uint64_t hvm_s_state = 0, hvm_pvdrv = 0;
-    int ret, rc;
-
-    /* Convenience aliases */
-    const uint32_t domid = dsps->domid;
-
-    if (dsps->type != LIBXL_DOMAIN_TYPE_PV) {
-        xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_CALLBACK_IRQ, &hvm_pvdrv);
-        xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, &hvm_s_state);
-    }
-
-    if ((hvm_s_state == 0) && (dsps->guest_evtchn.port >= 0)) {
-        LOGD(DEBUG, domid, "issuing %s suspend request via event channel",
-            dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
-        ret = xenevtchn_notify(CTX->xce, dsps->guest_evtchn.port);
-        if (ret < 0) {
-            LOGD(ERROR, domid, "xenevtchn_notify failed ret=%d", ret);
-            rc = ERROR_FAIL;
-            goto err;
-        }
-
-        dsps->guest_evtchn.callback = domain_suspend_common_wait_guest_evtchn;
-        rc = libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
-        if (rc) goto err;
-
-        rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
-                                         suspend_common_wait_guest_timeout,
-                                         60*1000);
-        if (rc) goto err;
-
-        return;
-    }
-
-    if (dsps->type == LIBXL_DOMAIN_TYPE_HVM && (!hvm_pvdrv || hvm_s_state)) {
-        LOGD(DEBUG, domid, "Calling xc_domain_shutdown on HVM domain");
-        ret = xc_domain_shutdown(CTX->xch, domid, SHUTDOWN_suspend);
-        if (ret < 0) {
-            LOGED(ERROR, domid, "xc_domain_shutdown failed");
-            rc = ERROR_FAIL;
-            goto err;
-        }
-        /* The guest does not (need to) respond to this sort of request. */
-        dsps->guest_responded = 1;
-        domain_suspend_common_wait_guest(egc, dsps);
-        return;
-    }
-
-    LOGD(DEBUG, domid, "issuing %s suspend request via XenBus control node",
-        dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
-
-    dsps->pvcontrol.ao = ao;
-    dsps->pvcontrol.callback = domain_suspend_common_pvcontrol_suspending;
-    rc = libxl__domain_pvcontrol(egc, &dsps->pvcontrol, domid, "suspend");
-    if (rc) goto err;
-
-    return;
-
- err:
-    domain_suspend_common_done(egc, dsps, rc);
-}
-
-static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
-        libxl__ev_evtchn *evev)
-{
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(evev, *dsps, guest_evtchn);
-    STATE_AO_GC(dsps->ao);
-    /* If we should be done waiting, suspend_common_wait_guest_check
-     * will end up calling domain_suspend_common_guest_suspended or
-     * domain_suspend_common_done, both of which cancel the evtchn
-     * wait as needed.  So re-enable it now. */
-    libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
-    suspend_common_wait_guest_check(egc, dsps);
-}
-
-static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
-      libxl__xswait_state *xswa, int rc, const char *state)
-{
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(xswa, *dsps, pvcontrol);
-    STATE_AO_GC(dsps->ao);
-    xs_transaction_t t = 0;
-
-    if (!rc && !domain_suspend_pvcontrol_acked(state))
-        /* keep waiting */
-        return;
-
-    libxl__xswait_stop(gc, &dsps->pvcontrol);
-
-    if (rc == ERROR_TIMEDOUT) {
-        /*
-         * Guest appears to not be responding. Cancel the suspend
-         * request.
-         *
-         * We re-read the suspend node and clear it within a
-         * transaction in order to handle the case where we race
-         * against the guest catching up and acknowledging the request
-         * at the last minute.
-         */
-        for (;;) {
-            rc = libxl__xs_transaction_start(gc, &t);
-            if (rc) goto err;
-
-            rc = libxl__xs_read_checked(gc, t, xswa->path, &state);
-            if (rc) goto err;
-
-            if (domain_suspend_pvcontrol_acked(state))
-                /* last minute ack */
-                break;
-
-            rc = libxl__xs_write_checked(gc, t, xswa->path, "");
-            if (rc) goto err;
-
-            rc = libxl__xs_transaction_commit(gc, &t);
-            if (!rc) {
-                LOGD(ERROR, dsps->domid,
-                     "guest didn't acknowledge suspend, cancelling request");
-                goto err;
-            }
-            if (rc<0) goto err;
-        }
-    } else if (rc) {
-        /* some error in xswait's read of xenstore, already logged */
-        goto err;
-    }
-
-    assert(domain_suspend_pvcontrol_acked(state));
-    LOGD(DEBUG, dsps->domid, "guest acknowledged suspend request");
-
-    libxl__xs_transaction_abort(gc, &t);
-    dsps->guest_responded = 1;
-    domain_suspend_common_wait_guest(egc,dsps);
-    return;
-
- err:
-    libxl__xs_transaction_abort(gc, &t);
-    domain_suspend_common_done(egc, dsps, rc);
-    return;
-}
-
-static void domain_suspend_common_wait_guest(libxl__egc *egc,
-                                             libxl__domain_suspend_state *dsps)
-{
-    STATE_AO_GC(dsps->ao);
-    int rc;
-
-    LOGD(DEBUG, dsps->domid, "wait for the guest to suspend");
-
-    rc = libxl__ev_xswatch_register(gc, &dsps->guest_watch,
-                                    suspend_common_wait_guest_watch,
-                                    "@releaseDomain");
-    if (rc) goto err;
-
-    rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
-                                     suspend_common_wait_guest_timeout,
-                                     60*1000);
-    if (rc) goto err;
-    return;
-
- err:
-    domain_suspend_common_done(egc, dsps, rc);
-}
-
-static void suspend_common_wait_guest_watch(libxl__egc *egc,
-      libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path)
-{
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(xsw, *dsps, guest_watch);
-    suspend_common_wait_guest_check(egc, dsps);
-}
-
-static void suspend_common_wait_guest_check(libxl__egc *egc,
-        libxl__domain_suspend_state *dsps)
-{
-    STATE_AO_GC(dsps->ao);
-    xc_domaininfo_t info;
-    int ret;
-    int shutdown_reason;
-
-    /* Convenience aliases */
-    const uint32_t domid = dsps->domid;
-
-    ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
-    if (ret < 0) {
-        LOGED(ERROR, domid, "unable to check for status of guest");
-        goto err;
-    }
-
-    if (!(ret == 1 && info.domain == domid)) {
-        LOGED(ERROR, domid, "guest we were suspending has been destroyed");
-        goto err;
-    }
-
-    if (!(info.flags & XEN_DOMINF_shutdown))
-        /* keep waiting */
-        return;
-
-    shutdown_reason = (info.flags >> XEN_DOMINF_shutdownshift)
-        & XEN_DOMINF_shutdownmask;
-    if (shutdown_reason != SHUTDOWN_suspend) {
-        LOGD(DEBUG, domid, "guest we were suspending has shut down"
-             " with unexpected reason code %d", shutdown_reason);
-        goto err;
-    }
-
-    LOGD(DEBUG, domid, "guest has suspended");
-    domain_suspend_common_guest_suspended(egc, dsps);
-    return;
-
- err:
-    domain_suspend_common_done(egc, dsps, ERROR_FAIL);
-}
-
-static void suspend_common_wait_guest_timeout(libxl__egc *egc,
-      libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, guest_timeout);
-    STATE_AO_GC(dsps->ao);
-    if (rc == ERROR_TIMEDOUT) {
-        LOGD(ERROR, dsps->domid, "guest did not suspend, timed out");
-        rc = ERROR_GUEST_TIMEDOUT;
-    }
-    domain_suspend_common_done(egc, dsps, rc);
-}
-
-static void domain_suspend_common_guest_suspended(libxl__egc *egc,
-                                         libxl__domain_suspend_state *dsps)
-{
-    STATE_AO_GC(dsps->ao);
-
-    libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
-    libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
-    libxl__ev_time_deregister(gc, &dsps->guest_timeout);
-
-    if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) {
-        dsps->callback_device_model_done = domain_suspend_common_done;
-        libxl__domain_suspend_device_model(egc, dsps); /* must be last */
-        return;
-    }
-    domain_suspend_common_done(egc, dsps, 0);
-}
-
-static void domain_suspend_common_done(libxl__egc *egc,
-                                       libxl__domain_suspend_state *dsps,
-                                       int rc)
-{
-    EGC_GC;
-    assert(!libxl__xswait_inuse(&dsps->pvcontrol));
-    libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
-    libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
-    libxl__ev_time_deregister(gc, &dsps->guest_timeout);
-    libxl__ev_qmp_dispose(gc, &dsps->qmp);
-    dsps->callback_common_done(egc, dsps, rc);
-}
-
-void libxl__domain_suspend_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__egc *egc = shs->egc;
-    libxl__domain_save_state *dss = shs->caller_state;
-    libxl__domain_suspend_state *dsps = &dss->dsps;
-
-    dsps->callback_common_done = domain_suspend_callback_common_done;
-    domain_suspend_callback_common(egc, dsps);
-}
-
-static void domain_suspend_callback_common_done(libxl__egc *egc,
-                                libxl__domain_suspend_state *dsps, int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
-    dss->rc = rc;
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-/*======================= Domain resume ========================*/
-
-int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid)
-{
-    const char *path, *state;
-
-    switch (libxl__device_model_version_running(gc, domid)) {
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
-        uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-
-        path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
-        state = libxl__xs_read(gc, XBT_NULL, path);
-        if (state != NULL && !strcmp(state, "paused")) {
-            libxl__qemu_traditional_cmd(gc, domid, "continue");
-            libxl__wait_for_device_model_deprecated(gc, domid, "running",
-                                                    NULL, NULL, NULL);
-        }
-        break;
-    }
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-        if (libxl__qmp_resume(gc, domid))
-            return ERROR_FAIL;
-        break;
-    default:
-        return ERROR_INVAL;
-    }
-
-    return 0;
-}
-
-int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid, int suspend_cancel)
-{
-    int rc = 0;
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (type == LIBXL_DOMAIN_TYPE_HVM) {
-        rc = libxl__domain_resume_device_model_deprecated(gc, domid);
-        if (rc) {
-            LOGD(ERROR, domid, "failed to resume device model:%d", rc);
-            goto out;
-        }
-    }
-
-    if (xc_domain_resume(CTX->xch, domid, suspend_cancel)) {
-        LOGED(ERROR, domid, "xc_domain_resume failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (!xs_resume_domain(CTX->xsh, domid)) {
-        LOGED(ERROR, domid, "xs_resume_domain failed");
-        rc = ERROR_FAIL;
-    }
-out:
-    return rc;
-}
-
-static void dm_resume_init(libxl__dm_resume_state *dmrs)
-{
-    libxl__ev_qmp_init(&dmrs->qmp);
-    libxl__ev_time_init(&dmrs->time);
-    libxl__ev_xswatch_init(&dmrs->watch);
-}
-
-static void dm_resume_dispose(libxl__gc *gc,
-                              libxl__dm_resume_state *dmrs)
-{
-    libxl__ev_qmp_dispose(gc, &dmrs->qmp);
-    libxl__ev_time_deregister(gc, &dmrs->time);
-    libxl__ev_xswatch_deregister(gc, &dmrs->watch);
-}
-
-static void dm_resume_xswatch_cb(libxl__egc *egc,
-    libxl__ev_xswatch *, const char *watch_path, const char *);
-static void dm_resume_qmp_done(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *, int rc);
-static void dm_resume_timeout(libxl__egc *egc,
-    libxl__ev_time *, const struct timeval *, int rc);
-static void dm_resume_done(libxl__egc *egc,
-    libxl__dm_resume_state *dmrs, int rc);
-
-void libxl__dm_resume(libxl__egc *egc,
-                      libxl__dm_resume_state *dmrs)
-{
-    STATE_AO_GC(dmrs->ao);
-    int rc = 0;
-
-    /* Convenience aliases */
-    libxl_domid domid = dmrs->domid;
-    libxl__ev_qmp *qmp = &dmrs->qmp;
-
-    dm_resume_init(dmrs);
-
-    rc = libxl__ev_time_register_rel(dmrs->ao,
-                                     &dmrs->time,
-                                     dm_resume_timeout,
-                                     LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
-    if (rc) goto out;
-
-    switch (libxl__device_model_version_running(gc, domid)) {
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
-        uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
-        const char *path, *state;
-
-        path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
-        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &state);
-        if (rc) goto out;
-        if (!state || strcmp(state, "paused")) {
-            /* already running */
-            rc = 0;
-            goto out;
-        }
-
-        rc = libxl__qemu_traditional_cmd(gc, domid, "continue");
-        if (rc) goto out;
-        rc = libxl__ev_xswatch_register(gc, &dmrs->watch,
-                                        dm_resume_xswatch_cb,
-                                        path);
-        if (rc) goto out;
-        break;
-    }
-    case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-        qmp->ao = dmrs->ao;
-        qmp->domid = domid;
-        qmp->callback = dm_resume_qmp_done;
-        qmp->payload_fd = -1;
-        rc = libxl__ev_qmp_send(egc, qmp, "cont", NULL);
-        if (rc) goto out;
-        break;
-    default:
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    return;
-
-out:
-    dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_xswatch_cb(libxl__egc *egc,
-                                 libxl__ev_xswatch *xsw,
-                                 const char *watch_path,
-                                 const char *event_path)
-{
-    EGC_GC;
-    libxl__dm_resume_state *dmrs = CONTAINER_OF(xsw, *dmrs, watch);
-    int rc;
-    const char *value;
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL, watch_path, &value);
-    if (rc) goto out;
-
-    if (!value || strcmp(value, "running"))
-        return;
-
-    rc = 0;
-out:
-    dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_qmp_done(libxl__egc *egc,
-                               libxl__ev_qmp *qmp,
-                               const libxl__json_object *response,
-                               int rc)
-{
-    libxl__dm_resume_state *dmrs = CONTAINER_OF(qmp, *dmrs, qmp);
-    dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_timeout(libxl__egc *egc,
-                              libxl__ev_time *ev,
-                              const struct timeval *requested_abs,
-                              int rc)
-{
-    libxl__dm_resume_state *dmrs = CONTAINER_OF(ev, *dmrs, time);
-    dm_resume_done(egc, dmrs, rc);
-}
-
-static void dm_resume_done(libxl__egc *egc,
-                           libxl__dm_resume_state *dmrs,
-                           int rc)
-{
-    EGC_GC;
-
-    if (rc) {
-        LOGD(ERROR, dmrs->domid,
-             "Failed to resume device model: rc=%d", rc);
-    }
-
-    dm_resume_dispose(gc, dmrs);
-    dmrs->dm_resumed_callback(egc, dmrs, rc);
-}
-
-
-static void domain_resume_done(libxl__egc *egc,
-                               libxl__dm_resume_state *dmrs, int rc);
-
-void libxl__domain_resume(libxl__egc *egc,
-                          libxl__dm_resume_state *dmrs,
-                          bool suspend_cancel)
-{
-    STATE_AO_GC(dmrs->ao);
-    int rc = 0;
-    libxl_domain_type type = libxl__domain_type(gc, dmrs->domid);
-
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (type != LIBXL_DOMAIN_TYPE_HVM) {
-        rc = 0;
-        goto out;
-    }
-
-    dmrs->suspend_cancel = suspend_cancel;
-    dmrs->dm_resumed_callback = domain_resume_done;
-    libxl__dm_resume(egc, dmrs); /* must be last */
-    return;
-
-out:
-    domain_resume_done(egc, dmrs, rc);
-}
-
-static void domain_resume_done(libxl__egc *egc,
-                               libxl__dm_resume_state *dmrs, int rc)
-{
-    EGC_GC;
-
-    /* Convenience aliases */
-    libxl_domid domid = dmrs->domid;
-
-    if (rc) goto out;
-
-    if (xc_domain_resume(CTX->xch, domid, dmrs->suspend_cancel)) {
-        LOGED(ERROR, domid, "xc_domain_resume failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (!xs_resume_domain(CTX->xsh, domid)) {
-        LOGED(ERROR, domid, "xs_resume_domain failed");
-        rc = ERROR_FAIL;
-    }
-out:
-    dmrs->callback(egc, dmrs, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_domain.c b/tools/libxl/libxl_domain.c
deleted file mode 100644 (file)
index 5d4ec90..0000000
+++ /dev/null
@@ -1,2462 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-#define PAGE_TO_MEMKB(pages) ((pages) * 4)
-
-int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
-                         const char *old_name, const char *new_name,
-                         xs_transaction_t trans)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *dom_path = 0;
-    const char *name_path;
-    char *got_old_name;
-    unsigned int got_old_len;
-    xs_transaction_t our_trans = 0;
-    uint32_t stub_dm_domid;
-    const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL;
-    int rc;
-    libxl_dominfo info;
-    char *uuid;
-    const char *vm_name_path;
-
-    libxl_dominfo_init(&info);
-
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path) goto x_nomem;
-
-    name_path= GCSPRINTF("%s/name", dom_path);
-    if (!name_path) goto x_nomem;
-
-    stub_dm_domid = libxl_get_stubdom_id(CTX, domid);
-    if (stub_dm_domid) {
-        stub_dm_old_name = libxl__stub_dm_name(gc, old_name);
-        stub_dm_new_name = libxl__stub_dm_name(gc, new_name);
-    }
-
- retry_transaction:
-    if (!trans) {
-        trans = our_trans = xs_transaction_start(ctx->xsh);
-        if (!our_trans) {
-            LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name");
-            goto x_fail;
-        }
-    }
-
-    if (!new_name) {
-        LOGD(ERROR, domid, "New domain name not specified");
-        rc = ERROR_INVAL;
-        goto x_rc;
-    }
-
-    if (new_name[0]) {
-        /* nonempty names must be unique */
-        uint32_t domid_e;
-        rc = libxl_name_to_domid(ctx, new_name, &domid_e);
-        if (rc == ERROR_INVAL) {
-            /* no such domain, good */
-        } else if (rc != 0) {
-            LOGD(ERROR, domid, "Unexpected error checking for existing domain");
-            goto x_rc;
-        } else if (domid_e == domid) {
-            /* domain already has this name, ok (but we do still
-             * need the rest of the code as we may need to check
-             * old_name, for example). */
-        } else {
-            LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name);
-            rc = ERROR_INVAL;
-            goto x_rc;
-        }
-    }
-
-    if (old_name) {
-        got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len);
-        if (!got_old_name) {
-            LOGEVD(ERROR, errno, domid,
-                   "Check old name for domain allegedly named `%s'",
-                   old_name);
-            goto x_fail;
-        }
-        if (strcmp(old_name, got_old_name)) {
-            LOGD(ERROR, domid,
-                 "Allegedly named `%s' is actually named `%s' - racing ?",
-                 old_name,
-                 got_old_name);
-            free(got_old_name);
-            goto x_fail;
-        }
-        free(got_old_name);
-    }
-    if (!xs_write(ctx->xsh, trans, name_path,
-                  new_name, strlen(new_name))) {
-        LOGD(ERROR, domid,
-             "Failed to write new name `%s'"
-             " for domain previously named `%s'",
-             new_name,
-             old_name);
-        goto x_fail;
-    }
-
-    /* update /vm/<uuid>/name */
-    rc = libxl_domain_info(ctx, &info, domid);
-    if (rc)
-        goto x_rc;
-
-    uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
-    vm_name_path = GCSPRINTF("/vm/%s/name", uuid);
-    if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name))
-        goto x_fail;
-
-    if (stub_dm_domid) {
-        rc = libxl__domain_rename(gc, stub_dm_domid,
-                                  stub_dm_old_name,
-                                  stub_dm_new_name,
-                                  trans);
-        if (rc) {
-            LOGED(ERROR, domid, "Unable to rename stub-domain");
-            goto x_rc;
-        }
-    }
-
-    if (our_trans) {
-        if (!xs_transaction_end(ctx->xsh, our_trans, 0)) {
-            trans = our_trans = 0;
-            if (errno != EAGAIN) {
-                LOGD(ERROR, domid,
-                     "Failed to commit new name `%s'"
-                     " for domain previously named `%s'",
-                     new_name,
-                     old_name);
-                goto x_fail;
-            }
-            LOGD(DEBUG, domid,
-                 "Need to retry rename transaction"
-                 " for domain (name_path=\"%s\", new_name=\"%s\")",
-                 name_path,
-                 new_name);
-            goto retry_transaction;
-        }
-        our_trans = 0;
-    }
-
-    rc = 0;
- x_rc:
-    if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1);
-    libxl_dominfo_dispose(&info);
-    return rc;
-
- x_fail:  rc = ERROR_FAIL;  goto x_rc;
- x_nomem: rc = ERROR_NOMEM; goto x_rc;
-}
-
-int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
-                        const char *old_name, const char *new_name)
-{
-    GC_INIT(ctx);
-    int rc;
-    rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL);
-    GC_FREE;
-    return rc;
-}
-
-static void domain_resume_done(libxl__egc *egc,
-                               libxl__dm_resume_state *,
-                               int rc);
-
-int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
-                        const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__dm_resume_state *dmrs;
-
-    GCNEW(dmrs);
-    dmrs->ao = ao;
-    dmrs->domid = domid;
-    dmrs->callback = domain_resume_done;
-    libxl__domain_resume(egc, dmrs, suspend_cancel);
-    return AO_INPROGRESS;
-}
-
-static void domain_resume_done(libxl__egc *egc,
-                               libxl__dm_resume_state *dmrs,
-                               int rc)
-{
-    STATE_AO_GC(dmrs->ao);
-    libxl__ao_complete(egc, ao, rc);
-}
-
-/*
- * Preserves a domain but rewrites xenstore etc to make it unique so
- * that the domain can be restarted.
- *
- * Does not modify info so that it may be reused.
- */
-int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
-                          libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
-{
-    GC_INIT(ctx);
-    struct xs_permissions roperm[2];
-    xs_transaction_t t;
-    char *preserved_name;
-    char *uuid_string;
-    char *vm_path;
-    char *dom_path;
-
-    int rc;
-
-    preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
-    if (!preserved_name) {
-        GC_FREE;
-        return ERROR_NOMEM;
-    }
-
-    uuid_string = libxl__uuid2string(gc, new_uuid);
-    if (!uuid_string) {
-        GC_FREE;
-        return ERROR_NOMEM;
-    }
-
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path) {
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-
-    vm_path = GCSPRINTF("/vm/%s", uuid_string);
-    if (!vm_path) {
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-
-    roperm[0].id = 0;
-    roperm[0].perms = XS_PERM_NONE;
-    roperm[1].id = domid;
-    roperm[1].perms = XS_PERM_READ;
-
- retry_transaction:
-    t = xs_transaction_start(ctx->xsh);
-
-    xs_rm(ctx->xsh, t, vm_path);
-    xs_mkdir(ctx->xsh, t, vm_path);
-    xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
-
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
-    rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
-    if (rc) {
-        GC_FREE;
-        return rc;
-    }
-
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
-
-    if (!xs_transaction_end(ctx->xsh, t, 0))
-        if (errno == EAGAIN)
-            goto retry_transaction;
-
-    GC_FREE;
-    return 0;
-}
-
-void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
-                          const xc_domaininfo_t *xcinfo,
-                          libxl_dominfo *xlinfo)
-{
-    size_t size;
-
-    memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
-    xlinfo->domid = xcinfo->domain;
-    xlinfo->ssidref = xcinfo->ssidref;
-    if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
-                                   &xlinfo->ssid_label, &size) < 0)
-        xlinfo->ssid_label = NULL;
-
-    xlinfo->dying      = !!(xcinfo->flags&XEN_DOMINF_dying);
-    xlinfo->shutdown   = !!(xcinfo->flags&XEN_DOMINF_shutdown);
-    xlinfo->paused     = !!(xcinfo->flags&XEN_DOMINF_paused);
-    xlinfo->blocked    = !!(xcinfo->flags&XEN_DOMINF_blocked);
-    xlinfo->running    = !!(xcinfo->flags&XEN_DOMINF_running);
-    xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain);
-
-    if (xlinfo->shutdown)
-        xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
-    else
-        xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
-
-    xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
-    xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
-    xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
-    xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
-    xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
-    xlinfo->cpu_time = xcinfo->cpu_time;
-    xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
-    xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
-    xlinfo->cpupool = xcinfo->cpupool;
-    xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
-        LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
-}
-
-libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
-{
-    libxl_dominfo *ptr = NULL;
-    int i, ret;
-    xc_domaininfo_t *info;
-    int size = 0;
-    uint32_t domid = 0;
-    GC_INIT(ctx);
-
-    GCNEW_ARRAY(info, 1024);
-
-    while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) {
-        ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo));
-        for (i = 0; i < ret; i++) {
-            libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]);
-        }
-        domid = info[ret - 1].domain + 1;
-        size += ret;
-    }
-
-    if (ret < 0) {
-        LOGE(ERROR, "getting domain info list");
-        free(ptr);
-        GC_FREE;
-        return NULL;
-    }
-
-    *nb_domain_out = size;
-    GC_FREE;
-    return ptr;
-}
-
-int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
-                      uint32_t domid) {
-    xc_domaininfo_t xcinfo;
-    int ret;
-    GC_INIT(ctx);
-
-    ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo);
-    if (ret<0) {
-        LOGED(ERROR, domid, "Getting domain info list");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    if (ret==0 || xcinfo.domain != domid) {
-        GC_FREE;
-        return ERROR_DOMAIN_NOTFOUND;
-    }
-
-    if (info_r)
-        libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r);
-    GC_FREE;
-    return 0;
-}
-
-/* this API call only list VM running on this host. A VM can
- * be an aggregate of multiple domains. */
-libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
-{
-    GC_INIT(ctx);
-    libxl_dominfo *info;
-    libxl_vminfo *ptr = NULL;
-    int idx, i, n_doms;
-
-    info = libxl_list_domain(ctx, &n_doms);
-    if (!info)
-        goto out;
-
-    /*
-     * Always make sure to allocate at least one element; if we don't and we
-     * request zero, libxl__calloc (might) think its internal call to calloc
-     * has failed (if it returns null), if so it would kill our process.
-     */
-    ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo));
-
-    for (idx = i = 0; i < n_doms; i++) {
-        if (libxl_is_stubdom(ctx, info[i].domid, NULL))
-            continue;
-        ptr[idx].uuid = info[i].uuid;
-        ptr[idx].domid = info[i].domid;
-
-        idx++;
-    }
-    *nb_vm_out = idx;
-    libxl_dominfo_list_free(info, n_doms);
-
-out:
-    GC_FREE;
-    return ptr;
-}
-
-static void remus_failover_cb(libxl__egc *egc,
-                              libxl__domain_save_state *dss, int rc);
-
-int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
-                             uint32_t domid, int send_fd, int recv_fd,
-                             const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__domain_save_state *dss;
-    int rc;
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /* The caller must set this defbool */
-    if (libxl_defbool_is_default(info->colo)) {
-        LOGD(ERROR, domid, "Colo mode must be enabled/disabled");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl_defbool_setdefault(&info->allow_unsafe, false);
-    libxl_defbool_setdefault(&info->blackhole, false);
-    libxl_defbool_setdefault(&info->compression,
-                             !libxl_defbool_val(info->colo));
-    libxl_defbool_setdefault(&info->netbuf, true);
-    libxl_defbool_setdefault(&info->diskbuf, true);
-
-    if (libxl_defbool_val(info->colo) &&
-        libxl_defbool_val(info->compression)) {
-            LOGD(ERROR, domid, "Cannot use memory checkpoint "
-                        "compression in COLO mode");
-            rc = ERROR_FAIL;
-            goto out;
-    }
-
-    if (!libxl_defbool_val(info->allow_unsafe) &&
-        (libxl_defbool_val(info->blackhole) ||
-         !libxl_defbool_val(info->netbuf) ||
-         !libxl_defbool_val(info->diskbuf))) {
-        LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null,"
-                    "disable network buffering and disk replication");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-
-    GCNEW(dss);
-    dss->ao = ao;
-    dss->callback = remus_failover_cb;
-    dss->domid = domid;
-    dss->fd = send_fd;
-    dss->recv_fd = recv_fd;
-    dss->type = type;
-    dss->live = 1;
-    dss->debug = 0;
-    dss->remus = info;
-    if (libxl_defbool_val(info->colo))
-        dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO;
-    else
-        dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS;
-
-    assert(info);
-
-    /* Point of no return */
-    if (libxl_defbool_val(info->colo))
-        libxl__colo_save_setup(egc, &dss->css);
-    else
-        libxl__remus_setup(egc, &dss->rs);
-    return AO_INPROGRESS;
-
- out:
-    return AO_CREATE_FAIL(rc);
-}
-
-static void remus_failover_cb(libxl__egc *egc,
-                              libxl__domain_save_state *dss, int rc)
-{
-    STATE_AO_GC(dss->ao);
-    /*
-     * With Remus, if we reach this point, it means either
-     * backup died or some network error occurred preventing us
-     * from sending checkpoints.
-     */
-    libxl__ao_complete(egc, ao, rc);
-}
-
-static void domain_suspend_cb(libxl__egc *egc,
-                              libxl__domain_save_state *dss, int rc)
-{
-    STATE_AO_GC(dss->ao);
-    int flrc;
-
-    flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
-    /* If suspend has failed already then report that error not this one. */
-    if (flrc && !rc) rc = flrc;
-
-    libxl__ao_complete(egc,ao,rc);
-
-}
-
-int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
-                         const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int rc;
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out_err;
-    }
-
-    libxl__domain_save_state *dss;
-    GCNEW(dss);
-
-    dss->ao = ao;
-    dss->callback = domain_suspend_cb;
-
-    dss->domid = domid;
-    dss->fd = fd;
-    dss->type = type;
-    dss->live = flags & LIBXL_SUSPEND_LIVE;
-    dss->debug = flags & LIBXL_SUSPEND_DEBUG;
-    dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
-
-    rc = libxl__fd_flags_modify_save(gc, dss->fd,
-                                     ~(O_NONBLOCK|O_NDELAY), 0,
-                                     &dss->fdfl);
-    if (rc < 0) goto out_err;
-
-    libxl__domain_save(egc, dss);
-    return AO_INPROGRESS;
-
- out_err:
-    return AO_CREATE_FAIL(rc);
-}
-
-static void domain_suspend_empty_cb(libxl__egc *egc,
-                              libxl__domain_suspend_state *dss, int rc)
-{
-    STATE_AO_GC(dss->ao);
-    libxl__ao_complete(egc,ao,rc);
-}
-
-int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
-                              const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__domain_suspend_state *dsps;
-    int rc;
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out_err;
-    }
-
-    GCNEW(dsps);
-    dsps->ao = ao;
-    dsps->domid = domid;
-    dsps->type = type;
-    rc = libxl__domain_suspend_init(egc, dsps, type);
-    if (rc < 0) goto out_err;
-    dsps->callback_common_done = domain_suspend_empty_cb;
-    libxl__domain_suspend(egc, dsps);
-    return AO_INPROGRESS;
-
- out_err:
-    return AO_CREATE_FAIL(rc);
-}
-
-int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
-                       const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int r;
-    r = xc_domain_pause(ctx->xch, domid);
-    if (r < 0) {
-        LOGED(ERROR, domid, "Pausing domain");
-        return AO_CREATE_FAIL(ERROR_FAIL);
-    }
-    libxl__ao_complete(egc, ao, 0);
-    return AO_INPROGRESS;
-}
-
-int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
-                           const char *filename,
-                           const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int ret, rc;
-
-    ret = xc_domain_dumpcore(ctx->xch, domid, filename);
-    if (ret<0) {
-        LOGED(ERROR, domid, "Core dumping domain to %s", filename);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-out:
-
-    libxl__ao_complete(egc, ao, rc);
-
-    return AO_INPROGRESS;
-}
-
-int libxl__domain_unpause_deprecated(libxl__gc *gc, libxl_domid domid)
-{
-    int r, rc;
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (type == LIBXL_DOMAIN_TYPE_HVM) {
-        rc = libxl__domain_resume_device_model_deprecated(gc, domid);
-        if (rc < 0) {
-            LOGD(ERROR, domid,
-                 "Failed to unpause device model for domain: %d", rc);
-            goto out;
-        }
-    }
-    r = xc_domain_unpause(CTX->xch, domid);
-    if (r < 0) {
-        LOGED(ERROR, domid, "Unpausing domain");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = 0;
-out:
-    return rc;
-}
-
-static void domain_unpause_done(libxl__egc *egc,
-                                libxl__dm_resume_state *,
-                                int rc);
-
-void libxl__domain_unpause(libxl__egc *egc,
-                           libxl__dm_resume_state *dmrs)
-{
-    STATE_AO_GC(dmrs->ao);
-    int rc = 0;
-
-    /* Convenience aliases */
-    libxl_domid domid = dmrs->domid;
-
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (type == LIBXL_DOMAIN_TYPE_HVM) {
-        dmrs->dm_resumed_callback = domain_unpause_done;
-        libxl__dm_resume(egc, dmrs); /* must be last */
-        return;
-    }
-    rc = 0;
-out:
-    domain_unpause_done(egc, dmrs, rc);
-}
-
-static void domain_unpause_done(libxl__egc *egc,
-                                libxl__dm_resume_state *dmrs,
-                                int rc)
-{
-    EGC_GC;
-    int r;
-
-    /* Convenience aliases */
-    libxl_domid domid = dmrs->domid;
-
-    if (rc) goto out;
-
-    r = xc_domain_unpause(CTX->xch, domid);
-    if (r < 0) {
-        LOGED(ERROR, domid, "Unpausing domain");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = 0;
-out:
-    dmrs->callback(egc, dmrs, rc);
-}
-
-static void domain_unpause_ao_done(libxl__egc *egc,
-                                   libxl__dm_resume_state *,
-                                   int rc);
-
-int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
-                         const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__dm_resume_state *dmrs;
-
-    GCNEW(dmrs);
-    dmrs->ao = ao;
-    dmrs->domid = domid;
-    dmrs->callback = domain_unpause_ao_done;
-    libxl__domain_unpause(egc, dmrs); /* must be last */
-    return AO_INPROGRESS;
-}
-
-static void domain_unpause_ao_done(libxl__egc *egc,
-                                   libxl__dm_resume_state *dmrs,
-                                   int rc)
-{
-    STATE_AO_GC(dmrs->ao);
-
-    libxl__ao_complete(egc, ao, rc);
-}
-
-int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-
-    uint64_t pvdriver = 0;
-    int ret;
-
-    libxl_domain_type domtype = libxl__domain_type(gc, domid);
-    if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
-        return ERROR_FAIL;
-
-    if (domtype != LIBXL_DOMAIN_TYPE_HVM)
-        return 1;
-
-    ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
-    if (ret<0) {
-        LOGED(ERROR, domid, "Getting HVM callback IRQ");
-        return ERROR_FAIL;
-    }
-    return !!pvdriver;
-}
-
-const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
-{
-    const char *dom_path;
-
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path)
-        return NULL;
-
-    return GCSPRINTF("%s/control/shutdown", dom_path);
-}
-
-char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
-                                    uint32_t domid)
-{
-    const char *shutdown_path;
-
-    shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
-    if (!shutdown_path)
-        return NULL;
-
-    return libxl__xs_read(gc, t, shutdown_path);
-}
-
-int libxl__domain_pvcontrol(libxl__egc *egc, libxl__xswait_state *pvcontrol,
-                            domid_t domid, const char *cmd)
-{
-    STATE_AO_GC(pvcontrol->ao);
-    const char *shutdown_path;
-    int rc;
-
-    rc = libxl__domain_pvcontrol_available(gc, domid);
-    if (rc < 0)
-        return rc;
-
-    if (!rc)
-        return ERROR_NOPARAVIRT;
-
-    shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
-    if (!shutdown_path)
-        return ERROR_FAIL;
-
-    rc = libxl__xs_printf(gc, XBT_NULL, shutdown_path, "%s", cmd);
-    if (rc)
-        return rc;
-
-    pvcontrol->path = shutdown_path;
-    pvcontrol->what = GCSPRINTF("guest acknowledgement of %s request", cmd);
-    pvcontrol->timeout_ms = 60 * 1000;
-    rc = libxl__xswait_start(gc, pvcontrol);
-    if (rc)
-        return rc;
-
-    return 0;
-}
-
-static bool pvcontrol_acked(const char *state)
-{
-    if (!state || !strcmp(state,""))
-        return true;
-
-    return false;
-}
-
-/* Xenstore watch callback prototype for the reboot/poweroff operations. */
-static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
-                         const char *state);
-
-int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
-                          const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__xswait_state *pvcontrol;
-    int rc;
-
-    GCNEW(pvcontrol);
-    pvcontrol->ao = ao;
-    pvcontrol->callback = pvcontrol_cb;
-    rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "poweroff");
-
-    return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
-}
-
-int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
-                        const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__xswait_state *pvcontrol;
-    int rc;
-
-    GCNEW(pvcontrol);
-    pvcontrol->ao = ao;
-    pvcontrol->callback = pvcontrol_cb;
-    rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "reboot");
-
-    return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
-}
-
-static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
-                         const char *state)
-{
-    STATE_AO_GC(xswa->ao);
-
-    if (!rc && !pvcontrol_acked(state))
-        return;
-
-    libxl__xswait_stop(gc, xswa);
-
-    if (rc)
-        LOG(ERROR, "guest didn't acknowledge control request: %d", rc);
-
-    libxl__ao_complete(egc, ao, rc);
-}
-
-static void domain_death_occurred(libxl__egc *egc,
-                                  libxl_evgen_domain_death **evg_upd,
-                                  const char *why) {
-    /* Removes **evg_upd from death_list and puts it on death_reported
-     * and advances *evg_upd to the next entry.
-     * Call sites in domain_death_xswatch_callback must use "continue". */
-    EGC_GC;
-    libxl_evgen_domain_death *const evg = *evg_upd;
-
-    LOGD(DEBUG, evg->domid, "%s", why);
-
-    libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry);
-    *evg_upd = evg_next;
-
-    libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
-
-    libxl__event_occurred(egc, ev);
-
-    evg->death_reported = 1;
-    LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
-    LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
-}
-
-static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
-                                        const char *wpath, const char *epath) {
-    EGC_GC;
-    libxl_evgen_domain_death *evg;
-    int rc;
-
-    CTX_LOCK;
-
-    evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
-
-    for (;;) {
-        if (!evg) goto out;
-
-        int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
-        xc_domaininfo_t domaininfos[nentries];
-        const xc_domaininfo_t *got = domaininfos, *gotend;
-
-        rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
-        if (rc == -1) {
-            LIBXL__EVENT_DISASTER(gc, "xc_domain_getinfolist failed while"
-                                  " processing @releaseDomain watch event",
-                                  errno, 0);
-            goto out;
-        }
-        gotend = &domaininfos[rc];
-
-        LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld",
-             evg, nentries, rc,
-             rc>0 ? (long)domaininfos[0].domain : 0,
-             rc>0 ? (long)domaininfos[rc-1].domain : 0);
-
-        for (;;) {
-            if (!evg) {
-                LOG(DEBUG, "[evg=0] all reported");
-                goto all_reported;
-            }
-
-            LOGD(DEBUG, evg->domid, "[evg=%p]"
-                 "   got=domaininfos[%d] got->domain=%ld",
-                 evg, (int)(got - domaininfos),
-                 got < gotend ? (long)got->domain : -1L);
-
-            if (!rc) {
-                domain_death_occurred(egc, &evg, "empty list");
-                continue;
-            }
-
-            if (got == gotend) {
-                LOG(DEBUG, " got==gotend");
-                break;
-            }
-
-            if (got->domain > evg->domid) {
-                /* ie, the list doesn't contain evg->domid any more so
-                 * the domain has been destroyed */
-                domain_death_occurred(egc, &evg, "missing from list");
-                continue;
-            }
-
-            if (got->domain < evg->domid) {
-                got++;
-                continue;
-            }
-
-            assert(evg->domid == got->domain);
-            LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x",
-                 evg->shutdown_reported, got->flags);
-
-            if (got->flags & XEN_DOMINF_dying) {
-                domain_death_occurred(egc, &evg, "dying");
-                continue;
-            }
-
-            if (!evg->shutdown_reported &&
-                (got->flags & XEN_DOMINF_shutdown)) {
-                libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
-                                            got->domain, evg->user);
-
-                LOG(DEBUG, " shutdown reporting");
-
-                ev->u.domain_shutdown.shutdown_reason =
-                    (got->flags >> XEN_DOMINF_shutdownshift) &
-                    XEN_DOMINF_shutdownmask;
-                libxl__event_occurred(egc, ev);
-
-                evg->shutdown_reported = 1;
-            }
-            evg = LIBXL_TAILQ_NEXT(evg, entry);
-        }
-
-        assert(rc); /* rc==0 results in us eating all evgs and quitting */
-    }
- all_reported:
- out:
-
-    LOG(DEBUG, "domain death search done");
-
-    CTX_UNLOCK;
-}
-
-int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
-                libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
-    GC_INIT(ctx);
-    libxl_evgen_domain_death *evg, *evg_search;
-    int rc;
-
-    CTX_LOCK;
-
-    evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
-    memset(evg, 0, sizeof(*evg));
-    evg->domid = domid;
-    evg->user = user;
-
-    LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
-                              evg->domid > evg_search->domid);
-
-    if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
-        rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
-                        domain_death_xswatch_callback, "@releaseDomain");
-        if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
-    }
-
-    *evgen_out = evg;
-    rc = 0;
-
- out:
-    CTX_UNLOCK;
-    GC_FREE;
-    return rc;
-};
-
-void libxl__evdisable_domain_death(libxl__gc *gc,
-                                   libxl_evgen_domain_death *evg) {
-    CTX_LOCK;
-
-    if (!evg->death_reported)
-        LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
-    else
-        LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
-
-    free(evg);
-
-    if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
-        libxl__ev_xswatch_isregistered(&CTX->death_watch))
-        libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
-
-    CTX_UNLOCK;
-}
-
-void libxl_evdisable_domain_death(libxl_ctx *ctx,
-                                  libxl_evgen_domain_death *evg) {
-    GC_INIT(ctx);
-    libxl__evdisable_domain_death(gc, evg);
-    GC_FREE;
-}
-
-/* Callbacks for libxl_domain_destroy */
-
-static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
-                              int rc);
-
-int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
-                         const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__domain_destroy_state *dds;
-
-    GCNEW(dds);
-    dds->ao = ao;
-    dds->domid = domid;
-    dds->callback = domain_destroy_cb;
-    libxl__domain_destroy(egc, dds);
-
-    return AO_INPROGRESS;
-}
-
-static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
-                              int rc)
-{
-    STATE_AO_GC(dds->ao);
-
-    if (rc)
-        LOGD(ERROR, dds->domid, "Destruction of domain failed");
-
-    libxl__ao_complete(egc, ao, rc);
-}
-
-/* Callbacks for libxl__domain_destroy */
-
-static void stubdom_destroy_callback(libxl__egc *egc,
-                                     libxl__destroy_domid_state *dis,
-                                     int rc);
-
-static void domain_destroy_callback(libxl__egc *egc,
-                                    libxl__destroy_domid_state *dis,
-                                    int rc);
-
-static void destroy_finish_check(libxl__egc *egc,
-                                 libxl__domain_destroy_state *dds);
-
-void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
-{
-    STATE_AO_GC(dds->ao);
-    uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
-
-    if (stubdomid) {
-        dds->stubdom.ao = ao;
-        dds->stubdom.domid = stubdomid;
-        dds->stubdom.callback = stubdom_destroy_callback;
-        dds->stubdom.soft_reset = false;
-        libxl__destroy_domid(egc, &dds->stubdom);
-    } else {
-        dds->stubdom_finished = 1;
-    }
-
-    dds->domain.ao = ao;
-    dds->domain.domid = dds->domid;
-    dds->domain.callback = domain_destroy_callback;
-    dds->domain.soft_reset = dds->soft_reset;
-    libxl__destroy_domid(egc, &dds->domain);
-}
-
-static void stubdom_destroy_callback(libxl__egc *egc,
-                                     libxl__destroy_domid_state *dis,
-                                     int rc)
-{
-    STATE_AO_GC(dis->ao);
-    libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
-    const char *savefile;
-
-    if (rc) {
-        LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u",
-             dis->domid);
-        dds->rc = rc;
-    }
-
-    dds->stubdom_finished = 1;
-    savefile = libxl__device_model_savefile(gc, dis->domid);
-    rc = libxl__remove_file(gc, savefile);
-    if (rc) {
-        LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s",
-             savefile);
-    }
-
-    destroy_finish_check(egc, dds);
-}
-
-static void domain_destroy_callback(libxl__egc *egc,
-                                    libxl__destroy_domid_state *dis,
-                                    int rc)
-{
-    STATE_AO_GC(dis->ao);
-    libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
-
-    if (rc) {
-        LOGD(ERROR, dis->domid, "Unable to destroy guest");
-        dds->rc = rc;
-    }
-
-    dds->domain_finished = 1;
-    destroy_finish_check(egc, dds);
-}
-
-static void destroy_finish_check(libxl__egc *egc,
-                                 libxl__domain_destroy_state *dds)
-{
-    if (!(dds->domain_finished && dds->stubdom_finished))
-        return;
-
-    dds->callback(egc, dds, dds->rc);
-}
-
-/* Callbacks for libxl__destroy_domid */
-static void destroy_domid_pci_done(libxl__egc *egc,
-                                   libxl__multidev *multidev,
-                                   int rc);
-static void dm_destroy_cb(libxl__egc *egc,
-                          libxl__destroy_devicemodel_state *ddms,
-                          int rc);
-
-static void devices_destroy_cb(libxl__egc *egc,
-                               libxl__devices_remove_state *drs,
-                               int rc);
-
-static void domain_destroy_domid_cb(libxl__egc *egc,
-                                    libxl__ev_child *destroyer,
-                                    pid_t pid, int status);
-
-void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
-{
-    STATE_AO_GC(dis->ao);
-    uint32_t domid = dis->domid;
-    int rc;
-
-    libxl__ev_child_init(&dis->destroyer);
-
-    rc = libxl_domain_info(CTX, NULL, domid);
-    switch(rc) {
-    case 0:
-        break;
-    case ERROR_DOMAIN_NOTFOUND:
-        LOGD(ERROR, domid, "Non-existant domain");
-    default:
-        goto out;
-    }
-
-    libxl__multidev_begin(ao, &dis->multidev);
-    dis->multidev.callback = destroy_domid_pci_done;
-    libxl__device_pci_destroy_all(egc, domid, &dis->multidev);
-    libxl__multidev_prepared(egc, &dis->multidev, 0);
-    return;
-
-out:
-    assert(rc);
-    dis->callback(egc, dis, rc);
-}
-
-static void destroy_domid_pci_done(libxl__egc *egc,
-                                   libxl__multidev *multidev,
-                                   int rc)
-{
-    STATE_AO_GC(multidev->ao);
-    libxl__destroy_domid_state *dis =
-        CONTAINER_OF(multidev, *dis, multidev);
-    int dm_present;
-    int r;
-
-    /* Convenience aliases */
-    libxl_domid domid = dis->domid;
-
-    if (rc) {
-        LOGD(ERROR, domid, "Pci shutdown failed");
-        goto out;
-    }
-
-    r = xc_domain_pause(CTX->xch, domid);
-    if (r < 0) {
-        LOGEVD(ERROR, r, domid, "xc_domain_pause failed");
-        rc = ERROR_FAIL;
-    }
-
-    switch (libxl__domain_type(gc, domid)) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        if (libxl_get_stubdom_id(CTX, domid)) {
-            dm_present = 0;
-            break;
-        }
-        /* fall through */
-    case LIBXL_DOMAIN_TYPE_PVH:
-    case LIBXL_DOMAIN_TYPE_PV:
-        dm_present = libxl__dm_active(gc, domid);
-        break;
-    case LIBXL_DOMAIN_TYPE_INVALID:
-        rc = ERROR_FAIL;
-        goto out;
-    default:
-        abort();
-    }
-
-    if (dm_present) {
-        dis->ddms.ao = ao;
-        dis->ddms.domid = domid;
-        dis->ddms.callback = dm_destroy_cb;
-
-        libxl__destroy_device_model(egc, &dis->ddms);
-        return;
-    } else {
-        dm_destroy_cb(egc, &dis->ddms, 0);
-        return;
-    }
-
-out:
-    assert(rc);
-    dis->callback(egc, dis, rc);
-    return;
-}
-
-static void dm_destroy_cb(libxl__egc *egc,
-                          libxl__destroy_devicemodel_state *ddms,
-                          int rc)
-{
-    libxl__destroy_domid_state *dis = CONTAINER_OF(ddms, *dis, ddms);
-    STATE_AO_GC(dis->ao);
-    uint32_t domid = dis->domid;
-    uint32_t target_domid;
-
-    if (rc < 0)
-        LOGD(ERROR, domid, "libxl__destroy_device_model failed");
-
-    if (libxl_is_stubdom(CTX, domid, &target_domid) &&
-        libxl__stubdomain_is_linux_running(gc, target_domid)) {
-        char *path = GCSPRINTF("/local/domain/%d/image/qmp-proxy-pid", domid);
-
-        libxl__kill_xs_path(gc, path, "QMP Proxy");
-        /* qmp-proxy for stubdom registers target_domid's QMP sockets. */
-        libxl__qmp_cleanup(gc, target_domid);
-    }
-
-    dis->drs.ao = ao;
-    dis->drs.domid = domid;
-    dis->drs.callback = devices_destroy_cb;
-    dis->drs.force.flag = LIBXL__FORCE_ON;
-    libxl__devices_destroy(egc, &dis->drs);
-}
-
-static unsigned int libxl__get_domid_reuse_timeout(void)
-{
-    const char *env_timeout = getenv("LIBXL_DOMID_REUSE_TIMEOUT");
-
-    return env_timeout ? strtol(env_timeout, NULL, 0) :
-        LIBXL_DOMID_REUSE_TIMEOUT;
-}
-
-char *libxl__domid_history_path(libxl__gc *gc, const char *suffix)
-{
-    return GCSPRINTF("%s/domid-history%s", libxl__run_dir_path(),
-                     suffix ?: "");
-}
-
-int libxl_clear_domid_history(libxl_ctx *ctx)
-{
-    GC_INIT(ctx);
-    char *path;
-    int rc = ERROR_FAIL;
-
-    path = libxl__domid_history_path(gc, NULL);
-    if (!path)
-        goto out;
-
-    if (unlink(path) < 0 && errno != ENOENT) {
-        LOGE(ERROR, "failed to remove '%s'\n", path);
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-struct libxl__domid_history {
-    long timeout;
-    char *path;
-    FILE *f;
-    struct timespec ts;
-};
-
-static void libxl__domid_history_dispose(
-    struct libxl__domid_history *ctxt)
-{
-    if (ctxt->f) {
-        fclose(ctxt->f);
-        ctxt->f = NULL;
-    }
-}
-
-static int libxl__open_domid_history(libxl__gc *gc,
-                                     struct libxl__domid_history *ctxt)
-{
-    ctxt->timeout = libxl__get_domid_reuse_timeout();
-    ctxt->path = libxl__domid_history_path(gc, NULL);
-
-    ctxt->f = fopen(ctxt->path, "r");
-    if (!ctxt->f && errno != ENOENT) {
-        LOGE(ERROR, "failed to open '%s'", ctxt->path);
-        return ERROR_FAIL;
-    }
-
-    if (clock_gettime(CLOCK_MONOTONIC, &ctxt->ts)) {
-        LOGE(ERROR, "failed to get time");
-        libxl__domid_history_dispose(ctxt);
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-static int libxl__close_domid_history(libxl__gc *gc,
-                                      struct libxl__domid_history *ctxt)
-{
-    int r;
-
-    if (!ctxt->f) return 0;
-
-    r = fclose(ctxt->f);
-    ctxt->f = NULL;
-    if (r == EOF) {
-        LOGE(ERROR, "failed to close '%s'", ctxt->path);
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-static int libxl__read_recent(libxl__gc *gc,
-                              struct libxl__domid_history *ctxt,
-                              unsigned long *sec, unsigned int *domid)
-{
-    if (!ctxt->f) {
-        *domid = INVALID_DOMID;
-        return 0;
-    }
-
-    for (;;) {
-        int r = fscanf(ctxt->f, "%lu %u", sec, domid);
-
-        if (r == EOF) {
-            if (ferror(ctxt->f)) {
-                LOGE(ERROR, "failed to read from '%s'", ctxt->path);
-                return ERROR_FAIL;
-            }
-
-            *domid = INVALID_DOMID;
-            break;
-        } else if (r == 2 && libxl_domid_valid_guest(*domid) &&
-                   ctxt->ts.tv_sec - *sec <= ctxt->timeout) {
-            break;
-        }
-    }
-
-    return 0;
-}
-
-static int libxl__mark_domid_recent(libxl__gc *gc, uint32_t domid)
-{
-    libxl__flock *lock;
-    struct libxl__domid_history ctxt = {};
-    char *new;
-    FILE *nf = NULL;
-    int r, rc;
-
-    lock = libxl__lock_domid_history(gc);
-    if (!lock) {
-        LOGED(ERROR, domid, "failed to acquire lock");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__open_domid_history(gc, &ctxt);
-    if (rc) goto out;
-
-    new = libxl__domid_history_path(gc, ".new");
-    nf = fopen(new, "a");
-    if (!nf) {
-        LOGED(ERROR, domid, "failed to open '%s'", new);
-        goto out;
-    }
-
-    for (;;) {
-        unsigned long sec;
-        unsigned int val;
-
-        rc = libxl__read_recent(gc, &ctxt, &sec, &val);
-        if (rc) goto out;
-
-        if (val == INVALID_DOMID) /* EOF */
-            break;
-
-        r = fprintf(nf, "%lu %u\n", sec, val);
-        if (r < 0) {
-            LOGED(ERROR, domid, "failed to write to '%s'", new);
-            goto out;
-        }
-    }
-
-    r = fprintf(nf, "%lu %u\n", ctxt.ts.tv_sec, domid);
-    if (r < 0) {
-        LOGED(ERROR, domid, "failed to write to '%s'", new);
-        goto out;
-    }
-
-    r = fclose(nf);
-    nf = NULL;
-    if (r == EOF) {
-        LOGED(ERROR, domid, "failed to close '%s'", new);
-        goto out;
-    }
-
-    rc = libxl__close_domid_history(gc, &ctxt);
-    if (rc) goto out;
-
-    r = rename(new, ctxt.path);
-    if (r) {
-        LOGE(ERROR, "failed to rename '%s' -> '%s'", new, ctxt.path);
-        return ERROR_FAIL;
-    }
-
-out:
-    if (nf) fclose(nf);
-    libxl__domid_history_dispose(&ctxt);
-    if (lock) libxl__unlock_file(lock);
-
-    return rc;
-}
-
-int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent)
-{
-    struct libxl__domid_history ctxt = {};
-    int rc;
-
-    rc = libxl__open_domid_history(gc, &ctxt);
-    if (rc) goto out;
-
-    *recent = false;
-    for (;;) {
-        unsigned long sec;
-        unsigned int val;
-
-        rc = libxl__read_recent(gc, &ctxt, &sec, &val);
-        if (rc) goto out;
-
-        if (val == INVALID_DOMID) /* EOF */
-            break;
-
-        if (val == domid && ctxt.ts.tv_sec - sec <= ctxt.timeout) {
-            *recent = true;
-            break;
-        }
-    }
-
-    rc = libxl__close_domid_history(gc, &ctxt);
-
-out:
-    libxl__domid_history_dispose(&ctxt);
-
-    return rc;
-}
-
-static void devices_destroy_cb(libxl__egc *egc,
-                               libxl__devices_remove_state *drs,
-                               int rc)
-{
-    STATE_AO_GC(drs->ao);
-    libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
-    libxl_ctx *ctx = CTX;
-    uint32_t domid = dis->domid;
-    char *dom_path;
-    char *vm_path;
-    libxl__flock *lock;
-
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (rc < 0)
-        LOGD(ERROR, domid, "libxl__devices_destroy failed");
-
-    vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
-    if (vm_path)
-        if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
-            LOGED(ERROR, domid, "xs_rm failed for %s", vm_path);
-
-    if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
-        LOGED(ERROR, domid, "xs_rm failed for %s", dom_path);
-
-    xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
-    xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
-
-    /* This is async operation, we already hold CTX lock */
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-    libxl__userdata_destroyall(gc, domid);
-
-    libxl__unlock_file(lock);
-
-    /* Clean up qemu-save and qemu-resume files. They are
-     * intermediate files created by libxc. Unfortunately they
-     * don't fit in existing userdata scheme very well. In soft reset
-     * case we need to keep the file.
-     */
-    if (!dis->soft_reset) {
-        rc = libxl__remove_file(gc,
-                                libxl__device_model_savefile(gc, domid));
-        if (rc < 0) goto out;
-    }
-    rc = libxl__remove_file(gc,
-             GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
-    if (rc < 0) goto out;
-
-    rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
-    if (rc < 0) goto out;
-    if (!rc) { /* child */
-        ctx->xch = xc_interface_open(ctx->lg,0,0);
-        if (!ctx->xch) goto badchild;
-
-        if (!dis->soft_reset) {
-            rc = libxl__mark_domid_recent(gc, domid);
-            if (rc) goto badchild;
-            rc = xc_domain_destroy(ctx->xch, domid);
-        } else {
-            rc = xc_domain_pause(ctx->xch, domid);
-            if (rc < 0) goto badchild;
-            rc = xc_domain_soft_reset(ctx->xch, domid);
-            if (rc < 0) goto badchild;
-            rc = xc_domain_unpause(ctx->xch, domid);
-        }
-        if (rc < 0) goto badchild;
-        _exit(0);
-
-    badchild:
-        if (errno > 0  && errno < 126) {
-            _exit(errno);
-        } else {
-            LOGED(ERROR, domid,
- "xc_domain_destroy failed (with difficult errno value %d)",
-                  errno);
-            _exit(-1);
-        }
-    }
-    LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc);
-
-    return;
-
-out:
-    dis->callback(egc, dis, rc);
-    return;
-}
-
-static void domain_destroy_domid_cb(libxl__egc *egc,
-                                    libxl__ev_child *destroyer,
-                                    pid_t pid, int status)
-{
-    libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
-    STATE_AO_GC(dis->ao);
-    int rc;
-
-    if (status) {
-        if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
-            LOGEVD(ERROR, WEXITSTATUS(status), dis->domid,
-                   "xc_domain_destroy failed");
-        } else {
-            libxl_report_child_exitstatus(CTX, XTL_ERROR,
-                                          "async domain destroy", pid, status);
-        }
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = 0;
-
- out:
-    dis->callback(egc, dis, rc);
-}
-
-int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
-{
-    int rc;
-    const char *xs_domid;
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
-    if (rc) goto out;
-    if (!xs_domid) {
-        LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    *domid = atoi(xs_domid);
-
-out:
-    return rc;
-}
-
-int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
-{
-    if (!name)
-        return 0;
-    return libxl_domain_qualifier_to_domid(CTX, name, domid);
-}
-
-libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
-                                       int *nr_vcpus_out, int *nr_cpus_out)
-{
-    GC_INIT(ctx);
-    libxl_vcpuinfo *ptr, *ret;
-    xc_domaininfo_t domaininfo;
-    xc_vcpuinfo_t vcpuinfo;
-
-    if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) {
-        LOGED(ERROR, domid, "Getting infolist");
-        GC_FREE;
-        return NULL;
-    }
-
-    if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
-        GC_FREE;
-        return NULL;
-    }
-
-    *nr_cpus_out = libxl_get_max_cpus(ctx);
-    ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
-                              sizeof(libxl_vcpuinfo));
-
-    for (*nr_vcpus_out = 0;
-         *nr_vcpus_out <= domaininfo.max_vcpu_id;
-         ++*nr_vcpus_out, ++ptr) {
-        libxl_bitmap_init(&ptr->cpumap);
-        if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
-            goto err;
-        libxl_bitmap_init(&ptr->cpumap_soft);
-        if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
-            goto err;
-        if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) {
-            LOGED(ERROR, domid, "Getting vcpu info");
-            goto err;
-        }
-
-        if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out,
-                                ptr->cpumap.map, ptr->cpumap_soft.map,
-                                XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
-            LOGED(ERROR, domid, "Getting vcpu affinity");
-            goto err;
-        }
-        ptr->vcpuid = *nr_vcpus_out;
-        ptr->cpu = vcpuinfo.cpu;
-        ptr->online = !!vcpuinfo.online;
-        ptr->blocked = !!vcpuinfo.blocked;
-        ptr->running = !!vcpuinfo.running;
-        ptr->vcpu_time = vcpuinfo.cpu_time;
-    }
-    GC_FREE;
-    return ret;
-
-err:
-    libxl_bitmap_dispose(&ptr->cpumap);
-    libxl_bitmap_dispose(&ptr->cpumap_soft);
-    free(ret);
-    GC_FREE;
-    return NULL;
-}
-
-static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
-                                          const libxl_bitmap *cpumap,
-                                          const libxl_dominfo *info)
-{
-    char *dompath;
-    xs_transaction_t t;
-    int i, rc = ERROR_FAIL;
-
-    if (!(dompath = libxl__xs_get_dompath(gc, domid)))
-        goto out;
-
-retry_transaction:
-    t = xs_transaction_start(CTX->xsh);
-    for (i = 0; i <= info->vcpu_max_id; i++)
-        libxl__xs_printf(gc, t,
-                         GCSPRINTF("%s/cpu/%u/availability", dompath, i),
-                         "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
-    if (!xs_transaction_end(CTX->xsh, t, 0)) {
-        if (errno == EAGAIN)
-            goto retry_transaction;
-    } else
-        rc = 0;
-out:
-    return rc;
-}
-
-static int qmp_parse_query_cpus(libxl__gc *gc,
-                                libxl_domid domid,
-                                const libxl__json_object *response,
-                                libxl_bitmap *const map)
-{
-    int i;
-    const libxl__json_object *cpu;
-
-    libxl_bitmap_set_none(map);
-    /* Parse response to QMP command "query-cpus":
-     * [ { 'CPU': 'int',...} ]
-     */
-    for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
-        unsigned int cpu_index;
-        const libxl__json_object *o;
-
-        o = libxl__json_map_get("CPU", cpu, JSON_INTEGER);
-        if (!o) {
-            LOGD(ERROR, domid, "Failed to retrieve CPU index.");
-            return ERROR_QEMU_API;
-        }
-
-        cpu_index = libxl__json_object_get_integer(o);
-        libxl_bitmap_set(map, cpu_index);
-    }
-
-    return 0;
-}
-
-typedef struct set_vcpuonline_state {
-    libxl__ev_qmp qmp;
-    libxl__ev_time timeout;
-    const libxl_bitmap *cpumap;
-    libxl_dominfo info;
-    libxl_bitmap final_map;
-    int index; /* for loop on final_map */
-} set_vcpuonline_state;
-
-static void set_vcpuonline_qmp_cpus_queried(libxl__egc *,
-    libxl__ev_qmp *, const libxl__json_object *, int rc);
-static void set_vcpuonline_qmp_add_cpu(libxl__egc *,
-    libxl__ev_qmp *, const libxl__json_object *response, int rc);
-static void set_vcpuonline_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void set_vcpuonline_done(libxl__egc *egc,
-    set_vcpuonline_state *svos, int rc);
-
-int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
-                         libxl_bitmap *cpumap,
-                         const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int rc, maxcpus;
-    set_vcpuonline_state *svos;
-
-    GCNEW(svos);
-    libxl__ev_qmp_init(&svos->qmp);
-    svos->qmp.ao = ao;
-    svos->qmp.domid = domid;
-    svos->qmp.payload_fd = -1;
-    libxl__ev_time_init(&svos->timeout);
-    svos->cpumap = cpumap;
-    libxl_dominfo_init(&svos->info);
-    libxl_bitmap_init(&svos->final_map);
-
-    /* Convenience aliases */
-    libxl_dominfo *info = &svos->info;
-    libxl__ev_qmp *qmp = &svos->qmp;
-
-    rc = libxl_domain_info(CTX, info, domid);
-    if (rc < 0) {
-        LOGED(ERROR, domid, "Getting domain info list");
-        goto out;
-    }
-
-    maxcpus = libxl_bitmap_count_set(cpumap);
-    if (maxcpus == 0)
-    {
-        LOGED(ERROR, domid, "Requested 0 VCPUs!");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (maxcpus > info->vcpu_max_id + 1)
-    {
-        LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!",
-              maxcpus, info->vcpu_max_id + 1);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    switch (libxl__domain_type(gc, domid)) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        switch (libxl__device_model_version_running(gc, domid)) {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            break;
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            rc = libxl__ev_time_register_rel(ao, &svos->timeout,
-                                             set_vcpuonline_timeout,
-                                             LIBXL_QMP_CMD_TIMEOUT * 1000);
-            if (rc) goto out;
-            qmp->callback = set_vcpuonline_qmp_cpus_queried;
-            rc = libxl__ev_qmp_send(egc, qmp, "query-cpus", NULL);
-            if (rc) goto out;
-            return AO_INPROGRESS;
-        default:
-            rc = ERROR_INVAL;
-        }
-        break;
-    case LIBXL_DOMAIN_TYPE_PVH:
-    case LIBXL_DOMAIN_TYPE_PV:
-        break;
-    default:
-        rc = ERROR_INVAL;
-    }
-
-out:
-    set_vcpuonline_done(egc, svos, rc); /* must be last */
-    return AO_INPROGRESS;
-}
-
-static void set_vcpuonline_qmp_cpus_queried(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
-    int i;
-    libxl_bitmap current_map;
-
-    /* Convenience aliases */
-    libxl_bitmap *final_map = &svos->final_map;
-
-    libxl_bitmap_init(&current_map);
-
-    if (rc) goto out;
-
-    libxl_bitmap_alloc(CTX, &current_map, svos->info.vcpu_max_id + 1);
-    rc = qmp_parse_query_cpus(gc, qmp->domid, response, &current_map);
-    if (rc) goto out;
-
-    libxl_bitmap_copy_alloc(CTX, final_map, svos->cpumap);
-
-    libxl_for_each_set_bit(i, current_map) {
-        libxl_bitmap_reset(final_map, i);
-    }
-
-out:
-    libxl_bitmap_dispose(&current_map);
-    svos->index = -1;
-    set_vcpuonline_qmp_add_cpu(egc, qmp, NULL, rc); /* must be last */
-}
-
-static void set_vcpuonline_qmp_add_cpu(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
-{
-    STATE_AO_GC(qmp->ao);
-    set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
-    libxl__json_object *args = NULL;
-
-    /* Convenience aliases */
-    libxl_bitmap *map = &svos->final_map;
-
-    if (rc) goto out;
-
-    while (libxl_bitmap_cpu_valid(map, ++svos->index)) {
-        if (libxl_bitmap_test(map, svos->index)) {
-            qmp->callback = set_vcpuonline_qmp_add_cpu;
-            libxl__qmp_param_add_integer(gc, &args, "id", svos->index);
-            rc = libxl__ev_qmp_send(egc, qmp, "cpu-add", args);
-            if (rc) goto out;
-            return;
-        }
-    }
-
-out:
-    set_vcpuonline_done(egc, svos, rc);
-}
-
-static void set_vcpuonline_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                                   const struct timeval *requested_abs,
-                                   int rc)
-{
-    EGC_GC;
-    set_vcpuonline_state *svos = CONTAINER_OF(ev, *svos, timeout);
-
-    if (rc == ERROR_TIMEDOUT)
-        LOGD(ERROR, svos->qmp.domid,
-             "Setting CPU online in QEMU timed out");
-
-    set_vcpuonline_done(egc, svos, rc);
-}
-
-static void set_vcpuonline_done(libxl__egc *egc,
-                                set_vcpuonline_state *svos,
-                                int rc)
-{
-    STATE_AO_GC(svos->qmp.ao);
-
-    /* Convenience aliases */
-    libxl_domid domid = svos->qmp.domid;
-
-    if (!rc)
-        rc = libxl__set_vcpuonline_xenstore(gc, domid, svos->cpumap,
-                                            &svos->info);
-
-    libxl_bitmap_dispose(&svos->final_map);
-    libxl_dominfo_dispose(&svos->info);
-    libxl__ev_time_deregister(gc, &svos->timeout);
-    libxl__ev_qmp_dispose(gc, &svos->qmp);
-    libxl__ao_complete(egc, ao, rc);
-}
-
-static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
-                                  const libxl__json_object *response,
-                                  int rc);
-
-static void domain_s3_resume(libxl__ao *ao, libxl__egc *egc, int domid)
-{
-    AO_GC;
-    libxl__ev_qmp *qmp;
-    int rc = 0;
-    int r;
-
-    GCNEW(qmp);
-    libxl__ev_qmp_init(qmp);
-    qmp->ao = ao;
-    qmp->domid = domid;
-    qmp->payload_fd = -1;
-    qmp->callback = domain_s3_resume_done;
-
-    switch (libxl__domain_type(gc, domid)) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        switch (libxl__device_model_version_running(gc, domid)) {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            r = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
-            if (r) {
-                LOGED(ERROR, domid, "Send trigger '%s' failed",
-                      libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME));
-                rc = ERROR_FAIL;
-            }
-            break;
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            rc = libxl__ev_qmp_send(egc, qmp, "system_wakeup", NULL);
-            if (rc) goto out;
-            return;
-        default:
-            rc = ERROR_INVAL;
-            break;
-        }
-        break;
-    default:
-        rc = ERROR_INVAL;
-        break;
-    }
-
-out:
-    domain_s3_resume_done(egc, qmp, NULL, rc);
-}
-
-static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
-                                  const libxl__json_object *response,
-                                  int rc)
-{
-    EGC_GC;
-
-    if (rc)
-        LOGD(ERROR, qmp->domid, "Send trigger '%s' failed, rc=%d",
-              libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME), rc);
-
-    libxl__ev_qmp_dispose(gc, qmp);
-    libxl__ao_complete(egc, qmp->ao, rc);
-}
-
-int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
-                       libxl_trigger trigger, uint32_t vcpuid,
-                       const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int rc;
-
-    switch (trigger) {
-    case LIBXL_TRIGGER_POWER:
-        rc = xc_domain_send_trigger(ctx->xch, domid,
-                                    XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
-        break;
-    case LIBXL_TRIGGER_SLEEP:
-        rc = xc_domain_send_trigger(ctx->xch, domid,
-                                    XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
-        break;
-    case LIBXL_TRIGGER_NMI:
-        rc = xc_domain_send_trigger(ctx->xch, domid,
-                                    XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
-        break;
-    case LIBXL_TRIGGER_INIT:
-        rc = xc_domain_send_trigger(ctx->xch, domid,
-                                    XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
-        break;
-    case LIBXL_TRIGGER_RESET:
-        rc = xc_domain_send_trigger(ctx->xch, domid,
-                                    XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
-        break;
-    case LIBXL_TRIGGER_S3RESUME:
-        domain_s3_resume(ao, egc, domid); /* must be last */
-        return AO_INPROGRESS;
-    default:
-        rc = -1;
-        errno = EINVAL;
-        break;
-    }
-
-    if (rc != 0) {
-        LOGED(ERROR, domid, "Send trigger '%s' failed",
-              libxl_trigger_to_string(trigger));
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl__ao_complete(egc, ao, rc);
-    return AO_INPROGRESS;
-out:
-    return AO_CREATE_FAIL(rc);
-}
-
-uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
-{
-    GC_INIT(ctx);
-    char *dompath = libxl__xs_get_dompath(gc, domid);
-    char *vm_path, *start_time;
-    uint32_t ret;
-
-    vm_path = libxl__xs_read(
-        gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
-    start_time = libxl__xs_read(
-        gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
-    if (start_time == NULL) {
-        LOGEVD(ERROR, -1, domid, "Can't get start time of domain");
-        ret = -1;
-    }else{
-        ret = strtoul(start_time, NULL, 10);
-    }
-    GC_FREE;
-    return ret;
-}
-
-static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid,
-                                              unsigned int max_vcpus,
-                                              libxl_bitmap *map)
-{
-    int rc;
-    unsigned int i;
-    const char *dompath;
-
-    dompath = libxl__xs_get_dompath(gc, domid);
-    if (!dompath) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    for (i = 0; i < max_vcpus; i++) {
-        const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i);
-        const char *content;
-        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content);
-        if (rc) goto out;
-        if (content && !strcmp(content, "online"))
-            libxl_bitmap_set(map, i);
-    }
-
-    rc = 0;
-out:
-    return rc;
-}
-
-typedef struct {
-    libxl__ev_qmp qmp;
-    libxl__ev_time timeout;
-    libxl_domain_config *d_config; /* user pointer */
-    libxl__ev_slowlock devlock;
-    libxl_bitmap qemuu_cpus;
-} retrieve_domain_configuration_state;
-
-static void retrieve_domain_configuration_lock_acquired(
-    libxl__egc *egc, libxl__ev_slowlock *, int rc);
-static void retrieve_domain_configuration_cpu_queried(
-    libxl__egc *egc, libxl__ev_qmp *qmp,
-    const libxl__json_object *response, int rc);
-static void retrieve_domain_configuration_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void retrieve_domain_configuration_end(libxl__egc *egc,
-    retrieve_domain_configuration_state *rdcs, int rc);
-
-int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
-                                        libxl_domain_config *d_config,
-                                        const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    retrieve_domain_configuration_state *rdcs;
-
-    GCNEW(rdcs);
-    libxl__ev_qmp_init(&rdcs->qmp);
-    rdcs->qmp.ao = ao;
-    rdcs->qmp.domid = domid;
-    rdcs->qmp.payload_fd = -1;
-    libxl__ev_time_init(&rdcs->timeout);
-    rdcs->d_config = d_config;
-    libxl_bitmap_init(&rdcs->qemuu_cpus);
-    libxl__ev_devlock_init(&rdcs->devlock);
-    rdcs->devlock.ao = ao;
-    rdcs->devlock.domid = domid;
-    rdcs->devlock.callback = retrieve_domain_configuration_lock_acquired;
-    libxl__ev_slowlock_lock(egc, &rdcs->devlock);
-    return AO_INPROGRESS;
-}
-
-static void retrieve_domain_configuration_lock_acquired(
-    libxl__egc *egc, libxl__ev_slowlock *devlock, int rc)
-{
-    retrieve_domain_configuration_state *rdcs =
-        CONTAINER_OF(devlock, *rdcs, devlock);
-    STATE_AO_GC(rdcs->qmp.ao);
-    libxl__flock *lock = NULL;
-    bool has_callback = false;
-
-    /* Convenience aliases */
-    libxl_domid domid = rdcs->qmp.domid;
-    libxl_domain_config *const d_config = rdcs->d_config;
-
-    if (rc) goto out;
-
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    rc = libxl__get_domain_configuration(gc, domid, d_config);
-    if (rc) {
-        LOGD(ERROR, domid, "Fail to get domain configuration");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl__unlock_file(lock);
-    lock = NULL;
-
-    /* We start by querying QEMU, if it is running, for its cpumap as this
-     * is a long operation. */
-    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM &&
-        libxl__device_model_version_running(gc, domid) ==
-            LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
-        /* For QEMU upstream we always need to provide the number
-         * of cpus present to QEMU whether they are online or not;
-         * otherwise QEMU won't accept the saved state.
-         */
-        rc = libxl__ev_time_register_rel(ao, &rdcs->timeout,
-            retrieve_domain_configuration_timeout,
-            LIBXL_QMP_CMD_TIMEOUT * 1000);
-        if (rc) goto out;
-        libxl_bitmap_alloc(CTX, &rdcs->qemuu_cpus,
-                           d_config->b_info.max_vcpus);
-        rdcs->qmp.callback = retrieve_domain_configuration_cpu_queried;
-        rc = libxl__ev_qmp_send(egc, &rdcs->qmp, "query-cpus", NULL);
-        if (rc) goto out;
-        has_callback = true;
-    }
-
-out:
-    if (lock) libxl__unlock_file(lock);
-    if (!has_callback)
-        retrieve_domain_configuration_end(egc, rdcs, rc);
-}
-
-static void retrieve_domain_configuration_cpu_queried(
-    libxl__egc *egc, libxl__ev_qmp *qmp,
-    const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    retrieve_domain_configuration_state *rdcs =
-        CONTAINER_OF(qmp, *rdcs, qmp);
-
-    if (rc) goto out;
-
-    rc = qmp_parse_query_cpus(gc, qmp->domid, response, &rdcs->qemuu_cpus);
-
-out:
-    retrieve_domain_configuration_end(egc, rdcs, rc);
-}
-
-static void retrieve_domain_configuration_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
-    retrieve_domain_configuration_state *rdcs =
-        CONTAINER_OF(ev, *rdcs, timeout);
-
-    retrieve_domain_configuration_end(egc, rdcs, rc);
-}
-
-static void retrieve_domain_configuration_end(libxl__egc *egc,
-    retrieve_domain_configuration_state *rdcs, int rc)
-{
-    STATE_AO_GC(rdcs->qmp.ao);
-    libxl__flock *lock = NULL;
-
-    /* Convenience aliases */
-    libxl_domain_config *const d_config = rdcs->d_config;
-    libxl_domid domid = rdcs->qmp.domid;
-
-    if (rc) goto out;
-
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    /* Domain name */
-    {
-        char *domname;
-        domname = libxl_domid_to_name(CTX, domid);
-        if (!domname) {
-            LOGD(ERROR, domid, "Fail to get domain name");
-            goto out;
-        }
-        free(d_config->c_info.name);
-        d_config->c_info.name = domname; /* steals allocation */
-    }
-
-    /* Domain UUID */
-    {
-        libxl_dominfo info;
-        libxl_dominfo_init(&info);
-        rc = libxl_domain_info(CTX, &info, domid);
-        if (rc) {
-            LOGD(ERROR, domid, "Fail to get domain info");
-            libxl_dominfo_dispose(&info);
-            goto out;
-        }
-        libxl_uuid_copy(CTX, &d_config->c_info.uuid, &info.uuid);
-        libxl_dominfo_dispose(&info);
-    }
-
-    /* VCPUs */
-    {
-        libxl_bitmap *map = &d_config->b_info.avail_vcpus;
-        unsigned int max_vcpus = d_config->b_info.max_vcpus;
-        libxl_device_model_version version;
-
-        libxl_bitmap_dispose(map);
-        libxl_bitmap_init(map);
-        libxl_bitmap_alloc(CTX, map, max_vcpus);
-        libxl_bitmap_set_none(map);
-
-        switch (d_config->b_info.type) {
-        case LIBXL_DOMAIN_TYPE_HVM:
-            version = libxl__device_model_version_running(gc, domid);
-            assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN);
-            switch (version) {
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-                libxl_bitmap_copy(CTX, map, &rdcs->qemuu_cpus);
-                break;
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-                rc = libxl__update_avail_vcpus_xenstore(gc, domid,
-                                                        max_vcpus, map);
-                break;
-            default:
-                abort();
-            }
-            break;
-        case LIBXL_DOMAIN_TYPE_PVH:
-        case LIBXL_DOMAIN_TYPE_PV:
-            rc = libxl__update_avail_vcpus_xenstore(gc, domid,
-                                                    max_vcpus, map);
-            break;
-        default:
-            abort();
-        }
-
-        if (rc) {
-            LOGD(ERROR, domid, "Fail to update available cpu map");
-            goto out;
-        }
-    }
-
-
-    /* Memory limits:
-     *
-     * Currently there are three memory limits:
-     *  1. "target" in xenstore (originally memory= in config file)
-     *  2. "static-max" in xenstore (originally maxmem= in config file)
-     *  3. "max_memkb" in hypervisor
-     *
-     * The third one is not visible and currently managed by
-     * toolstack. In order to rebuild a domain we only need to have
-     * "target" and "static-max".
-     */
-    {
-        uint64_t target_memkb = 0, max_memkb = 0;
-
-        /* "target" */
-        rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
-        if (rc) {
-            LOGD(ERROR, domid, "Fail to get memory target");
-            goto out;
-        }
-
-        /* libxl__get_targetmem_fudge() calculates the difference from
-         * what is in xenstore to what we have in the domain build info.
-         */
-        d_config->b_info.target_memkb = target_memkb +
-            libxl__get_targetmem_fudge(gc, &d_config->b_info);
-
-        d_config->b_info.max_memkb = max_memkb;
-    }
-
-    /* Scheduler params */
-    {
-        libxl_domain_sched_params_dispose(&d_config->b_info.sched_params);
-        rc = libxl_domain_sched_params_get(CTX, domid,
-                                           &d_config->b_info.sched_params);
-        if (rc) {
-            LOGD(ERROR, domid, "Fail to get scheduler parameters");
-            goto out;
-        }
-    }
-
-    /* Devices: disk, nic, vtpm, pcidev etc. */
-
-    /* The MERGE macro implements following logic:
-     * 0. retrieve JSON (done by now)
-     * 1. retrieve list of device from xenstore
-     * 2. use xenstore entries as primary reference and compare JSON
-     *    entries with them.
-     *    a. if a device is present in xenstore and in JSON, merge the
-     *       two views.
-     *    b. if a device is not present in xenstore but in JSON, delete
-     *       it from the result.
-     *    c. it's impossible to have an entry present in xenstore but
-     *       not in JSON, because we maintain an invariant that every
-     *       entry in xenstore must have a corresponding entry in JSON.
-     * 3. "merge" operates on "src" and "dst". "src" points to the
-     *    entry retrieved from xenstore while "dst" points to the entry
-     *    retrieve from JSON.
-     */
-    {
-        const libxl__device_type *dt;
-        int idx;
-
-        for (idx = 0;; idx++) {
-            void *p = NULL;
-            void **devs;
-            int i, j, num;
-            int *num_dev;
-
-            dt = device_type_tbl[idx];
-            if (!dt)
-                break;
-
-            if (!dt->compare)
-                continue;
-
-            num_dev = libxl__device_type_get_num(dt, d_config);
-            p = libxl__device_list(gc, dt, domid, &num);
-            if (p == NULL) {
-                LOGD(DEBUG, domid, "No %s from xenstore",
-                     libxl__device_kind_to_string(dt->type));
-            }
-            devs = libxl__device_type_get_ptr(dt, d_config);
-
-            for (i = 0; i < *num_dev; i++) {
-                void *q;
-
-                q = libxl__device_type_get_elem(dt, d_config, i);
-                for (j = 0; j < num; j++) {
-                    if (dt->compare(p + dt->dev_elem_size * j, q))
-                        break;
-                }
-
-                if (j < num) {         /* found in xenstore */
-                    if (dt->merge)
-                        dt->merge(CTX, p + dt->dev_elem_size * j, q);
-                } else {                /* not found in xenstore */
-                    LOGD(WARN, domid,
-                         "Device present in JSON but not in xenstore, ignored");
-
-                    dt->dispose(q);
-
-                    for (j = i; j < *num_dev - 1; j++)
-                        memcpy(libxl__device_type_get_elem(dt, d_config, j),
-                               libxl__device_type_get_elem(dt, d_config, j+1),
-                               dt->dev_elem_size);
-
-                    /* rewind counters */
-                    (*num_dev)--;
-                    i--;
-
-                    *devs = libxl__realloc(NOGC, *devs,
-                                           dt->dev_elem_size * *num_dev);
-                }
-            }
-
-            for (i = 0; i < num; i++)
-                dt->dispose(p + dt->dev_elem_size * i);
-            free(p);
-        }
-    }
-
-out:
-    libxl__ev_slowlock_unlock(gc, &rdcs->devlock);
-    if (lock) libxl__unlock_file(lock);
-    libxl_bitmap_dispose(&rdcs->qemuu_cpus);
-    libxl__ev_qmp_dispose(gc, &rdcs->qmp);
-    libxl__ev_time_deregister(gc, &rdcs->timeout);
-    libxl__ao_complete(egc, ao, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c
deleted file mode 100644 (file)
index 7c5387e..0000000
+++ /dev/null
@@ -1,2467 +0,0 @@
-/*
- * Copyright (C) 2011      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-/*
- * Internal event machinery for use by other parts of libxl
- */
-
-#include <poll.h>
-
-#include "libxl_internal.h"
-
-
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define LIBXL__DBG_LOG(ctx, args, ...) \
-    LIBXL__LOG((ctx), XTL_DEBUG, args, __VA_ARGS__)
-#else
-# define LIBXL__DBG_LOG(ctx, args, ...) ((void)0)
-#endif
-#define DBG(args, ...) LIBXL__DBG_LOG(CTX, args, __VA_ARGS__)
-
-
-static libxl__ao *ao_nested_root(libxl__ao *ao);
-
-static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao);
-
-
-/*
- * osevent update baton handling
- *
- * We need the following property (the "unstale liveness property"):
- *
- * Whenever any thread is blocking as a result of being given an fd
- * set or timeout by libxl, at least one thread must be using an up to
- * date osevent set.  It is OK for all but one threads to have stale
- * event sets, because so long as one waiting thread has the right
- * event set, any actually interesting event will, if nothing else,
- * wake that "right" thread up.  It will then make some progress
- * and/or, if it exits, ensure that some other thread becomes the
- * "right" thread.
- *
- * For threads blocking outside libxl and which are receiving libxl's
- * fd and timeout information via the libxl_osevent_hooks callbacks,
- * libxl calls this function as soon as it becomes interested.  It is
- * the responsiblity of a provider of these functions in a
- * multithreaded environment to make arrangements to wake up event
- * waiting thread(s) with stale event sets.
- *
- * Waiters outside libxl using _beforepoll are dealt with below.
- *
- * For the libxl event loop, the argument is as follows:
- *
- * The issue we are concerned about is libxl sleeping on an out of
- * date fd set, or too long a timeout, so that it doesn't make
- * progress.  If the property above is satisfied, then if any thread
- * is waiting in libxl at least one such thread will be waiting on a
- * sufficient osevent set, so any relevant osevent will wake up a
- * libxl thread which will either handle the event, or arrange that at
- * least one other libxl thread has the right set.
- *
- * There are two calls to poll in libxl: one is the fd recheck, which
- * is not blocking.  There is only the one blocking call, in
- * eventloop_iteration.  poll runs with the ctx unlocked, so osevents
- * might be added after it unlocks the ctx - that is what we are
- * worried about.
- *
- * To demonstrate that the unstale liveness property is satisfied:
- *
- * We define a baton holder as follows: a libxl thread is a baton
- * holder if
- *   (a) it has an egc or an ao and holds the ctx lock, or
- *   (b) it has an active non-app poller and no osevents have been
- *       added since it released the lock, or
- *   (c) it has an active non-app poller which has been woken
- *       (by writing to its pipe), so it will not sleep
- * We will maintain the invariant (the "baton invariant") that
- * whenever there is any active poller, there is at least
- * one baton holder.  ("non-app" means simply "not poller_app".)
- *
- * No thread outside libxl can have an active non-app poller: pollers
- * are put on the active list by poller_get which is called in three
- * places: libxl_event_wait, which puts it before returning;
- * libxl__ao_create but only in the synchronous case, in which case
- * the poller is put before returning; and the poller_app, during
- * initialisation.
- *
- * So any time when all libxl threads are blocking (and therefore do
- * not have the ctx lock), the non-app active pollers belong to those
- * threads.  If at least one is a baton holder (the invariant), that
- * thread has a good enough event set.
- *
- * Now we will demonstrate that the "baton invariant" is maintained:
- *
- * The rule is that any thread which might be the baton holder is
- * responsible for checking that there continues to be a baton holder
- * as needed.
- *
- * Firstly, consider the case when the baton holders (b) cease to be
- * baton holders because osevents are added.
- *
- * There are only two kinds of osevents: timeouts and fds.  Every
- * other internal event source reduces to one of these eventually.
- * Both of these cases are handled (in the case of fd events, add and
- * modify, separately), calling pollers_note_osevent_added.
- *
- * This walks the poller_active list, marking the active pollers
- * osevents_added=1.  Such a poller cannot be the baton holder.  But
- * pollers_note_osevent_added is called only from ev_* functions,
- * which are only called from event-chain libxl code: ie, code with an
- * ao or an egc.  So at this point we are a baton holder, and there is
- * still a baton holder.
- *
- * Secondly, consider the case where baton holders (a) cease to be
- * batton holders because they dispose of their egc or ao.  We call
- * libxl__egc_ao_cleanup_1_baton on every exit path.  We arrange that
- * everything that disposes of an egc or an ao checks that there is a
- * new baton holder by calling libxl__egc_ao_cleanup_1_baton.
- *
- * This function handles the invariant explicitly: if we have any
- * non-app active pollers it looks for one which is up to date (baton
- * holder category (b)), and failing that it picks a victim to turn
- * into the baton holder category (c) by waking it up.  (Correctness
- * depends on this function not spotting its own thread as the
- * baton-holder, since it is on its way to not being the baton-holder,
- * so it must be called after the poller has been put back.)
- *
- * Thirdly, we must consider the case (c).  A thread in category (c)
- * will reenter libxl when it gains the lock and necessarily then
- * becomes a baton holder in category (a).
- *
- * So the "baton invariant" is maintained.
- * QED (for waiters in libxl).
- *
- *
- * For waiters outside libxl which used libxl_osevent_beforepoll
- * to get the fd set:
- *
- * As above, adding an osevent involves having an egc or an ao.
- * It sets poller->osevents_added on all active pollers.  Notably
- * it sets it on poller_app, which is always active.
- *
- * The thread which does this will dispose of its egc or ao before
- * exiting libxl so it will always wake up the poller_app if the last
- * call to _beforepoll was before the osevents were added.  So the
- * application's fd set contains at least a wakeup in the form of the
- * poller_app fd.  The application cannot sleep on the libxl fd set
- * until it has called _afterpoll which empties the pipe, and it
- * is expected to then call _beforepoll again before sleeping.
- *
- * So all the application's event waiting thread(s) will always have
- * an up to date osevent set, and will be woken up if necessary to
- * achieve this.  (This is in contrast libxl's own event loop where
- * only one thread need be up to date, as discussed above.)
- */
-static void pollers_note_osevent_added(libxl_ctx *ctx) {
-    libxl__poller *poller;
-    LIBXL_LIST_FOREACH(poller, &ctx->pollers_active, active_entry)
-        poller->osevents_added = 1;
-}
-
-static void baton_wake(libxl__gc *gc, libxl__poller *wake)
-{
-    libxl__poller_wakeup(gc, wake);
-
-    wake->osevents_added = 0;
-    /* This serves to make _1_baton idempotent.  It is OK even though
-     * that poller may currently be sleeping on only old osevents,
-     * because it is going to wake up because we've just prodded it,
-     * and it pick up new osevents on its next iteration (or pass
-     * on the baton). */
-}
-
-void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc)
-    /* Any poller we had must have been `put' already. */
-{
-    libxl__poller *search, *wake=0;
-
-    if (CTX->poller_app->osevents_added)
-        baton_wake(gc, CTX->poller_app);
-
-    LIBXL_LIST_FOREACH(search, &CTX->pollers_active, active_entry) {
-        if (search == CTX->poller_app)
-            /* This one is special.  We can't give it the baton. */
-            continue;
-        if (!search->osevents_added)
-            /* This poller is up to date and will wake up as needed. */
-            return;
-        if (!wake)
-            wake = search;
-    }
-
-    if (!wake)
-        /* no-one in libxl waiting for any events */
-        return;
-
-    baton_wake(gc, wake);
-}
-
-/*
- * The counter osevent_in_hook is used to ensure that the application
- * honours the reentrancy restriction documented in libxl_event.h.
- *
- * The application's registration hooks should be called ONLY via
- * these macros, with the ctx locked.  Likewise all the "occurred"
- * entrypoints from the application should assert(!in_hook);
- *
- * During the hook call - including while the arguments are being
- * evaluated - ev->nexus is guaranteed to be valid and refer to the
- * nexus which is being used for this event registration.  The
- * arguments should specify ev->nexus for the for_libxl argument and
- * ev->nexus->for_app_reg (or a pointer to it) for for_app_reg.
- */
-#define OSEVENT_HOOK_INTERN(retval, failedp, evkind, hookop, nexusop, ...) do { \
-    if (CTX->osevent_hooks) {                                           \
-        CTX->osevent_in_hook++;                                         \
-        libxl__osevent_hook_nexi *nexi = &CTX->hook_##evkind##_nexi_idle; \
-        osevent_hook_pre_##nexusop(gc, ev, nexi, &ev->nexus);            \
-        retval CTX->osevent_hooks->evkind##_##hookop                    \
-            (CTX->osevent_user, __VA_ARGS__);                           \
-        if ((failedp))                                                  \
-            osevent_hook_failed_##nexusop(gc, ev, nexi, &ev->nexus);     \
-        CTX->osevent_in_hook--;                                         \
-    }                                                                   \
-} while (0)
-
-#define OSEVENT_HOOK(evkind, hookop, nexusop, ...) ({                   \
-    int osevent_hook_rc = 0;                                    \
-    OSEVENT_HOOK_INTERN(osevent_hook_rc =, !!osevent_hook_rc,   \
-                        evkind, hookop, nexusop, __VA_ARGS__);          \
-    osevent_hook_rc;                                            \
-})
-
-#define OSEVENT_HOOK_VOID(evkind, hookop, nexusop, ...)                         \
-    OSEVENT_HOOK_INTERN(/* void */, 0, evkind, hookop, nexusop, __VA_ARGS__)
-
-/*
- * The application's calls to libxl_osevent_occurred_... may be
- * indefinitely delayed with respect to the rest of the program (since
- * they are not necessarily called with any lock held).  So the
- * for_libxl value we receive may be (almost) arbitrarily old.  All we
- * know is that it came from this ctx.
- *
- * Therefore we may not free the object referred to by any for_libxl
- * value until we free the whole libxl_ctx.  And if we reuse it we
- * must be able to tell when an old use turns up, and discard the
- * stale event.
- *
- * Thus we cannot use the ev directly as the for_libxl value - we need
- * a layer of indirection.
- *
- * We do this by keeping a pool of libxl__osevent_hook_nexus structs,
- * and use pointers to them as for_libxl values.  In fact, there are
- * two pools: one for fds and one for timeouts.  This ensures that we
- * don't risk a type error when we upcast nexus->ev.  In each nexus
- * the ev is either null or points to a valid libxl__ev_time or
- * libxl__ev_fd, as applicable.
- *
- * We /do/ allow ourselves to reassociate an old nexus with a new ev
- * as otherwise we would have to leak nexi.  (This reassociation
- * might, of course, be an old ev being reused for a new purpose so
- * simply comparing the ev pointer is not sufficient.)  Thus the
- * libxl_osevent_occurred functions need to check that the condition
- * allegedly signalled by this event actually exists.
- *
- * The nexi and the lists are all protected by the ctx lock.
- */
-
-struct libxl__osevent_hook_nexus {
-    void *ev;
-    void *for_app_reg;
-    LIBXL_SLIST_ENTRY(libxl__osevent_hook_nexus) next;
-};
-
-static void *osevent_ev_from_hook_nexus(libxl_ctx *ctx,
-           libxl__osevent_hook_nexus *nexus /* pass  void *for_libxl */)
-{
-    return nexus->ev;
-}
-
-static void osevent_release_nexus(libxl__gc *gc,
-                                  libxl__osevent_hook_nexi *nexi_idle,
-                                  libxl__osevent_hook_nexus *nexus)
-{
-    nexus->ev = 0;
-    LIBXL_SLIST_INSERT_HEAD(nexi_idle, nexus, next);
-}
-
-/*----- OSEVENT* hook functions for nexusop "alloc" -----*/
-static void osevent_hook_pre_alloc(libxl__gc *gc, void *ev,
-                                   libxl__osevent_hook_nexi *nexi_idle,
-                                   libxl__osevent_hook_nexus **nexus_r)
-{
-    libxl__osevent_hook_nexus *nexus = LIBXL_SLIST_FIRST(nexi_idle);
-    if (nexus) {
-        LIBXL_SLIST_REMOVE_HEAD(nexi_idle, next);
-    } else {
-        nexus = libxl__zalloc(NOGC, sizeof(*nexus));
-    }
-    nexus->ev = ev;
-    *nexus_r = nexus;
-}
-static void osevent_hook_failed_alloc(libxl__gc *gc, void *ev,
-                                      libxl__osevent_hook_nexi *nexi_idle,
-                                      libxl__osevent_hook_nexus **nexus)
-{
-    osevent_release_nexus(gc, nexi_idle, *nexus);
-}
-
-/*----- OSEVENT* hook functions for nexusop "release" -----*/
-static void osevent_hook_pre_release(libxl__gc *gc, void *ev,
-                                     libxl__osevent_hook_nexi *nexi_idle,
-                                     libxl__osevent_hook_nexus **nexus)
-{
-    osevent_release_nexus(gc, nexi_idle, *nexus);
-}
-static void osevent_hook_failed_release(libxl__gc *gc, void *ev,
-                                        libxl__osevent_hook_nexi *nexi_idle,
-                                        libxl__osevent_hook_nexus **nexus)
-{
-    abort();
-}
-
-/*----- OSEVENT* hook functions for nexusop "noop" -----*/
-static void osevent_hook_pre_noop(libxl__gc *gc, void *ev,
-                                  libxl__osevent_hook_nexi *nexi_idle,
-                                  libxl__osevent_hook_nexus **nexus) { }
-static void osevent_hook_failed_noop(libxl__gc *gc, void *ev,
-                                     libxl__osevent_hook_nexi *nexi_idle,
-                                     libxl__osevent_hook_nexus **nexus) { }
-
-
-/*
- * fd events
- */
-
-int libxl__ev_fd_register(libxl__gc *gc, libxl__ev_fd *ev,
-                          libxl__ev_fd_callback *func,
-                          int fd, short events)
-{
-    int rc;
-
-    assert(fd >= 0);
-
-    CTX_LOCK;
-
-    DBG("ev_fd=%p register fd=%d events=%x", ev, fd, events);
-
-    rc = OSEVENT_HOOK(fd,register, alloc, fd, &ev->nexus->for_app_reg,
-                      events, ev->nexus);
-    if (rc) goto out;
-
-    ev->fd = fd;
-    ev->events = events;
-    ev->func = func;
-
-    LIBXL_LIST_INSERT_HEAD(&CTX->efds, ev, entry);
-    pollers_note_osevent_added(CTX);
-
-    rc = 0;
-
- out:
-    CTX_UNLOCK;
-    return rc;
-}
-
-int libxl__ev_fd_modify(libxl__gc *gc, libxl__ev_fd *ev, short events)
-{
-    int rc;
-
-    CTX_LOCK;
-    assert(libxl__ev_fd_isregistered(ev));
-
-    DBG("ev_fd=%p modify fd=%d events=%x", ev, ev->fd, events);
-
-    rc = OSEVENT_HOOK(fd,modify, noop, ev->fd, &ev->nexus->for_app_reg, events);
-    if (rc) goto out;
-
-    if ((events & ~ev->events))
-        pollers_note_osevent_added(CTX);
-    ev->events = events;
-
-    rc = 0;
- out:
-    CTX_UNLOCK;
-    return rc;
-}
-
-void libxl__ev_fd_deregister(libxl__gc *gc, libxl__ev_fd *ev)
-{
-    CTX_LOCK;
-    libxl__poller *poller;
-
-    if (!libxl__ev_fd_isregistered(ev)) {
-        DBG("ev_fd=%p deregister unregistered",ev);
-        goto out;
-    }
-
-    DBG("ev_fd=%p deregister fd=%d", ev, ev->fd);
-
-    OSEVENT_HOOK_VOID(fd,deregister, release, ev->fd, ev->nexus->for_app_reg);
-    LIBXL_LIST_REMOVE(ev, entry);
-    ev->fd = -1;
-
-    LIBXL_LIST_FOREACH(poller, &CTX->pollers_active, active_entry)
-        poller->fds_deregistered = 1;
-
- out:
-    CTX_UNLOCK;
-}
-
-short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events) {
-    struct pollfd check;
-    int r;
-    EGC_GC;
-
-    for (;;) {
-        check.fd = fd;
-        check.events = events;
-        r = poll(&check, 1, 0);
-        DBG("poll recheck fd=%d r=%d revents=%#x", fd, r, check.revents);
-        if (!r)
-            break;
-        if (r==1)
-            break;
-        assert(r<0);
-        if (errno != EINTR) {
-            LIBXL__EVENT_DISASTER(gc, "failed poll to check for fd", errno, 0);
-            return 0;
-        }
-    }
-    assert(!!r == !!check.revents);
-    return check.revents;
-}
-
-/*
- * timeouts
- */
-
-
-int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r)
-{
-    int rc = gettimeofday(now_r, 0);
-    if (rc) {
-        LOGE(ERROR, "gettimeofday failed");
-        return ERROR_FAIL;
-    }
-    return 0;
-}
-
-static int time_rel_to_abs(libxl__gc *gc, int ms, struct timeval *abs_out)
-{
-    int rc;
-    struct timeval additional = {
-        .tv_sec = ms / 1000,
-        .tv_usec = (ms % 1000) * 1000
-    };
-    struct timeval now;
-
-    rc = libxl__gettimeofday(gc, &now);
-    if (rc) return rc;
-
-    timeradd(&now, &additional, abs_out);
-    return 0;
-}
-
-static int time_register_finite(libxl__gc *gc, libxl__ev_time *ev,
-                                struct timeval absolute)
-{
-    int rc;
-    libxl__ev_time *evsearch;
-
-    rc = OSEVENT_HOOK(timeout,register, alloc, &ev->nexus->for_app_reg,
-                      absolute, ev->nexus);
-    if (rc) return rc;
-
-    ev->infinite = 0;
-    ev->abs = absolute;
-    LIBXL_TAILQ_INSERT_SORTED(&CTX->etimes, entry, ev, evsearch, /*empty*/,
-                              timercmp(&ev->abs, &evsearch->abs, >));
-
-    pollers_note_osevent_added(CTX);
-    return 0;
-}
-
-static void time_deregister(libxl__gc *gc, libxl__ev_time *ev)
-{
-    libxl__ao_abortable_deregister(&ev->abrt);
-
-    if (!ev->infinite) {
-        struct timeval right_away = { 0, 0 };
-        if (ev->nexus) /* only set if app provided hooks */
-            ev->nexus->ev = 0;
-        OSEVENT_HOOK_VOID(timeout,modify,
-                          noop /* release nexus in _occurred_ */,
-                          &ev->nexus->for_app_reg, right_away);
-        LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
-    }
-}
-
-static void time_done_debug(libxl__gc *gc, const char *func,
-                            libxl__ev_time *ev, int rc)
-{
-#ifdef DEBUG
-    libxl__log(CTX, XTL_DEBUG, -1, __FILE__, 0, func, INVALID_DOMID,
-               "ev_time=%p done rc=%d .func=%p infinite=%d abs=%lu.%06lu",
-               ev, rc, ev->func, ev->infinite,
-               (unsigned long)ev->abs.tv_sec, (unsigned long)ev->abs.tv_usec);
-#endif
-}
-
-static void time_aborted(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
-{
-    libxl__ev_time *ev = CONTAINER_OF(abrt, *ev, abrt);
-    EGC_GC;
-
-    time_deregister(gc, ev);
-    DBG("ev_time=%p aborted", ev);
-    ev->func(egc, ev, &ev->abs, rc);
-}
-
-static int time_register_abortable(libxl__ao *ao, libxl__ev_time *ev)
-{
-    ev->abrt.ao = ao;
-    ev->abrt.callback = time_aborted;
-    return libxl__ao_abortable_register(&ev->abrt);
-}
-
-int libxl__ev_time_register_abs(libxl__ao *ao, libxl__ev_time *ev,
-                                libxl__ev_time_callback *func,
-                                struct timeval absolute)
-{
-    AO_GC;
-    int rc;
-
-    CTX_LOCK;
-
-    DBG("ev_time=%p register abs=%lu.%06lu",
-        ev, (unsigned long)absolute.tv_sec, (unsigned long)absolute.tv_usec);
-
-    rc = time_register_abortable(ao, ev);
-    if (rc) goto out;
-
-    rc = time_register_finite(gc, ev, absolute);
-    if (rc) goto out;
-
-    ev->func = func;
-
-    rc = 0;
- out:
-    libxl__ao_abortable_deregister(&ev->abrt);
-    time_done_debug(gc,__func__,ev,rc);
-    CTX_UNLOCK;
-    return rc;
-}
-
-
-int libxl__ev_time_register_rel(libxl__ao *ao, libxl__ev_time *ev,
-                                libxl__ev_time_callback *func,
-                                int milliseconds /* as for poll(2) */)
-{
-    AO_GC;
-    struct timeval absolute;
-    int rc;
-
-    CTX_LOCK;
-
-    DBG("ev_time=%p register ms=%d", ev, milliseconds);
-
-    rc = time_register_abortable(ao, ev);
-    if (rc) goto out;
-
-    if (milliseconds < 0) {
-        ev->infinite = 1;
-    } else {
-        rc = time_rel_to_abs(gc, milliseconds, &absolute);
-        if (rc) goto out;
-
-        rc = time_register_finite(gc, ev, absolute);
-        if (rc) goto out;
-    }
-
-    ev->func = func;
-    rc = 0;
-
- out:
-    if (!libxl__ev_time_isregistered(ev))
-        libxl__ao_abortable_deregister(&ev->abrt);
-    time_done_debug(gc,__func__,ev,rc);
-    CTX_UNLOCK;
-    return rc;
-}
-
-void libxl__ev_time_deregister(libxl__gc *gc, libxl__ev_time *ev)
-{
-    CTX_LOCK;
-
-    DBG("ev_time=%p deregister", ev);
-
-    if (!libxl__ev_time_isregistered(ev))
-        goto out;
-
-    time_deregister(gc, ev);
-    ev->func = 0;
-
- out:
-    time_done_debug(gc,__func__,ev,0);
-    CTX_UNLOCK;
-    return;
-}
-
-static void time_occurs(libxl__egc *egc, libxl__ev_time *etime, int rc)
-{
-    EGC_GC;
-
-    DBG("ev_time=%p occurs abs=%lu.%06lu",
-        etime, (unsigned long)etime->abs.tv_sec,
-        (unsigned long)etime->abs.tv_usec);
-
-    libxl__ev_time_callback *func = etime->func;
-    etime->func = 0;
-    func(egc, etime, &etime->abs, rc);
-}
-
-
-/*
- * xenstore watches
- */
-
-libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum)
-{
-    libxl__ev_watch_slot *slot = &CTX->watch_slots[slotnum];
-    libxl__ev_watch_slot *slotcontents = LIBXL_SLIST_NEXT(slot, empty);
-
-    if (slotcontents == NULL ||
-        ((uintptr_t)slotcontents >= (uintptr_t)CTX->watch_slots &&
-         (uintptr_t)slotcontents < (uintptr_t)(CTX->watch_slots +
-                                               CTX->watch_nslots)))
-        /* An empty slot has either a NULL pointer (end of the
-         * free list), or a pointer to another entry in the array.
-         * So we can do a bounds check to distinguish empty from
-         * full slots.
-         */
-        /* We need to do the comparisons as uintptr_t because
-         * comparing pointers which are not in the same object is
-         * undefined behaviour; if the compiler managed to figure
-         * out that watch_slots[0..watch_nslots-1] is all of the
-         * whole array object it could prove that the above bounds
-         * check was always true if it was legal, and remove it!
-         *
-         * uintptr_t because even on a machine with signed
-         * pointers, objects do not cross zero; whereas on
-         * machines with unsigned pointers, they may cross
-         * 0x8bazillion.
-         */
-        return NULL;
-
-        /* see comment near libxl__ev_watch_slot definition */
-    return (void*)slotcontents;
-}
-
-static void libxl__set_watch_slot_contents(libxl__ev_watch_slot *slot,
-                                           libxl__ev_xswatch *w)
-{
-    /* we look a bit behind the curtain of LIBXL_SLIST, to explicitly
-     * assign to the pointer that's the next link.  See the comment
-     * by the definition of libxl__ev_watch_slot */
-    slot->empty.sle_next = (void*)w;
-}
-
-static void watchfd_callback(libxl__egc *egc, libxl__ev_fd *ev,
-                             int fd, short events, short revents)
-{
-    EGC_GC;
-
-    if (revents & (POLLERR|POLLHUP))
-        LIBXL__EVENT_DISASTER(gc, "unexpected poll event on watch fd", 0, 0);
-
-    for (;;) {
-        char **event = xs_check_watch(CTX->xsh);
-        if (!event) {
-            if (errno == EAGAIN) break;
-            if (errno == EINTR) continue;
-            LIBXL__EVENT_DISASTER(gc, "cannot check/read watches", errno, 0);
-            return;
-        }
-
-        const char *epath = event[0];
-        const char *token = event[1];
-        int slotnum;
-        uint32_t counterval;
-        int rc = sscanf(token, "%d/%"SCNx32, &slotnum, &counterval);
-        if (rc != 2) {
-            LOG(ERROR, "watch epath=%s token=%s: failed to parse token",
-                epath, token);
-            /* oh well */
-            goto ignore;
-        }
-        if (slotnum < 0 || slotnum >= CTX->watch_nslots) {
-            /* perhaps in the future we will make the watchslots array shrink */
-            LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, "watch epath=%s token=%s:"
-                       " slotnum %d out of range [0,%d>",
-                       epath, token, slotnum, CTX->watch_nslots);
-            goto ignore;
-        }
-
-        libxl__ev_xswatch *w = libxl__watch_slot_contents(gc, slotnum);
-
-        if (!w) {
-            LOG(DEBUG, "watch epath=%s token=%s: empty slot", epath, token);
-            goto ignore;
-        }
-
-        if (w->counterval != counterval) {
-            LOG(DEBUG, "watch w=%p epath=%s token=%s: counter != %"PRIx32,
-                w, epath, token, w->counterval);
-            goto ignore;
-        }
-
-        /* Now it's possible, though unlikely, that this was an event
-         * from a previous use of the same slot with the same counterval.
-         *
-         * In that case either:
-         *  - the event path is a child of the watch path, in
-         *    which case this watch would really have generated this
-         *    event if it had been registered soon enough and we are
-         *    OK to give this possibly-spurious event to the caller; or
-         * - it is not, in which case we must suppress it as the
-         *   caller should not see events for unrelated paths.
-         *
-         * See also docs/misc/xenstore.txt.
-         */
-        if (!xs_path_is_subpath(w->path, epath)) {
-            LOG(DEBUG, "watch w=%p wpath=%s token=%s: unexpected epath=%s",
-                w, w->path, token, epath);
-            goto ignore;
-        }
-
-        /* At last, we have checked everything! */
-        LOG(DEBUG, "watch w=%p wpath=%s token=%s: event epath=%s",
-            w, w->path, token, epath);
-        w->callback(egc, w, w->path, epath);
-
-    ignore:
-        free(event);
-    }
-}
-
-static char *watch_token(libxl__gc *gc, int slotnum, uint32_t counterval)
-{
-    return GCSPRINTF("%d/%"PRIx32, slotnum, counterval);
-}
-
-static void watches_check_fd_deregister(libxl__gc *gc)
-{
-    assert(CTX->nwatches>=0);
-    if (!CTX->nwatches)
-        libxl__ev_fd_deregister(gc, &CTX->watch_efd);
-}
-
-int libxl__ev_xswatch_register(libxl__gc *gc, libxl__ev_xswatch *w,
-                               libxl__ev_xswatch_callback *func,
-                               const char *path /* copied */)
-{
-    libxl__ev_watch_slot *use = NULL;
-    char *path_copy = NULL;
-    int rc;
-
-    CTX_LOCK;
-
-    if (!libxl__ev_fd_isregistered(&CTX->watch_efd)) {
-        rc = libxl__ev_fd_register(gc, &CTX->watch_efd, watchfd_callback,
-                                   xs_fileno(CTX->xsh), POLLIN);
-        if (rc) goto out_rc;
-    }
-
-    if (LIBXL_SLIST_EMPTY(&CTX->watch_freeslots)) {
-        /* Free list is empty so there is not in fact a linked
-         * free list in the array and we can safely realloc it */
-        int newarraysize = (CTX->watch_nslots + 1) << 2;
-        int i;
-        libxl__ev_watch_slot *newarray =
-            libxl__realloc(NOGC,
-                           CTX->watch_slots, sizeof(*newarray) * newarraysize);
-        if (!newarray) goto out_nomem;
-        for (i = CTX->watch_nslots; i < newarraysize; i++)
-            LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots,
-                                    &newarray[i], empty);
-        CTX->watch_slots = newarray;
-        CTX->watch_nslots = newarraysize;
-    }
-    use = LIBXL_SLIST_FIRST(&CTX->watch_freeslots);
-    assert(use);
-    LIBXL_SLIST_REMOVE_HEAD(&CTX->watch_freeslots, empty);
-
-    path_copy = strdup(path);
-    if (!path_copy) goto out_nomem;
-
-    int slotnum = use - CTX->watch_slots;
-    w->counterval = CTX->watch_counter++;
-
-    const char *token = watch_token(gc, slotnum, w->counterval);
-    LOG(DEBUG, "watch w=%p wpath=%s token=%s: register slotnum=%d",
-        w, path, token, slotnum);
-
-    if (!xs_watch(CTX->xsh, path, token)) {
-        LOGEV(ERROR, errno, "create watch for path %s", path);
-        rc = ERROR_FAIL;
-        goto out_rc;
-    }
-
-    w->slotnum = slotnum;
-    w->path = path_copy;
-    w->callback = func;
-    CTX->nwatches++;
-    libxl__set_watch_slot_contents(use, w);
-
-    CTX_UNLOCK;
-    return 0;
-
- out_nomem:
-    rc = ERROR_NOMEM;
- out_rc:
-    if (use)
-        LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, use, empty);
-    free(path_copy);
-    watches_check_fd_deregister(gc);
-    CTX_UNLOCK;
-    return rc;
-}
-
-void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch *w)
-{
-    /* it is legal to deregister from within _callback */
-    CTX_LOCK;
-
-    if (w->slotnum >= 0) {
-        const char *token = watch_token(gc, w->slotnum, w->counterval);
-
-        LOG(DEBUG, "watch w=%p wpath=%s token=%s: deregister slotnum=%d",
-            w, w->path, token, w->slotnum);
-
-        if (!xs_unwatch(CTX->xsh, w->path, token))
-            /* Oh well, we will just get watch events forever more
-             * and ignore them.  But we should complain to the log. */
-            LOGEV(ERROR, errno, "remove watch for path %s", w->path);
-
-        libxl__ev_watch_slot *slot = &CTX->watch_slots[w->slotnum];
-        LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, slot, empty);
-        w->slotnum = -1;
-        CTX->nwatches--;
-        watches_check_fd_deregister(gc);
-    } else {
-        LOG(DEBUG, "watch w=%p: deregister unregistered", w);
-    }
-
-    free(w->path);
-    w->path = NULL;
-
-    CTX_UNLOCK;
-}
-
-/*
- * evtchn
- */
-
-static int evtchn_revents_check(libxl__egc *egc, int revents)
-{
-    EGC_GC;
-
-    if (revents & ~POLLIN) {
-        LOG(ERROR, "unexpected poll event on event channel fd: %x", revents);
-        LIBXL__EVENT_DISASTER(gc,
-                   "unexpected poll event on event channel fd", 0, 0);
-        libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
-        return ERROR_FAIL;
-    }
-
-    assert(revents & POLLIN);
-
-    return 0;
-}
-
-static void evtchn_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
-                               int fd, short events, short revents)
-{
-    EGC_GC;
-    libxl__ev_evtchn *evev;
-    int rc;
-    xenevtchn_port_or_error_t port;
-
-    rc = evtchn_revents_check(egc, revents);
-    if (rc) return;
-
-    for (;;) {
-        /* Check the fd again.  The incoming revent may no longer be
-         * true, because the libxl ctx lock has not necessarily been
-         * held continuously since someone noticed the fd.  Normally
-         * this wouldn't be a problem but evtchn devices don't always
-         * honour O_NONBLOCK (see xenctrl.h). */
-        revents = libxl__fd_poll_recheck(egc,fd,POLLIN);
-        if (!revents)
-            break;
-        rc = evtchn_revents_check(egc, revents);
-        if (rc) return;
-
-        /* OK, that's that workaround done.  We can actually check for
-         * work for us to do: */
-
-        port = xenevtchn_pending(CTX->xce);
-        if (port < 0) {
-            if (errno == EAGAIN)
-                break;
-            LIBXL__EVENT_DISASTER(gc,
-     "unexpected failure fetching occurring event port number from evtchn",
-                                  errno, 0);
-            return;
-        }
-
-        LIBXL_LIST_FOREACH(evev, &CTX->evtchns_waiting, entry)
-            if (port == evev->port)
-                goto found;
-        /* not found */
-        DBG("ev_evtchn port=%d no-one cared", port);
-        continue;
-
-    found:
-        DBG("ev_evtchn=%p port=%d signaled", evev, port);
-        evev->waiting = 0;
-        LIBXL_LIST_REMOVE(evev, entry);
-        evev->callback(egc, evev);
-    }
-}
-
-int libxl__ctx_evtchn_init(libxl__gc *gc) {
-    xenevtchn_handle *xce;
-    int rc, fd;
-
-    if (CTX->xce)
-        return 0;
-
-    xce = xenevtchn_open(CTX->lg, 0);
-    if (!xce) {
-        LOGE(ERROR,"cannot open libxc evtchn handle");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    fd = xenevtchn_fd(xce);
-    assert(fd >= 0);
-
-    rc = libxl_fd_set_nonblock(CTX, fd, 1);
-    if (rc) goto out;
-
-    CTX->xce = xce;
-    return 0;
-
- out:
-    xenevtchn_close(xce);
-    return rc;
-}
-
-static void evtchn_check_fd_deregister(libxl__gc *gc)
-{
-    if (CTX->xce && LIBXL_LIST_EMPTY(&CTX->evtchns_waiting))
-        libxl__ev_fd_deregister(gc, &CTX->evtchn_efd);
-}
-
-int libxl__ev_evtchn_wait(libxl__gc *gc, libxl__ev_evtchn *evev)
-{
-    int r, rc;
-
-    DBG("ev_evtchn=%p port=%d wait (was waiting=%d)",
-        evev, evev->port, evev->waiting);
-
-    rc = libxl__ctx_evtchn_init(gc);
-    if (rc) goto out;
-
-    if (!libxl__ev_fd_isregistered(&CTX->evtchn_efd)) {
-        rc = libxl__ev_fd_register(gc, &CTX->evtchn_efd, evtchn_fd_callback,
-                                   xenevtchn_fd(CTX->xce), POLLIN);
-        if (rc) goto out;
-    }
-
-    if (evev->waiting)
-        return 0;
-
-    r = xenevtchn_unmask(CTX->xce, evev->port);
-    if (r) {
-        LOGE(ERROR,"cannot unmask event channel %d",evev->port);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    evev->waiting = 1;
-    LIBXL_LIST_INSERT_HEAD(&CTX->evtchns_waiting, evev, entry);
-    return 0;
-
- out:
-    evtchn_check_fd_deregister(gc);
-    return rc;
-}
-
-void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev)
-{
-    DBG("ev_evtchn=%p port=%d cancel (was waiting=%d)",
-        evev, evev->port, evev->waiting);
-
-    if (!evev->waiting)
-        return;
-
-    evev->waiting = 0;
-    LIBXL_LIST_REMOVE(evev, entry);
-    evtchn_check_fd_deregister(gc);
-}
-
-/*
- * waiting for device state
- */
-
-static void devstate_callback(libxl__egc *egc, libxl__xswait_state *xsw,
-                              int rc, const char *sstate)
-{
-    EGC_GC;
-    libxl__ev_devstate *ds = CONTAINER_OF(xsw, *ds, w);
-
-    if (rc) {
-        if (rc == ERROR_TIMEDOUT)
-            LOG(DEBUG, "backend %s wanted state %d "" timed out", ds->w.path,
-                ds->wanted);
-        goto out;
-    }
-    if (!sstate) {
-        LOG(DEBUG, "backend %s wanted state %d"" but it was removed",
-            ds->w.path, ds->wanted);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    int got = atoi(sstate);
-    if (got == ds->wanted) {
-        LOG(DEBUG, "backend %s wanted state %d ok", ds->w.path, ds->wanted);
-        rc = 0;
-    } else {
-        LOG(DEBUG, "backend %s wanted state %d"" still waiting state %d",
-            ds->w.path, ds->wanted, got);
-        return;
-    }
-
- out:
-    libxl__ev_devstate_cancel(gc, ds);
-    ds->callback(egc, ds, rc);
-}
-
-int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
-                            libxl__ev_devstate_callback cb,
-                            const char *state_path, int state, int milliseconds)
-{
-    AO_GC;
-    int rc;
-
-    libxl__xswait_init(&ds->w);
-    ds->wanted = state;
-    ds->callback = cb;
-
-    ds->w.ao = ao;
-    ds->w.what = GCSPRINTF("backend %s (hoping for state change to %d)",
-                           state_path, state);
-    ds->w.path = state_path;
-    ds->w.timeout_ms = milliseconds;
-    ds->w.callback = devstate_callback;
-    rc = libxl__xswait_start(gc, &ds->w);
-    if (rc) goto out;
-
-    return 0;
-
- out:
-    libxl__ev_devstate_cancel(gc, ds);
-    return rc;
-}
-
-/*
- * immediate non-reentrant callback
- */
-
-void libxl__ev_immediate_register(libxl__egc *egc, libxl__ev_immediate *ei)
-{
-    LIBXL_STAILQ_INSERT_TAIL(&egc->ev_immediates, ei, entry);
-}
-
-/*
- * domain death/destruction
- */
-
-/*
- * We use a xenstore watch on the domain's path, rather than using an
- * @releaseDomain watch and asking the hypervisor.  This is simpler
- * because turning @releaseDomain into domain-specific information is
- * complicated.
- *
- * It is also sufficient for our callers, which are generally trying
- * to do cleanup of their own execution state on domain death, for the
- * following reason: if the domain is destroyed then either (a) the
- * entries in xenstore have already been deleted, in which case the
- * test here works or (b) they have not in which case something has
- * gone very badly wrong and we are going to leak those xenstore
- * entries, in which case trying to avoid leaking other stuff is
- * futile.
- */
-
-void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc)
-{
-    libxl__ao_abortable_init(&dc->abrt);
-    libxl__ev_xswatch_init(&dc->watch);
-}
-
-void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc)
-{
-    libxl__ao_abortable_deregister(&dc->abrt);
-    libxl__ev_xswatch_deregister(gc,&dc->watch);
-}
-
-static void domaindeathcheck_callback(libxl__egc *egc, libxl__ev_xswatch *w,
-                            const char *watch_path, const char *event_path)
-{
-    libxl__domaindeathcheck *dc = CONTAINER_OF(w, *dc, watch);
-    EGC_GC;
-    const char *p = libxl__xs_read(gc, XBT_NULL, watch_path);
-    if (p) return;
-
-    libxl__domaindeathcheck_stop(gc,dc);
-
-    if (errno!=ENOENT) {
-        LIBXL__EVENT_DISASTER(gc,"failed to read xenstore"
-                              " for domain detach check", errno, 0);
-        return;
-    }
-
-    LOG(ERROR,"%s: domain %"PRIu32" removed (%s no longer in xenstore)",
-        dc->what, dc->domid, watch_path);
-    dc->callback(egc, dc, ERROR_DOMAIN_DESTROYED);
-}
-
-static void domaindeathcheck_abort(libxl__egc *egc,
-                                   libxl__ao_abortable *abrt,
-                                   int rc)
-{
-    libxl__domaindeathcheck *dc = CONTAINER_OF(abrt, *dc, abrt);
-    EGC_GC;
-
-    libxl__domaindeathcheck_stop(gc,dc);
-    dc->callback(egc, dc, rc);
-}
-
-int libxl__domaindeathcheck_start(libxl__ao *ao,
-                                  libxl__domaindeathcheck *dc)
-{
-    AO_GC;
-    int rc;
-    const char *path = GCSPRINTF("/local/domain/%"PRIu32, dc->domid);
-
-    libxl__domaindeathcheck_init(dc);
-
-    dc->abrt.ao = ao;
-    dc->abrt.callback = domaindeathcheck_abort;
-    rc = libxl__ao_abortable_register(&dc->abrt);
-    if (rc) goto out;
-
-    rc = libxl__ev_xswatch_register(gc, &dc->watch,
-                                    domaindeathcheck_callback, path);
-    if (rc) goto out;
-
-    return 0;
-
- out:
-    libxl__domaindeathcheck_stop(gc,dc);
-    return rc;
-}
-
-/*
- * osevent poll
- */
-
-static int beforepoll_internal(libxl__gc *gc, libxl__poller *poller,
-                               int *nfds_io, struct pollfd *fds,
-                               int *timeout_upd, struct timeval now)
-{
-    libxl__ev_fd *efd;
-    int rc;
-
-    /*
-     * We need to look at the fds we want twice: firstly, to count
-     * them so we can make the rindex array big enough, and secondly
-     * to actually fill the arrays in.
-     *
-     * To ensure correctness and avoid repeating the logic for
-     * deciding which fds are relevant, we define a macro
-     *    REQUIRE_FDS( BODY )
-     * which calls
-     *    do{
-     *        int req_fd;
-     *        int req_events;
-     *        BODY;
-     *    }while(0)
-     * for each fd with a nonzero events.  This is invoked twice.
-     *
-     * The definition of REQUIRE_FDS is simplified with the helper
-     * macro
-     *    void REQUIRE_FD(int req_fd, int req_events, BODY);
-     */
-
-#define REQUIRE_FDS(BODY) do{                                          \
-                                                                       \
-        LIBXL_LIST_FOREACH(efd, &CTX->efds, entry)                     \
-            REQUIRE_FD(efd->fd, efd->events, BODY);                    \
-                                                                       \
-        REQUIRE_FD(poller->wakeup_pipe[0], POLLIN, BODY);              \
-                                                                       \
-    }while(0)
-
-#define REQUIRE_FD(req_fd_, req_events_, BODY) do{      \
-        int req_events = (req_events_);                 \
-        int req_fd = (req_fd_);                         \
-        if (req_events) {                               \
-            BODY;                                       \
-        }                                               \
-    }while(0)
-
-
-    /*
-     * In order to be able to efficiently find the libxl__ev_fd for a
-     * struct poll during _afterpoll, we maintain a shadow data
-     * structure in CTX->fd_rindices: each fd corresponds to a slot in
-     * fd_rindices, and each element in the rindices is three indices
-     * into the fd array (for POLLIN, POLLPRI and POLLOUT).
-     */
-
-    if (*nfds_io) {
-        /*
-         * As an optimisation, we don't touch fd_rindex
-         * if *nfds_io is zero on entry, since in that case the
-         * caller just wanted to know how big an array to give us.
-         *
-         * If !*nfds_io, the unconditional parts below are guaranteed
-         * not to mess with fd_rindex.
-         */
-
-        int maxfd = 0;
-
-        REQUIRE_FDS({
-            if (req_fd >= maxfd)
-                maxfd = req_fd + 1;
-        });
-
-        /* make sure our array is as big as *nfds_io */
-        if (poller->fd_rindices_allocd < maxfd) {
-            assert(ARRAY_SIZE_OK(poller->fd_rindices, maxfd));
-            poller->fd_rindices =
-                libxl__realloc(NOGC, poller->fd_rindices,
-                               maxfd * sizeof(*poller->fd_rindices));
-            memset(poller->fd_rindices + poller->fd_rindices_allocd,
-                   0,
-                   (maxfd - poller->fd_rindices_allocd)
-                     * sizeof(*poller->fd_rindices));
-            poller->fd_rindices_allocd = maxfd;
-        }
-    }
-
-    int used = 0;
-
-    REQUIRE_FDS({
-        if (used < *nfds_io) {
-            fds[used].fd = req_fd;
-            fds[used].events = req_events;
-            fds[used].revents = 0;
-            assert(req_fd < poller->fd_rindices_allocd);
-            if (req_events & POLLIN)  poller->fd_rindices[req_fd][0] = used;
-            if (req_events & POLLPRI) poller->fd_rindices[req_fd][1] = used;
-            if (req_events & POLLOUT) poller->fd_rindices[req_fd][2] = used;
-        }
-        used++;
-    });
-
-    rc = used <= *nfds_io ? 0 : ERROR_BUFFERFULL;
-
-    *nfds_io = used;
-
-    poller->fds_deregistered = 0;
-    poller->osevents_added = 0;
-
-    libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
-    if (etime) {
-        int our_timeout;
-        struct timeval rel;
-        static struct timeval zero;
-
-        timersub(&etime->abs, &now, &rel);
-
-        if (timercmp(&rel, &zero, <)) {
-            our_timeout = 0;
-        } else if (rel.tv_sec >= 2000000) {
-            our_timeout = 2000000000;
-        } else {
-            our_timeout = rel.tv_sec * 1000 + (rel.tv_usec + 999) / 1000;
-        }
-        if (*timeout_upd < 0 || our_timeout < *timeout_upd)
-            *timeout_upd = our_timeout;
-    }
-
-    return rc;
-}
-
-int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
-                             struct pollfd *fds, int *timeout_upd,
-                             struct timeval now)
-{
-    EGC_INIT(ctx);
-    CTX_LOCK;
-    int rc = beforepoll_internal(gc, ctx->poller_app,
-                                 nfds_io, fds, timeout_upd, now);
-    CTX_UNLOCK_EGC_FREE;
-    return rc;
-}
-
-static int afterpoll_check_fd(libxl__poller *poller,
-                              const struct pollfd *fds, int nfds,
-                              int fd, int events)
-    /* Returns mask of events which were requested and occurred.  Will
-     * return nonzero only once for each (poller,fd,events)
-     * combination, until the next beforepoll.  If events from
-     * different combinations overlap, between one such combination
-     * and all distinct combinations will produce nonzero returns. */
-{
-    if (fd >= poller->fd_rindices_allocd)
-        /* added after we went into poll, have to try again */
-        return 0;
-
-    events |= POLLERR | POLLHUP;
-
-    int i, revents = 0;
-    for (i=0; i<3; i++) {
-        int *slotp = &poller->fd_rindices[fd][i];
-        int slot = *slotp;
-
-        if (slot >= nfds)
-            /* stale slot entry (again, added afterwards), */
-            /* or slot for which we have already returned nonzero */
-            continue;
-
-        if (fds[slot].fd != fd)
-            /* again, stale slot entry */
-            continue;
-
-        assert(poller->fds_deregistered || !(fds[slot].revents & POLLNVAL));
-
-        /* we mask in case requested events have changed */
-        int slot_revents = fds[slot].revents & events;
-        if (!slot_revents)
-            /* this slot is for a different set of events */
-            continue;
-
-        revents |= slot_revents;
-        *slotp = INT_MAX; /* so that next time we'll see slot >= nfds */
-    }
-
-    return revents;
-}
-
-static void fd_occurs(libxl__egc *egc, libxl__ev_fd *efd, short revents_ign)
-{
-    short revents_current = libxl__fd_poll_recheck(egc, efd->fd, efd->events);
-    EGC_GC;
-
-    DBG("ev_fd=%p occurs fd=%d events=%x revents_ign=%x revents_current=%x",
-        efd, efd->fd, efd->events, revents_ign, revents_current);
-
-    if (revents_current)
-        efd->func(egc, efd, efd->fd, efd->events, revents_current);
-}
-
-static void afterpoll_internal(libxl__egc *egc, libxl__poller *poller,
-                               int nfds, const struct pollfd *fds,
-                               struct timeval now)
-{
-    /* May make callbacks into the application for child processes.
-     * ctx must be locked exactly once */
-    EGC_GC;
-    libxl__ev_fd *efd;
-
-    /*
-     * Warning! Reentrancy hazards!
-     *
-     * Many parts of this function eventually call arbitrary callback
-     * functions which may modify the event handling data structures.
-     *
-     * Of the data structures used here:
-     *
-     *   egc, poller, now
-     *                are allocated by our caller and relate to the
-     *                current thread and its call stack down into the
-     *                event machinery; it is not freed until we return.
-     *                So it is safe.
-     *
-     *   fds          is either what application passed into
-     *                libxl_osevent_afterpoll (which, although this
-     *                isn't explicitly stated, clearly must remain
-     *                valid until libxl_osevent_afterpoll returns) or
-     *                it's poller->fd_polls which is modified only by
-     *                our (non-recursive) caller eventloop_iteration.
-     *
-     *   CTX          comes from our caller, and applications are
-     *                forbidden from destroying it while we are running.
-     *                So the ctx pointer itself is safe to use; now
-     *                for its contents:
-     *
-     *   CTX->etimes  is used in a simple reentrancy-safe manner.
-     *
-     *   CTX->efds    is more complicated; see below.
-     */
-
-    for (;;) {
-        /* We restart our scan of fd events whenever we call a
-         * callback function.  This is necessary because such
-         * a callback might make arbitrary changes to CTX->efds.
-         * We invalidate the fd_rindices[] entries which were used
-         * so that we don't call the same function again. */
-        int revents;
-
-        LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) {
-
-            if (!efd->events)
-                continue;
-
-            revents = afterpoll_check_fd(poller,fds,nfds,
-                                         efd->fd,efd->events);
-            if (revents)
-                goto found_fd_event;
-        }
-        /* no ordinary fd events, then */
-        break;
-
-    found_fd_event:
-        fd_occurs(egc, efd, revents);
-    }
-
-    for (;;) {
-        libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes);
-        if (!etime)
-            break;
-
-        assert(!etime->infinite);
-
-        if (timercmp(&etime->abs, &now, >))
-            break;
-
-        time_deregister(gc, etime);
-
-        time_occurs(egc, etime, ERROR_TIMEDOUT);
-    }
-
-    if (afterpoll_check_fd(poller,fds,nfds, poller->wakeup_pipe[0],POLLIN)) {
-        poller->pipe_nonempty = 0;
-        int e = libxl__self_pipe_eatall(poller->wakeup_pipe[0]);
-        if (e) LIBXL__EVENT_DISASTER(gc, "read wakeup", e, 0);
-    }
-}
-
-void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
-                             struct timeval now)
-{
-    EGC_INIT(ctx);
-    CTX_LOCK;
-    afterpoll_internal(egc, ctx->poller_app, nfds, fds, now);
-    CTX_UNLOCK_EGC_FREE;
-}
-
-/*
- * osevent hook and callback machinery
- */
-
-void libxl_osevent_register_hooks(libxl_ctx *ctx,
-                                  const libxl_osevent_hooks *hooks,
-                                  void *user)
-{
-    GC_INIT(ctx);
-    CTX_LOCK;
-    assert(LIBXL_LIST_EMPTY(&ctx->efds));
-    assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
-    ctx->osevent_hooks = hooks;
-    ctx->osevent_user = user;
-    CTX_UNLOCK;
-    GC_FREE;
-}
-
-
-void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
-                               int fd, short events_ign, short revents_ign)
-{
-    EGC_INIT(ctx);
-    CTX_LOCK;
-    assert(!CTX->osevent_in_hook);
-
-    libxl__ev_fd *ev = osevent_ev_from_hook_nexus(ctx, for_libxl);
-    if (!ev) goto out;
-    if (ev->fd != fd) goto out;
-
-    fd_occurs(egc, ev, revents_ign);
-
- out:
-    CTX_UNLOCK_EGC_FREE;
-}
-
-void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
-{
-    EGC_INIT(ctx);
-    CTX_LOCK;
-    assert(!CTX->osevent_in_hook);
-
-    libxl__osevent_hook_nexus *nexus = for_libxl;
-    libxl__ev_time *ev = osevent_ev_from_hook_nexus(ctx, nexus);
-
-    osevent_release_nexus(gc, &CTX->hook_timeout_nexi_idle, nexus);
-
-    if (!ev) goto out;
-    assert(!ev->infinite);
-
-    LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry);
-
-    time_occurs(egc, ev, ERROR_TIMEDOUT);
-
- out:
-    CTX_UNLOCK_EGC_FREE;
-}
-
-void libxl__event_disaster(libxl__gc *gc, const char *msg, int errnoval,
-                           libxl_event_type type /* may be 0 */,
-                           const char *file, int line, const char *func)
-{
-    libxl__log(CTX, XTL_CRITICAL, errnoval, file, line, func, INVALID_DOMID,
-               "DISASTER in event loop: %s%s%s%s",
-               msg,
-               type ? " (relates to event type " : "",
-               type ? libxl_event_type_to_string(type) : "",
-               type ? ")" : "");
-
-    if (CTX->event_hooks && CTX->event_hooks->disaster) {
-        CTX->event_hooks->disaster(CTX->event_hooks_user, type, msg, errnoval);
-        return;
-    }
-
-    const char verybad[] =
-        "DISASTER in event loop not handled by libxl application";
-    LIBXL__LOG(CTX, XTL_CRITICAL, verybad);
-    fprintf(stderr, "libxl: fatal error, exiting program: %s\n", verybad);
-    exit(-1);
-}
-
-static void egc_run_callbacks(libxl__egc *egc)
-{
-    /*
-     * The callbacks must happen with the ctx unlocked.  See the
-     * comment near #define EGC_GC in libxl_internal.h and those in
-     * the definitions of libxl__egc, libxl__ao and libxl__aop.
-     */
-    EGC_GC;
-    libxl_event *ev, *ev_tmp;
-    libxl__aop_occurred *aop, *aop_tmp;
-    libxl__ev_immediate *ei;
-
-    while (!LIBXL_STAILQ_EMPTY(&egc->ev_immediates)) {
-        ei = LIBXL_STAILQ_FIRST(&egc->ev_immediates);
-        LIBXL_STAILQ_REMOVE_HEAD(&egc->ev_immediates, entry);
-        CTX_LOCK;
-        /* This callback is internal to libxl and expects CTX to be
-         * locked. */
-        ei->callback(egc, ei);
-        CTX_UNLOCK;
-    }
-
-    LIBXL_TAILQ_FOREACH_SAFE(ev, &egc->occurred_for_callback, link, ev_tmp) {
-        LIBXL_TAILQ_REMOVE(&egc->occurred_for_callback, ev, link);
-        LOG(DEBUG,"event %p callback type=%s",
-            ev, libxl_event_type_to_string(ev->type));
-        CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev);
-    }
-
-    LIBXL_TAILQ_FOREACH_SAFE(aop, &egc->aops_for_callback, entry, aop_tmp) {
-        LIBXL_TAILQ_REMOVE(&egc->aops_for_callback, aop, entry);
-        LOG(DEBUG,"ao %p: progress report: callback aop=%p", aop->ao, aop);
-        aop->how->callback(CTX, aop->ev, aop->how->for_callback);
-
-        CTX_LOCK;
-        assert(aop->ao->magic == LIBXL__AO_MAGIC);
-        aop->ao->progress_reports_outstanding--;
-        libxl__ao_complete_check_progress_reports(egc, aop->ao);
-        CTX_UNLOCK;
-    }
-
-    libxl__ao *ao, *ao_tmp;
-    LIBXL_TAILQ_FOREACH_SAFE(ao, &egc->aos_for_callback,
-                             entry_for_callback, ao_tmp) {
-        LIBXL_TAILQ_REMOVE(&egc->aos_for_callback, ao, entry_for_callback);
-        LOG(DEBUG,"ao %p: completion callback", ao);
-        ao->how.callback(CTX, ao->rc, ao->how.u.for_callback);
-        CTX_LOCK;
-        ao->notified = 1;
-        ao__check_destroy(CTX, ao);
-        CTX_UNLOCK;
-    }
-}
-
-void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc)
-{
-    EGC_GC;
-    egc_run_callbacks(egc);
-
-    libxl__free_all(gc);
-}
-
-/*
- * Event retrieval etc.
- */
-
-void libxl_event_register_callbacks(libxl_ctx *ctx,
-                  const libxl_event_hooks *hooks, void *user)
-{
-    ctx->event_hooks = hooks;
-    ctx->event_hooks_user = user;
-}
-
-void libxl__event_occurred(libxl__egc *egc, libxl_event *event)
-{
-    EGC_GC;
-
-    if (CTX->event_hooks &&
-        (CTX->event_hooks->event_occurs_mask & (1UL << event->type))) {
-        /* libxl__egc_cleanup will call the callback, just before exit
-         * from libxl.  This helps avoid reentrancy bugs: parts of
-         * libxl that call libxl__event_occurred do not have to worry
-         * that libxl might be reentered at that point. */
-        LIBXL_TAILQ_INSERT_TAIL(&egc->occurred_for_callback, event, link);
-        return;
-    } else {
-        libxl__poller *poller;
-        LIBXL_TAILQ_INSERT_TAIL(&CTX->occurred, event, link);
-        LIBXL_LIST_FOREACH(poller, &CTX->pollers_event, entry)
-            libxl__poller_wakeup(gc, poller);
-    }
-}
-
-void libxl_event_free(libxl_ctx *ctx, libxl_event *event)
-{
-    /* Exceptionally, this function may be called from libxl, with ctx==0 */
-    libxl_event_dispose(event);
-    free(event);
-}
-
-libxl_event *libxl__event_new(libxl__egc *egc,
-                              libxl_event_type type, uint32_t domid,
-                              libxl_ev_user for_user)
-{
-    EGC_GC;
-    libxl_event *ev;
-
-    ev = libxl__zalloc(NOGC,sizeof(*ev));
-
-    libxl_event_init(ev);
-    libxl_event_init_type(ev, type);
-
-    ev->domid = domid;
-    ev->for_user = for_user;
-
-    return ev;
-}
-
-static int event_check_internal(libxl__egc *egc, libxl_event **event_r,
-                                unsigned long typemask,
-                                libxl_event_predicate *pred, void *pred_user)
-{
-    EGC_GC;
-    libxl_event *ev;
-    int rc;
-
-    LIBXL_TAILQ_FOREACH(ev, &CTX->occurred, link) {
-        if (!(typemask & ((uint64_t)1 << ev->type)))
-            continue;
-
-        if (pred && !pred(ev, pred_user))
-            continue;
-
-        /* got one! */
-        LIBXL_TAILQ_REMOVE(&CTX->occurred, ev, link);
-        *event_r = ev;
-        rc = 0;
-        goto out;
-    }
-    rc = ERROR_NOT_READY;
-
- out:
-    return rc;
-}
-
-int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
-                      uint64_t typemask,
-                      libxl_event_predicate *pred, void *pred_user)
-{
-    EGC_INIT(ctx);
-    CTX_LOCK;
-    int rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
-    CTX_UNLOCK_EGC_FREE;
-    return rc;
-}
-
-/*
- * Utilities for pipes (specifically, useful for self-pipes)
- */
-
-void libxl__pipe_close(int fds[2])
-{
-    if (fds[0] >= 0) close(fds[0]);
-    if (fds[1] >= 0) close(fds[1]);
-    fds[0] = fds[1] = -1;
-}
-
-int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2])
-{
-    int r, rc;
-
-    r = libxl_pipe(ctx, fds);
-    if (r) {
-        fds[0] = fds[1] = -1;
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl_fd_set_nonblock(ctx, fds[0], 1);
-    if (rc) goto out;
-
-    rc = libxl_fd_set_nonblock(ctx, fds[1], 1);
-    if (rc) goto out;
-
-    return 0;
-
- out:
-    libxl__pipe_close(fds);
-    return rc;
-}
-
-int libxl__self_pipe_wakeup(int fd)
-{
-    /* Called from signal handlers, so needs to be async-signal-safe */
-    static const char buf[1] = "";
-
-    for (;;) {
-        int r = write(fd, buf, 1);
-        if (r==1) return 0;
-        assert(r==-1);
-        if (errno == EINTR) continue;
-        if (errno == EWOULDBLOCK) return 0;
-        if (!errno) abort();
-        return errno;
-    }
-}
-
-int libxl__self_pipe_eatall(int fd)
-{
-    char buf[256];
-    for (;;) {
-        int r = read(fd, buf, sizeof(buf));
-        if (r == sizeof(buf)) continue;
-        if (r >= 0) return 0;
-        assert(r == -1);
-        if (errno == EINTR) continue;
-        if (errno == EWOULDBLOCK) return 0;
-        assert(errno);
-        return errno;
-    }
-}
-
-/*
- * Manipulation of pollers
- */
-
-int libxl__poller_init(libxl__gc *gc, libxl__poller *p)
-{
-    int rc;
-    p->fd_polls = 0;
-    p->fd_rindices = 0;
-    p->fds_deregistered = 0;
-
-    rc = libxl__pipe_nonblock(CTX, p->wakeup_pipe);
-    if (rc) goto out;
-
-    return 0;
-
- out:
-    libxl__poller_dispose(p);
-    return rc;
-}
-
-void libxl__poller_dispose(libxl__poller *p)
-{
-    libxl__pipe_close(p->wakeup_pipe);
-    free(p->fd_polls);
-    free(p->fd_rindices);
-}
-
-libxl__poller *libxl__poller_get(libxl__gc *gc)
-{
-    /* must be called with ctx locked */
-    int rc;
-
-    libxl__poller *p = LIBXL_LIST_FIRST(&CTX->pollers_idle);
-    if (p) {
-        LIBXL_LIST_REMOVE(p, entry);
-    } else {
-        p = libxl__zalloc(NOGC, sizeof(*p));
-
-        rc = libxl__poller_init(gc, p);
-        if (rc) {
-            free(p);
-            return NULL;
-        }
-    }
-
-    LIBXL_LIST_INSERT_HEAD(&CTX->pollers_active, p,
-                           active_entry);
-    return p;
-}
-
-void libxl__poller_put(libxl_ctx *ctx, libxl__poller *p)
-{
-    if (!p) return;
-    LIBXL_LIST_REMOVE(p, active_entry);
-    LIBXL_LIST_INSERT_HEAD(&ctx->pollers_idle, p, entry);
-}
-
-void libxl__poller_wakeup(libxl__gc *gc, libxl__poller *p)
-{
-    if (p->pipe_nonempty) return;
-    p->pipe_nonempty = 1;
-    int e = libxl__self_pipe_wakeup(p->wakeup_pipe[1]);
-    if (e) LIBXL__EVENT_DISASTER(gc, "cannot poke watch pipe", e, 0);
-}
-
-/*
- * Main event loop iteration
- */
-
-static int eventloop_iteration(libxl__egc *egc, libxl__poller *poller) {
-    /* The CTX must be locked EXACTLY ONCE so that this function
-     * can unlock it when it polls.
-     */
-    EGC_GC;
-    int rc, nfds;
-    struct timeval now;
-    
-    rc = libxl__gettimeofday(gc, &now);
-    if (rc) goto out;
-
-    int timeout;
-
-    for (;;) {
-        nfds = poller->fd_polls_allocd;
-        timeout = -1;
-        rc = beforepoll_internal(gc, poller, &nfds, poller->fd_polls,
-                                 &timeout, now);
-        if (!rc) break;
-        if (rc != ERROR_BUFFERFULL) goto out;
-
-        struct pollfd *newarray =
-            (nfds > INT_MAX / sizeof(struct pollfd) / 2) ? 0 :
-            libxl__realloc(NOGC, poller->fd_polls, sizeof(*newarray) * nfds);
-
-        if (!newarray) { rc = ERROR_NOMEM; goto out; }
-
-        poller->fd_polls = newarray;
-        poller->fd_polls_allocd = nfds;
-    }
-
-    CTX_UNLOCK;
-    rc = poll(poller->fd_polls, nfds, timeout);
-    CTX_LOCK;
-
-    if (rc < 0) {
-        if (errno == EINTR)
-            return 0; /* will go round again if caller requires */
-
-        LOGEV(ERROR, errno, "poll failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__gettimeofday(gc, &now);
-    if (rc) goto out;
-
-    afterpoll_internal(egc, poller, nfds, poller->fd_polls, now);
-
-    rc = 0;
- out:
-    return rc;
-}
-
-int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
-                     uint64_t typemask,
-                     libxl_event_predicate *pred, void *pred_user)
-{
-    int rc;
-    libxl__poller *poller = NULL;
-
-    EGC_INIT(ctx);
-    CTX_LOCK;
-
-    poller = libxl__poller_get(gc);
-    if (!poller) { rc = ERROR_FAIL; goto out; }
-
-    for (;;) {
-        rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
-        if (rc != ERROR_NOT_READY) goto out;
-
-        rc = eventloop_iteration(egc, poller);
-        if (rc) goto out;
-
-        /* we unlock and cleanup the egc each time we go through this
-         * loop, so that (a) we don't accumulate garbage and (b) any
-         * events which are to be dispatched by callback are actually
-         * delivered in a timely fashion.  _1_baton will be
-         * called to pass the baton iff we actually leave; otherwise
-         * we are still carrying it.
-         */
-        CTX_UNLOCK;
-        libxl__egc_cleanup_2_ul_cb_gc(egc);
-        CTX_LOCK;
-    }
-
- out:
-    libxl__poller_put(ctx, poller);
-
-    CTX_UNLOCK_EGC_FREE;
-    return rc;
-}
-
-
-
-/*
- * The two possible state flow of an ao:
- *
- * Completion before initiator return:
- *
- *     Initiator thread                       Possible other threads
- *
- *   * ao_create allocates memory and
- *     initialises the struct
- *
- *   * the initiator function does its
- *     work, setting up various internal
- *     asynchronous operations -----------> * asynchronous operations
- *                                            start to take place and
- *                                            might cause ao completion
- *                                                |
- *   * initiator calls ao_inprogress              |
- *     - if synchronous, run event loop           |
- *       until the ao completes                   |
- *                              - ao completes on some thread
- *                              - completing thread releases the lock
- *                     <--------------'
- *     - ao_inprogress takes the lock
- *     - destroy the ao
- *
- *
- * Completion after initiator return (asynch. only):
- *
- *
- *     Initiator thread                       Possible other threads
- *
- *   * ao_create allocates memory and
- *     initialises the struct
- *
- *   * the initiator function does its
- *     work, setting up various internal
- *     asynchronous operations -----------> * asynchronous operations
- *                                            start to take place and
- *                                            might cause ao completion
- *                                                |
- *   * initiator calls ao_inprogress              |
- *     - observes event not yet done,             |
- *     - returns to caller                        |
- *                                                |
- *                              - ao completes on some thread
- *                              - generate the event or call the callback
- *                              - destroy the ao
- */
-
-
-/*
- * A "manip" is a libxl public function manipulating this ao, which
- * has a pointer to it.  We have to not destroy it while that's the
- * case, obviously.  Callers must have the ctx locked, obviously.
- */
-static void ao__manip_enter(libxl__ao *ao)
-{
-    assert(ao->manip_refcnt < INT_MAX);
-    ao->manip_refcnt++;
-}
-
-static void ao__manip_leave(libxl_ctx *ctx, libxl__ao *ao)
-{
-    assert(ao->manip_refcnt > 0);
-    ao->manip_refcnt--;
-    ao__check_destroy(ctx, ao);
-}
-
-static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao)
-{
-    if (!ao->manip_refcnt && ao->notified) {
-        assert(ao->complete);
-        libxl__ao__destroy(ctx, ao);
-    }
-}
-
-void libxl__ao__destroy(libxl_ctx *ctx, libxl__ao *ao)
-{
-    AO_GC;
-    if (!ao) return;
-    LOG(DEBUG,"ao %p: destroy",ao);
-    libxl__poller_put(ctx, ao->poller);
-    ao->magic = LIBXL__AO_MAGIC_DESTROYED;
-    libxl__free_all(&ao->gc);
-    free(ao);
-}
-
-void libxl__ao_create_fail(libxl__ao *ao)
-{
-    AO_GC;
-    LOG(DEBUG,"ao %p: create fail",ao);
-    assert(ao->magic == LIBXL__AO_MAGIC);
-    assert(ao->in_initiator);
-    assert(!ao->complete);
-    assert(!ao->progress_reports_outstanding);
-    assert(!ao->aborting);
-    LIBXL_LIST_REMOVE(ao, inprogress_entry);
-    libxl__ao__destroy(CTX, ao);
-}
-
-libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao)
-{
-    assert(ao);
-    assert(ao->magic == LIBXL__AO_MAGIC);
-    assert(!ao->complete);
-    return &ao->gc;
-}
-
-void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc)
-{
-    AO_GC;
-    LOG(DEBUG,"ao %p: complete, rc=%d",ao,rc);
-    assert(ao->magic == LIBXL__AO_MAGIC);
-    assert(!ao->complete);
-    assert(!ao->nested_root);
-    assert(!ao->nested_progeny);
-    ao->complete = 1;
-    ao->rc = rc;
-    LIBXL_LIST_REMOVE(ao, inprogress_entry);
-    if (ao->outstanding_killed_child)
-        LOG(DEBUG, "ao %p: .. but waiting for %d fork to exit",
-            ao, ao->outstanding_killed_child);
-    libxl__ao_complete_check_progress_reports(egc, ao);
-}
-
-static bool ao_work_outstanding(libxl__ao *ao)
-{
-    /*
-     * We don't consider an ao complete if it has any outstanding
-     * callbacks.  These callbacks might be outstanding on other
-     * threads, queued up in the other threads' egc's.  Those threads
-     * will, after making the callback, take out the lock again,
-     * decrement progress_reports_outstanding, and call
-     * libxl__ao_complete_check_progress_reports.
-     */
-    return !ao->complete || ao->progress_reports_outstanding
-        || ao->outstanding_killed_child;
-}
-
-void libxl__ao_complete_check_progress_reports(libxl__egc *egc, libxl__ao *ao)
-{
-    EGC_GC;
-    libxl_ctx *ctx = libxl__gc_owner(&egc->gc);
-    assert(ao->progress_reports_outstanding >= 0);
-
-    if (ao_work_outstanding(ao))
-        return;
-
-    if (ao->poller) {
-        assert(ao->in_initiator);
-        if (!ao->constructing)
-            /* don't bother with this if we're not in the event loop */
-            libxl__poller_wakeup(gc, ao->poller);
-    } else if (ao->how.callback) {
-        LOG(DEBUG, "ao %p: complete for callback", ao);
-        LIBXL_TAILQ_INSERT_TAIL(&egc->aos_for_callback, ao, entry_for_callback);
-    } else {
-        libxl_event *ev;
-        ev = NEW_EVENT(egc, OPERATION_COMPLETE, ao->domid, ao->how.u.for_event);
-        if (ev) {
-            ev->u.operation_complete.rc = ao->rc;
-            libxl__event_occurred(egc, ev);
-        }
-        ao->notified = 1;
-    }
-    
-    ao__check_destroy(ctx, ao);
-}
-
-libxl__ao *libxl__ao_create(libxl_ctx *ctx, uint32_t domid,
-                            const libxl_asyncop_how *how,
-                            const char *file, int line, const char *func)
-{
-    libxl__ao *ao;
-
-    ao = calloc(1, sizeof(*ao));
-    if (!ao) goto out;
-
-    ao->magic = LIBXL__AO_MAGIC;
-    ao->constructing = 1;
-    ao->in_initiator = 1;
-    ao__manip_enter(ao);
-    ao->poller = 0;
-    ao->domid = domid;
-    LIBXL_INIT_GC(ao->gc, ctx);
-
-    if (how) {
-        ao->how = *how;
-    } else {
-        ao->poller = libxl__poller_get(&ao->gc);
-        if (!ao->poller) goto out;
-    }
-    libxl__log(ctx,XTL_DEBUG,-1,file,line,func,domid,
-               "ao %p: create: how=%p callback=%p poller=%p",
-               ao, how, ao->how.callback, ao->poller);
-
-    LIBXL_LIST_INSERT_HEAD(&ctx->aos_inprogress, ao, inprogress_entry);
-
-    return ao;
-
- out:
-    if (ao) libxl__ao__destroy(ctx, ao);
-    return NULL;
-}
-
-
-int libxl__ao_inprogress(libxl__ao *ao,
-                         const char *file, int line, const char *func)
-{
-    AO_GC;
-    int rc;
-    uint32_t domid = ao->domid;
-
-    assert(ao->magic == LIBXL__AO_MAGIC);
-    assert(ao->constructing);
-    assert(ao->in_initiator);
-    ao->constructing = 0;
-
-    if (ao->nested_root)
-        domid = ao->nested_root->domid;
-
-    libxl__log(CTX,XTL_DEBUG,-1,file,line,func,domid,
-               "ao %p: inprogress: poller=%p, flags=%s%s%s%s",
-               ao, ao->poller,
-               ao->constructing ? "o" : "",
-               ao->in_initiator ? "i" : "",
-               ao->complete ? "c" : "",
-               ao->notified ? "n" : "");
-
-    if (ao->poller) {
-        /* Caller wants it done synchronously. */
-        /* We use a fresh gc, so that we can free things
-         * each time round the loop. */
-        libxl__egc egc;
-        LIBXL_INIT_EGC(egc,CTX);
-
-        for (;;) {
-            assert(ao->magic == LIBXL__AO_MAGIC);
-
-            if (!ao_work_outstanding(ao)) {
-                rc = ao->rc;
-                ao->notified = 1;
-                break;
-            }
-
-            DBG("ao %p: not ready, waiting",ao);
-
-            rc = eventloop_iteration(&egc,ao->poller);
-            if (rc) {
-                /* Oh dear, this is quite unfortunate. */
-                LOG(ERROR,
-                    "Error waiting for"" event during long-running operation (rc=%d)",
-                    rc);
-                sleep(1);
-                /* It's either this or return ERROR_I_DONT_KNOW_WHETHER
-                 * _THE_THING_YOU_ASKED_FOR_WILL_BE_DONE_LATER_WHEN
-                 * _YOU_DIDNT_EXPECT_IT, since we don't have a
-                 * synchronous cancellation ability. */
-            }
-
-            /* The call to egc..1_baton is below, only if we are leaving. */
-            CTX_UNLOCK;
-            libxl__egc_cleanup_2_ul_cb_gc(&egc);
-            CTX_LOCK;
-        }
-
-        /* Dispose of this early so libxl__egc_ao_cleanup_1_baton
-         * doesn't mistake us for a baton-holder.  No-one much is
-         * going to look at this ao now so setting this to 0 is fine.
-         * We can't call _baton below _leave because _leave destroys
-         * our gc, which _baton needs. */
-        libxl__poller_put(CTX, ao->poller);
-        ao->poller = 0;
-    } else {
-        rc = 0;
-    }
-
-    libxl__egc_ao_cleanup_1_baton(gc);
-    ao->in_initiator = 0;
-    ao__manip_leave(CTX, ao);
-
-    return rc;
-}
-
-
-/* abort requests */
-
-static int ao__abort(libxl_ctx *ctx, libxl__ao *parent)
-/* Temporarily unlocks ctx, which must be locked exactly once on entry. */
-{
-    libxl__egc egc;
-    LIBXL_INIT_EGC(egc,ctx);
-
-    int rc;
-    ao__manip_enter(parent);
-
-    if (parent->aborting) {
-        rc = ERROR_ABORTED;
-        goto out;
-    }
-
-    parent->aborting = 1;
-
-    if (LIBXL_LIST_EMPTY(&parent->abortables)) {
-        LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
-                   "ao %p: abort requested and noted, but no-one interested",
-                   parent);
-        rc = 0;
-        goto out;
-    }
-
-    /* We keep calling abort hooks until there are none left */
-    while (!LIBXL_LIST_EMPTY(&parent->abortables)) {
-        assert(!parent->complete);
-
-        libxl__ao_abortable *abrt = LIBXL_LIST_FIRST(&parent->abortables);
-        assert(parent == ao_nested_root(abrt->ao));
-
-        LIBXL_LIST_REMOVE(abrt, entry);
-        abrt->registered = 0;
-
-        LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
-                   "ao %p: abrt=%p: aborting", parent, abrt->ao);
-        abrt->callback(&egc, abrt, ERROR_ABORTED);
-
-        /* The call to egc..1_baton is in the out block below. */
-        libxl__ctx_unlock(ctx);
-        libxl__egc_cleanup_2_ul_cb_gc(&egc);
-        libxl__ctx_lock(ctx);
-    }
-
-    rc = 0;
-
- out:
-    libxl__egc_ao_cleanup_1_baton(&egc.gc);
-    ao__manip_leave(ctx, parent);
-    /* The call to egc..2_ul_cb_gc is above.  This is sufficient
-     * because only code inside the loop adds anything to the egc, and
-     * we ensures that the egc is clean when we leave the loop. */
-    return rc;
-}
-
-int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
-{
-    libxl__ao *search;
-    libxl__ctx_lock(ctx);
-    int rc;
-
-    LIBXL_LIST_FOREACH(search, &ctx->aos_inprogress, inprogress_entry) {
-        if (how) {
-            /* looking for ao to be reported by callback or event */
-            if (search->poller)
-                /* sync */
-                continue;
-            if (how->callback != search->how.callback)
-                continue;
-            if (how->callback
-                ? (how->u.for_callback != search->how.u.for_callback)
-                : (how->u.for_event != search->how.u.for_event))
-                continue;
-        } else {
-            /* looking for synchronous call */
-            if (!search->poller)
-                /* async */
-                continue;
-        }
-        goto found;
-    }
-    rc = ERROR_NOTFOUND;
-    goto out;
-
- found:
-    rc = ao__abort(ctx, search);
- out:
-    libxl__ctx_unlock(ctx);
-    return rc;
-}
-
-int libxl__ao_aborting(libxl__ao *ao)
-{
-    libxl__ao *root = ao_nested_root(ao);
-    AO_GC;
-
-    if (root->aborting) {
-        DBG("ao=%p: aborting at explicit check (root=%p)", ao, root);
-        return ERROR_ABORTED;
-    }
-
-    return 0;
-}
-
-int libxl__ao_abortable_register(libxl__ao_abortable *abrt)
-{
-    libxl__ao *ao = abrt->ao;
-    libxl__ao *root = ao_nested_root(ao);
-    AO_GC;
-
-    if (root->aborting) {
- DBG("ao=%p: preemptively aborting ao_abortable registration %p (root=%p)",
-            ao, abrt, root);
-        return ERROR_ABORTED;
-    }
-
-    DBG("ao=%p, abrt=%p: registering (root=%p)", ao, abrt, root);
-    LIBXL_LIST_INSERT_HEAD(&root->abortables, abrt, entry);
-    abrt->registered = 1;
-
-    return 0;
-}
-
-_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable *abrt)
-{
-    if (!abrt->registered)
-        return;
-
-    libxl__ao *ao = abrt->ao;
-    libxl__ao *root __attribute__((unused)) = ao_nested_root(ao);
-    AO_GC;
-
-    DBG("ao=%p, abrt=%p: deregistering (root=%p)", ao, abrt, root);
-    LIBXL_LIST_REMOVE(abrt, entry);
-    abrt->registered = 0;
-}
-
-
-/* progress reporting */
-
-/* The application indicates a desire to ignore events by passing NULL
- * for how.  But we want to copy *how.  So we have this dummy function
- * whose address is stored in callback if the app passed how==NULL. */
-static void dummy_asyncprogress_callback_ignore
-  (libxl_ctx *ctx, libxl_event *ev, void *for_callback) { }
-
-void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
-                               const libxl_asyncprogress_how *from_app) {
-    if (from_app)
-        *in_state = *from_app;
-    else
-        in_state->callback = dummy_asyncprogress_callback_ignore;
-}
-
-void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
-        const libxl_asyncprogress_how *how, libxl_event *ev)
-{
-    AO_GC;
-    assert(!ao->nested_root);
-    if (how->callback == dummy_asyncprogress_callback_ignore) {
-        LOG(DEBUG,"ao %p: progress report: ignored",ao);
-        libxl_event_free(CTX,ev);
-        /* ignore */
-    } else if (how->callback) {
-        libxl__aop_occurred *aop = libxl__zalloc(&egc->gc, sizeof(*aop));
-        ao->progress_reports_outstanding++;
-        aop->ao = ao;
-        aop->ev = ev;
-        aop->how = how;
-        LIBXL_TAILQ_INSERT_TAIL(&egc->aops_for_callback, aop, entry);
-        LOG(DEBUG,"ao %p: progress report: callback queued aop=%p",ao,aop);
-    } else {
-        LOG(DEBUG,"ao %p: progress report: event queued ev=%p type=%s",
-            ao, ev, libxl_event_type_to_string(ev->type));
-        libxl__event_occurred(egc, ev);
-    }
-}
-
-
-/* nested ao */
-
-static libxl__ao *ao_nested_root(libxl__ao *ao) {
-    libxl__ao *root = ao->nested_root ? : ao;
-    assert(!root->nested_root);
-    return root;
-}
-
-_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent)
-{
-    libxl__ao *child = NULL, *root;
-    libxl_ctx *ctx = libxl__gc_owner(&parent->gc);
-
-    assert(parent->magic == LIBXL__AO_MAGIC);
-    root = ao_nested_root(parent);
-
-    child = libxl__zalloc(&ctx->nogc_gc, sizeof(*child));
-    child->magic = LIBXL__AO_MAGIC;
-    child->nested_root = root;
-    assert(root->nested_progeny < INT_MAX);
-    root->nested_progeny++;
-    LIBXL_INIT_GC(child->gc, ctx);
-    libxl__gc *gc = &child->gc;
-
-    LOG(DEBUG,"ao %p: nested ao, parent %p", child, parent);
-    return child;
-}
-
-_hidden void libxl__nested_ao_free(libxl__ao *child)
-{
-    assert(child->magic == LIBXL__AO_MAGIC);
-    libxl__ao *root = child->nested_root;
-    assert(root);
-    assert(root->nested_progeny > 0);
-    root->nested_progeny--;
-    libxl_ctx *ctx = libxl__gc_owner(&child->gc);
-    libxl__ao__destroy(ctx, child);
-}
-
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h
deleted file mode 100644 (file)
index 8d0aa64..0000000
+++ /dev/null
@@ -1,632 +0,0 @@
-/*
- * Copyright (C) 2011      Citrix Ltd.
- * Author Ian Jackson <ian.jackson@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef LIBXL_EVENT_H
-#define LIBXL_EVENT_H
-
-#include <libxl.h>
-#include <poll.h>
-#include <sys/time.h>
-
-/*======================================================================*/
-
-/*
- * Domain event handling - getting Xen events from libxl
- *
- * (Callers inside libxl may not call libxl_event_check or _wait.)
- */
-
-#define LIBXL_EVENTMASK_ALL (~(unsigned long)0)
-
-typedef int libxl_event_predicate(const libxl_event*, void *user);
-  /* Return value is 0 if the event is unwanted or non-0 if it is.
-   * Predicates are not allowed to fail.
-   */
-
-int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
-                      uint64_t typemask,
-                      libxl_event_predicate *predicate, void *predicate_user)
-                      LIBXL_EXTERNAL_CALLERS_ONLY;
-  /* Searches for an event, already-happened, which matches typemask
-   * and predicate.  predicate==0 matches any event.
-   * libxl_event_check returns the event, which must then later be
-   * freed by the caller using libxl_event_free.
-   *
-   * Returns ERROR_NOT_READY if no such event has happened.
-   */
-
-int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
-                     uint64_t typemask,
-                     libxl_event_predicate *predicate, void *predicate_user)
-                     LIBXL_EXTERNAL_CALLERS_ONLY;
-  /* Like libxl_event_check but blocks if no suitable events are
-   * available, until some are.  Uses libxl_osevent_beforepoll/
-   * _afterpoll so may be inefficient if very many domains are being
-   * handled by a single program.
-   */
-
-void libxl_event_free(libxl_ctx *ctx, libxl_event *event);
-
-
-/* Alternatively or additionally, the application may also use this: */
-
-typedef struct libxl_event_hooks {
-    uint64_t event_occurs_mask;
-    void (*event_occurs)(void *user,
-#ifndef LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
-                         const
-#endif
-                         libxl_event *event);
-    void (*disaster)(void *user, libxl_event_type type,
-                     const char *msg, int errnoval);
-} libxl_event_hooks;
-
-void libxl_event_register_callbacks(libxl_ctx *ctx,
-                                    const libxl_event_hooks *hooks, void *user);
-  /*
-   * Arranges that libxl will henceforth call event_occurs for any
-   * events whose type is set in event_occurs_mask, rather than
-   * queueing the event for retrieval by libxl_event_check/wait.
-   * Events whose bit is clear in mask are not affected.
-   *
-   * event becomes owned by the application and must be freed, either
-   * by event_occurs or later.
-   *
-   * event_occurs may be NULL if mask is 0.
-   *
-   * libxl_event_register_callback also provides a way for libxl to
-   * report to the application that there was a problem reporting
-   * events; this can occur due to lack of host memory during event
-   * handling, or other wholly unrecoverable errors from system calls
-   * made by libxl.  This will not happen for frivolous reasons - only
-   * if the system, or the Xen components of it, are badly broken.
-   *
-   * msg and errnoval will describe the action that libxl was trying
-   * to do, and type specifies the type of libxl events which may be
-   * missing.  type may be 0 in which case events of all types may be
-   * missing.
-   *
-   * disaster may be NULL.  If it is, or if _register_callbacks has
-   * not been called, errors of this kind are fatal to the entire
-   * application: libxl will print messages to its logs and to stderr
-   * and call exit(-1).
-   *
-   * If disaster returns, it may be the case that some or all future
-   * libxl calls will return errors; likewise it may be the case that
-   * no more events (of the specified type, if applicable) can be
-   * produced.  An application which supplies a disaster function
-   * should normally react either by exiting, or by (when it has
-   * returned to its main event loop) shutting down libxl with
-   * libxl_ctx_free and perhaps trying to restart it with
-   * libxl_ctx_init.
-   *
-   * In any case before calling disaster, libxl will have logged a
-   * message with level XTL_CRITICAL.
-   *
-   * Reentrancy: it IS permitted to call libxl from within
-   * event_occurs.  It is NOT permitted to call libxl from within
-   * disaster.  The event_occurs and disaster callbacks may occur on
-   * any thread in which the application calls libxl.
-   *
-   * libxl_event_register_callbacks may be called as many times, with
-   * different parameters, as the application likes; the most recent
-   * call determines the libxl behaviour.  However it is NOT safe to
-   * call _register_callbacks concurrently with, or reentrantly from,
-   * any other libxl function, nor while any event-generation
-   * facilities are enabled.
-   */
-
-
-/*
- * Events are only generated if they have been requested.
- * The following functions request the generation of specific events.
- *
- * Each set of functions for controlling event generation has this form:
- *
- *   typedef struct libxl__evgen_FOO libxl__evgen_FOO;
- *   int libxl_evenable_FOO(libxl_ctx *ctx, FURTHER PARAMETERS,
- *                          libxl_ev_user user, libxl__evgen_FOO **evgen_out);
- *   void libxl_evdisable_FOO(libxl_ctx *ctx, libxl__evgen_FOO *evgen);
- *
- * The evenable function arranges that the events (as described in the
- * doc comment for the individual function) will start to be generated
- * by libxl.  On success, *evgen_out is set to a non-null pointer to
- * an opaque struct.
- *
- * The user value is returned in the generated events and may be
- * used by the caller for whatever it likes.  The type ev_user is
- * guaranteed to be an unsigned integer type which is at least
- * as big as uint64_t and is also guaranteed to be big enough to
- * contain any intptr_t value.
- *
- * If it becomes desirable to stop generation of the relevant events,
- * or to reclaim the resources in libxl associated with the evgen
- * structure, the same evgen value should be passed to the evdisable
- * function.  However, note that events which occurred prior to the
- * evdisable call may still be returned.
- *
- * The caller may enable identical events more than once.  If they do
- * so, each actual occurrence will generate several events to be
- * returned by libxl_event_check, with the appropriate user value(s).
- * Aside from this, each occurrence of each event is returned by
- * libxl_event_check exactly once.
- *
- * An evgen is associated with the libxl_ctx used for its creation.
- * After libxl_ctx_free, all corresponding evgen handles become
- * invalid and must no longer be passed to evdisable.
- *
- * Applications should ensure that they eventually retrieve every
- * event using libxl_event_check or libxl_event_wait, since events
- * which occur but are not retrieved by the application will be queued
- * inside libxl indefinitely.  libxl_event_check/_wait may be O(n)
- * where n is the number of queued events which do not match the
- * criteria specified in the arguments to check/wait.
- */
-
-typedef struct libxl__evgen_domain_death libxl_evgen_domain_death;
-int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
-                         libxl_ev_user, libxl_evgen_domain_death **evgen_out);
-void libxl_evdisable_domain_death(libxl_ctx *ctx, libxl_evgen_domain_death*);
-  /* Arranges for the generation of DOMAIN_SHUTDOWN and DOMAIN_DEATH
-   * events.  A domain which is destroyed before it shuts down
-   * may generate only a DEATH event.
-   */
-
-typedef struct libxl__evgen_disk_eject libxl_evgen_disk_eject;
-int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t domid, const char *vdev,
-                        libxl_ev_user, libxl_evgen_disk_eject **evgen_out);
-void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject*);
-  /* Arranges for the generation of DISK_EJECT events.  A copy of the
-   * string *vdev will be made for libxl's internal use, and a pointer
-   * to this (or some other) copy will be returned as the vdev
-   * member of event.u.
-   */
-
-
-/*======================================================================*/
-
-/*
- * OS event handling - passing low-level OS events to libxl
- *
- * Event-driven programs must use these facilities to allow libxl
- * to become aware of readability/writeability of file descriptors
- * and the occurrence of timeouts.
- *
- * There are two approaches available.  The first is appropriate for
- * simple programs handling reasonably small numbers of domains:
- *
- *   for (;;) {
- *      libxl_osevent_beforepoll(...)
- *      poll();
- *      libxl_osevent_afterpoll(...);
- *      for (;;) {
- *          r = libxl_event_check(...);
- *          if (r==ERROR_NOT_READY) break;
- *          if (r) goto error_out;
- *          do something with the event;
- *      }
- *   }
- *
- * The second approach uses libxl_osevent_register_hooks and is
- * suitable for programs which are already using a callback-based
- * event library.
- *
- * An application may freely mix the two styles of interaction.
- *
- * (Callers inside libxl may not call libxl_osevent_... functions.)
- */
-
-struct pollfd;
-
-/* The caller should provide beforepoll with some space for libxl's
- * fds, and tell libxl how much space is available by setting *nfds_io.
- * fds points to the start of this space (and fds may be a pointer into
- * a larger array, for example, if the application has some fds of
- * its own that it is interested in).
- *
- * On return *nfds_io will in any case have been updated by libxl
- * according to how many fds libxl wants to poll on.
- *
- * If the space was sufficient, libxl fills in fds[0..<new
- * *nfds_io>] suitably for poll(2), updates *timeout_upd if needed,
- * and returns ok.
- *
- * If space was insufficient, fds[0..<old *nfds_io>] is undefined on
- * return; *nfds_io on return will be greater than the value on
- * entry; *timeout_upd may or may not have been updated; and
- * libxl_osevent_beforepoll returns ERROR_BUFERFULL.  In this case
- * the application needs to make more space (enough space for
- * *nfds_io struct pollfd) and then call beforepoll again, before
- * entering poll(2).  Typically this will involve calling realloc.
- *
- * The application may call beforepoll with fds==NULL and
- * *nfds_io==0 in order to find out how much space is needed.
- *
- * *timeout_upd is as for poll(2): it's in milliseconds, and
- * negative values mean no timeout (infinity).
- * libxl_osevent_beforepoll will only reduce the timeout, naturally.
- */
-int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
-                             struct pollfd *fds, int *timeout_upd,
-                             struct timeval now)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* nfds and fds[0..nfds] must be from the most recent call to
- * _beforepoll, as modified by poll.  (It is therefore not possible
- * to have multiple threads simultaneously polling using this
- * interface.)
- *
- * This function actually performs all of the IO and other actions,
- * and generates events (libxl_event), which are implied by either
- * (a) the time of day or (b) both (i) the returned information from
- * _beforepoll, and (ii) the results from poll specified in
- * fds[0..nfds-1].  Generated events can then be retrieved by
- * libxl_event_check.
- */
-void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
-                             struct timeval now)
-                             LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-typedef struct libxl_osevent_hooks {
-  int (*fd_register)(void *user, int fd, void **for_app_registration_out,
-                     short events, void *for_libxl);
-  int (*fd_modify)(void *user, int fd, void **for_app_registration_update,
-                   short events);
-  void (*fd_deregister)(void *user, int fd, void *for_app_registration);
-  int (*timeout_register)(void *user, void **for_app_registration_out,
-                          struct timeval abs, void *for_libxl);
-  int (*timeout_modify)(void *user, void **for_app_registration_update,
-                         struct timeval abs)
-      /* only ever called with abs={0,0}, meaning ASAP */;
-  void (*timeout_deregister)(void *user, void *for_app_registration)
-      /* will never be called */;
-} libxl_osevent_hooks;
-
-/* The application which calls register_fd_hooks promises to
- * maintain a register of fds and timeouts that libxl is interested
- * in, and make calls into libxl (libxl_osevent_occurred_*)
- * when those fd events and timeouts occur.  This is more efficient
- * than _beforepoll/_afterpoll if there are many fds (which can
- * happen if the same libxl application is managing many domains).
- *
- * For an fd event, events is as for poll().  register or modify may
- * be called with events==0, in which case it must still work
- * normally, just not generate any events.
- *
- * For a timeout event, milliseconds is as for poll().
- * Specifically, negative values of milliseconds mean NO TIMEOUT.
- * This is used by libxl to temporarily disable a timeout.
- *
- * If the register or modify hook succeeds it may update
- * *for_app_registration_out/_update and must then return 0.
- * On entry to register, *for_app_registration_out is always NULL.
- *
- * A registration or modification hook may fail, in which case it
- * must leave the registration state of the fd or timeout unchanged.
- * It may then either return ERROR_OSEVENT_REG_FAIL or any positive
- * int.  The value returned will be passed up through libxl and
- * eventually returned back to the application.  When register
- * fails, any value stored into *for_registration_out is ignored by
- * libxl; when modify fails, any changed value stored into
- * *for_registration_update is honoured by libxl and will be passed
- * to future modify or deregister calls.
- *
- * libxl may want to register more than one callback for any one fd;
- * in that case: (i) each such registration will have at least one bit
- * set in revents which is unique to that registration; (ii) if an
- * event occurs which is relevant for multiple registrations the
- * application's event system may call libxl_osevent_occurred_fd
- * for one, some, or all of those registrations.
- *
- * If fd_modify is used, it is permitted for the application's event
- * system to still make calls to libxl_osevent_occurred_fd for the
- * "old" set of requested events; these will be safely ignored by
- * libxl.
- *
- * libxl will remember the value stored in *for_app_registration_out
- * (or *for_app_registration_update) by a successful call to
- * register (or modify), and pass it to subsequent calls to modify
- * or deregister.
- *
- * Note that the application must cope with a call from libxl to
- * timeout_modify racing with its own call to
- * libxl__osevent_occurred_timeout.  libxl guarantees that
- * timeout_modify will only be called with abs={0,0} but the
- * application must still ensure that libxl's attempt to cause the
- * timeout to occur immediately is safely ignored even the timeout is
- * actually already in the process of occurring.
- *
- * timeout_deregister is not used because it forms part of a
- * deprecated unsafe mode of use of the API.
- *
- * osevent_register_hooks may be called only once for each libxl_ctx.
- * libxl may make calls to register/modify/deregister from within
- * any libxl function (indeed, it will usually call register from
- * register_event_hooks).  Conversely, the application MUST NOT make
- * the event occurrence calls (libxl_osevent_occurred_*) into libxl
- * reentrantly from within libxl (for example, from within the
- * register/modify functions).
- *
- * Lock hierarchy: the register/modify/deregister functions may be
- * called with locks held.  These locks (the "libxl internal locks")
- * are inside the libxl_ctx.  Therefore, if those register functions
- * acquire any locks of their own ("caller register locks") outside
- * libxl, to avoid deadlock one of the following must hold for each
- * such caller register lock:
- *  (a) "acquire libxl internal locks before caller register lock":
- *      No libxl function may be called with the caller register
- *      lock held.
- *  (b) "acquire caller register lock before libxl internal locks":
- *      No libxl function may be called _without_ the caller
- *      register lock held.
- * Of these we would normally recommend (a).
- *
- * The value *hooks is not copied and must outlast the libxl_ctx.
- */
-void libxl_osevent_register_hooks(libxl_ctx *ctx,
-                                  const libxl_osevent_hooks *hooks,
-                                  void *user);
-
-/* It is NOT legal to call _occurred_ reentrantly within any libxl
- * function.  Specifically it is NOT legal to call it from within
- * a register callback.  Conversely, libxl MAY call register/deregister
- * from within libxl_event_occurred_call_*.
- */
-
-void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
-                               int fd, short events, short revents)
-                               LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/* Implicitly, on entry to this function the timeout has been
- * deregistered.  If _occurred_timeout is called, libxl will not
- * call timeout_deregister; if it wants to requeue the timeout it
- * will call timeout_register again.
- */
-void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
-                                    LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-/*======================================================================*/
-
-/*
- * Subprocess handling.
- *
- * Unfortunately the POSIX interface makes this very awkward.
- *
- * There are two possible arrangements for collecting statuses from
- * wait/waitpid.
- *
- * For naive programs:
- *
- *     libxl will keep a SIGCHLD handler installed whenever it has an
- *     active (unreaped) child.  It will reap all children with
- *     wait(); any children it does not recognise will be passed to
- *     the application via an optional callback (and will result in
- *     logged warnings if no callback is provided or the callback
- *     denies responsibility for the child).
- *
- *     libxl may have children whenever:
- *
- *       - libxl is performing an operation which can be made
- *         asynchronous; ie one taking a libxl_asyncop_how, even
- *         if NULL is passed indicating that the operation is
- *         synchronous; or
- *
- *       - events of any kind are being generated, as requested
- *         by libxl_evenable_....
- *
- *     A multithreaded application which is naive in this sense may
- *     block SIGCHLD on some of its threads, but there must be at
- *     least one thread that has SIGCHLD unblocked.  libxl will not
- *     modify the blocking flag for SIGCHLD (except that it may create
- *     internal service threads with all signals blocked).
- *
- *     A naive program must only have at any one time only
- *     one libxl context which might have children.
- *
- * For programs which run their own children alongside libxl's:
- *
- *     A program which does this must call libxl_childproc_setmode.
- *     There are three options:
- * 
- *     libxl_sigchld_owner_libxl:
- *
- *       While any libxl operation which might use child processes
- *       is running, works like libxl_sigchld_owner_libxl_always;
- *       but, deinstalls the handler the rest of the time.
- *
- *       In this mode, the application, while it uses any libxl
- *       operation which might create or use child processes (see
- *       above):
- *           - Must not have any child processes running.
- *           - Must not install a SIGCHLD handler.
- *           - Must not reap any children.
- *
- *       This is the default (i.e. if setmode is not called, or 0 is
- *       passed for hooks).
- *
- *     libxl_sigchld_owner_mainloop:
- *
- *       The application must install a SIGCHLD handler and reap (at
- *       least) all of libxl's children and pass their exit status to
- *       libxl by calling libxl_childproc_exited.  (If the application
- *       has multiple libxl ctx's, it must call libxl_childproc_exited
- *       on each ctx.)
- *
- *     libxl_sigchld_owner_libxl_always:
- *
- *       The application expects this libxl ctx to reap all of the
- *       process's children, and provides a callback to be notified of
- *       their exit statuses.  The application must have only one
- *       libxl_ctx configured this way.
- *
- *     libxl_sigchld_owner_libxl_always_selective_reap:
- *
- *       The application expects to reap all of its own children
- *       synchronously, and does not use SIGCHLD.  libxl is to install
- *       a SIGCHLD handler.  The application may have multiple
- *       libxl_ctxs configured this way; in which case all of its ctxs
- *       must be so configured.
- */
-
-
-typedef enum {
-    /* libxl owns SIGCHLD whenever it has a child, and reaps
-     * all children, including those not spawned by libxl. */
-    libxl_sigchld_owner_libxl,
-
-    /* Application promises to discover when SIGCHLD occurs and call
-     * libxl_childproc_exited or libxl_childproc_sigchld_occurred (but
-     * NOT from within a signal handler).  libxl will not itself
-     * arrange to (un)block or catch SIGCHLD. */
-    libxl_sigchld_owner_mainloop,
-
-    /* libxl owns SIGCHLD all the time, and the application is
-     * relying on libxl's event loop for reaping its children too. */
-    libxl_sigchld_owner_libxl_always,
-
-    /* libxl owns SIGCHLD all the time, but it must only reap its own
-     * children.  The application will reap its own children
-     * synchronously with waitpid, without the assistance of SIGCHLD. */
-    libxl_sigchld_owner_libxl_always_selective_reap,
-} libxl_sigchld_owner;
-
-typedef struct {
-    libxl_sigchld_owner chldowner;
-
-    /* All of these are optional: */
-
-    /* Called by libxl instead of fork.  Should behave exactly like
-     * fork, including setting errno etc.  May NOT reenter into libxl.
-     * Application may use this to discover pids of libxl's children,
-     * for example.
-     */
-    pid_t (*fork_replacement)(void *user);
-
-    /* With libxl_sigchld_owner_libxl, called by libxl when it has
-     * reaped a pid.  (Not permitted with _owner_mainloop.)
-     *
-     * Should return 0 if the child was recognised by the application
-     * (or if the application does not keep those kind of records),
-     * ERROR_UNKNOWN_CHILD if the application knows that the child is not
-     * the application's; if it returns another error code it is a
-     * disaster as described for libxl_event_register_callbacks.
-     * (libxl will report unexpected children to its error log.)
-     *
-     * If not supplied, the application is assumed not to start
-     * any children of its own.
-     *
-     * This function is NOT called from within the signal handler.
-     * Rather it will be called from inside a libxl's event handling
-     * code and thus only when libxl is running, for example from
-     * within libxl_event_wait.  (libxl uses the self-pipe trick
-     * to implement this.)
-     *
-     * childproc_exited_callback may call back into libxl, but it
-     * is best to avoid making long-running libxl calls as that might
-     * stall the calling event loop while the nested operation
-     * completes.
-     */
-    int (*reaped_callback)(pid_t, int status, void *user);
-} libxl_childproc_hooks;
-
-/* hooks may be 0 in which is equivalent to &{ libxl_sigchld_owner_libxl, 0, 0 }
- *
- * May not be called when libxl might have any child processes, or the
- * behaviour is undefined.  So it is best to call this at
- * initialisation.
- *
- * The value *hooks is not copied and must outlast the libxl_ctx.
- */
-void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
-                             void *user);
-
-/*
- * This function is for an application which owns SIGCHLD and which
- * reaps all of the process's children, and dispatches the exit status
- * to the correct place inside the application.
- *
- * May be called only by an application which has called setmode with
- * chldowner == libxl_sigchld_owner_mainloop.  If pid was a process started
- * by this instance of libxl, returns 0 after doing whatever
- * processing is appropriate.  Otherwise silently returns
- * ERROR_UNKNOWN_CHILD.  No other error returns are possible.
- *
- * May NOT be called from within a signal handler which might
- * interrupt any libxl operation.  The application will almost
- * certainly need to use the self-pipe trick (or a working pselect or
- * ppoll) to implement this.
- */
-int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status)
-                           LIBXL_EXTERNAL_CALLERS_ONLY;
-
-/*
- * This function is for an application which owns SIGCHLD but which
- * doesn't keep track of all of its own children in a manner suitable
- * for reaping all of them and then dispatching them.
- *
- * Such an the application must notify libxl, by calling this
- * function, that a SIGCHLD occurred.  libxl will then check all its
- * children, reap any that are ready, and take any action necessary -
- * but it will not reap anything else.
- *
- * May be called only by an application which has called setmode with
- * chldowner == libxl_sigchld_owner_mainloop.
- *
- * May NOT be called from within a signal handler which might
- * interrupt any libxl operation (just like libxl_childproc_reaped).
- */
-void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
-                           LIBXL_EXTERNAL_CALLERS_ONLY;
-
-
-/*
- * An application which initialises a libxl_ctx in a parent process
- * and then forks a child which does not quickly exec, must
- * instead libxl_postfork_child_noexec in the child.  One call
- * on any existing (or specially made) ctx is sufficient; after
- * this all previously existing libxl_ctx's are invalidated and
- * must not be used - or even freed.  It is harmless to call this
- * postfork function and then exec anyway.
- *
- * Until libxl_postfork_child_noexec has returned:
- *  - No other libxl calls may be made.
- *  - If any libxl ctx was configured handle the process's SIGCHLD,
- *    the child may not create further (grand)child processes, nor
- *    manipulate SIGCHLD.
- *
- * libxl_postfork_child_noexec may not reclaim all the resources
- * associated with the libxl ctx.  This includes but is not limited
- * to: ordinary memory; files on disk and in /var/run; file
- * descriptors; memory mapped into the process from domains being
- * managed (grant maps); Xen event channels.  Use of libxl in
- * processes which fork long-lived children is not recommended for
- * this reason.  libxl_postfork_child_noexec is provided so that
- * an application can make further libxl calls in a child which
- * is going to exec or exit soon.
- */
-void libxl_postfork_child_noexec(libxl_ctx *ctx);
-
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_exec.c b/tools/libxl/libxl_exec.c
deleted file mode 100644 (file)
index 47c9c8f..0000000
+++ /dev/null
@@ -1,473 +0,0 @@
-
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-static void check_open_fds(const char *what)
-{
-    const char *env_debug;
-    int debug;
-    int i, flags, badness = 0;
-
-    env_debug = getenv("_LIBXL_DEBUG_EXEC_FDS");
-    if (!env_debug) return;
-
-    debug = strtol(env_debug, (char **) NULL, 10);
-    if (debug <= 0) return;
-
-    for (i = 4; i < 256; i++) {
-#ifdef __linux__
-        ssize_t len;
-        char path[PATH_MAX];
-        char linkpath[PATH_MAX+1];
-#endif
-        flags = fcntl(i, F_GETFD);
-        if ( flags == -1 ) {
-            if ( errno != EBADF )
-                fprintf(stderr, "libxl: execing %s: fd %d flags returned %s (%d)\n",
-                        what, i, strerror(errno), errno);
-            continue;
-        }
-
-        if ( flags & FD_CLOEXEC )
-            continue;
-
-        badness++;
-
-#ifdef __linux__
-        snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), i);
-        len = readlink(path, linkpath, PATH_MAX);
-        if (len > 0) {
-            linkpath[len] = '\0';
-            fprintf(stderr, "libxl: execing %s: fd %d is open to %s with flags %#x\n",
-                    what, i, linkpath, flags);
-        } else
-#endif
-            fprintf(stderr, "libxl: execing %s: fd %d is open with flags %#x\n",
-                    what, i, flags);
-    }
-    if (debug < 2) return;
-    if (badness) abort();
-}
-
-void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd,
-                 const char *arg0, char *const args[], char *const env[])
-     /* call this in the child */
-{
-    if (stdinfd != -1)
-        dup2(stdinfd, STDIN_FILENO);
-    if (stdoutfd != -1)
-        dup2(stdoutfd, STDOUT_FILENO);
-    if (stderrfd != -1)
-        dup2(stderrfd, STDERR_FILENO);
-
-    if (stdinfd > 2)
-        close(stdinfd);
-    if (stdoutfd > 2 && stdoutfd != stdinfd)
-        close(stdoutfd);
-    if (stderrfd > 2 && stderrfd != stdinfd && stderrfd != stdoutfd)
-        close(stderrfd);
-
-    check_open_fds(arg0);
-
-    signal(SIGPIPE, SIG_DFL);
-    /* in case our caller set it to IGN.  subprocesses are entitled
-     * to assume they got DFL. */
-
-    if (env != NULL) {
-        for (int i = 0; env[i] != NULL && env[i+1] != NULL; i += 2) {
-            if (setenv(env[i], env[i+1], 1) < 0) {
-                LOGEV(ERROR, errno, "setting env vars (%s = %s)",
-                                    env[i], env[i+1]);
-                goto out;
-            }
-        }
-    }
-    execvp(arg0, args);
-
-out:
-    fprintf(stderr, "libxl: cannot execute %s: %s\n", arg0, strerror(errno));
-    _exit(-1);
-}
-
-void libxl_report_child_exitstatus(libxl_ctx *ctx,
-                                   xentoollog_level level,
-                                   const char *what, pid_t pid, int status)
-{
-
-    if (WIFEXITED(status)) {
-        int st = WEXITSTATUS(status);
-        if (st)
-            LIBXL__LOG(ctx, level, "%s [%ld] exited"
-                   " with error status %d", what, (unsigned long)pid, st);
-        else
-            LIBXL__LOG(ctx, level, "%s [%ld] unexpectedly"
-                   " exited status zero", what, (unsigned long)pid);
-    } else if (WIFSIGNALED(status)) {
-        int sig = WTERMSIG(status);
-        const char *str = strsignal(sig);
-        const char *coredump = WCOREDUMP(status) ? " (core dumped)" : "";
-        if (str)
-            LIBXL__LOG(ctx, level, "%s [%ld] died due to"
-                   " fatal signal %s%s", what, (unsigned long)pid,
-                   str, coredump);
-        else
-            LIBXL__LOG(ctx, level, "%s [%ld] died due to unknown"
-                   " fatal signal number %d%s", what, (unsigned long)pid,
-                   sig, coredump);
-    } else {
-        LIBXL__LOG(ctx, level, "%s [%ld] gave unknown"
-               " wait status 0x%x", what, (unsigned long)pid, status);
-    }
-}
-
-int libxl__spawn_record_pid(libxl__gc *gc, libxl__spawn_state *spawn, pid_t pid)
-{
-    int r, rc;
-
-    rc = libxl__ev_child_xenstore_reopen(gc, spawn->what);
-    if (rc) goto out;
-
-    r = libxl__xs_printf(gc, XBT_NULL, spawn->pidpath, "%d", pid);
-    if (r) {
-        LOGE(ERROR,
-             "write %s = %d: xenstore write failed", spawn->pidpath, pid);
-        rc = ERROR_FAIL;  goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc ? SIGTERM : 0;
-}
-
-int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
-                                 uint32_t domid,
-                                 uint32_t timeout, char *what,
-                                 char *path, char *state,
-                                 libxl__spawn_starting *spawning,
-                                 int (*check_callback)(libxl__gc *gc,
-                                                       uint32_t domid,
-                                                       const char *state,
-                                                       void *userdata),
-                                 void *check_callback_userdata)
-{
-    char *p;
-    unsigned int len;
-    int rc = 0;
-    struct xs_handle *xsh;
-    int nfds;
-    fd_set rfds;
-    struct timeval tv;
-    unsigned int num;
-    char **l = NULL;
-
-    xsh = xs_daemon_open();
-    if (xsh == NULL) {
-        LOG(ERROR, "Unable to open xenstore connection");
-        goto err;
-    }
-
-    xs_watch(xsh, path, path);
-    tv.tv_sec = timeout;
-    tv.tv_usec = 0;
-    nfds = xs_fileno(xsh) + 1;
-    assert(!spawning);
-
-    while (rc > 0 || (!rc && tv.tv_sec > 0)) {
-        p = xs_read(xsh, XBT_NULL, path, &len);
-        if ( NULL == p )
-            goto again;
-
-        if ( NULL != state && strcmp(p, state) )
-            goto again;
-
-        if ( NULL != check_callback ) {
-            rc = (*check_callback)(gc, domid, p, check_callback_userdata);
-            if ( rc > 0 )
-                goto again;
-        }
-
-        free(p);
-        xs_unwatch(xsh, path, path);
-        xs_daemon_close(xsh);
-        return rc;
-again:
-        free(p);
-        FD_ZERO(&rfds);
-        FD_SET(xs_fileno(xsh), &rfds);
-        rc = select(nfds, &rfds, NULL, NULL, &tv);
-        if (rc > 0) {
-            if (FD_ISSET(xs_fileno(xsh), &rfds)) {
-                l = xs_read_watch(xsh, &num);
-                if (l != NULL)
-                    free(l);
-                else
-                    goto again;
-            }
-        }
-    }
-    LOG(ERROR, "%s not ready", what);
-
-    xs_unwatch(xsh, path, path);
-    xs_daemon_close(xsh);
-err:
-    return -1;
-}
-
-
-/*----- spawn implementation -----*/
-
-/*
- * Full set of possible states of a libxl__spawn_state and its _detachable:
- *
- *                   detaching rc      mid     timeout      xswatch
- *  - Undefined         undef   undef   -        undef        undef
- *  - Idle              any     any     Idle     Idle         Idle
- *  - Attached OK       0       0       Active   Active       Active
- *  - Attached Failed   0       non-0   Active   Idle         Idle
- *  - Detaching         1       maybe   Active   Idle         Idle
- *  - Partial           any     any     Idle     Active/Idle  Active/Idle
- *
- * When in states Detaching or Attached Failed, the middle process has
- * been sent a SIGKILL.
- *
- * The difference between Attached OK and Attached Failed is not
- * directly visible to callers - callers see these two the same,
- * although of course Attached OK will hopefully eventually result in
- * a call to detached_cb, whereas Attached Failed will end up
- * in a call to failure_cb.
- */
-
-/* Event callbacks. */
-static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
-                              int rc, const char *xsdata);
-static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
-                               pid_t pid, int status);
-
-/* Precondition: Partial.  Results: Idle. */
-static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss);
-
-/* Precondition: Attached or Detaching; caller has logged failure reason.
- * Results: Detaching, or Attached Failed */
-static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc);
-
-void libxl__spawn_init(libxl__spawn_state *ss)
-{
-    libxl__ev_child_init(&ss->mid);
-    libxl__xswait_init(&ss->xswait);
-}
-
-int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss)
-{
-    STATE_AO_GC(ss->ao);
-    int r;
-    pid_t child;
-    int status, rc;
-
-    libxl__spawn_init(ss);
-    ss->rc = ss->detaching = 0;
-
-    ss->xswait.ao = ao;
-    ss->xswait.what = GCSPRINTF("%s startup", ss->what);
-    ss->xswait.path = ss->xspath;
-    ss->xswait.timeout_ms = ss->timeout_ms;
-    ss->xswait.callback = spawn_watch_event;
-    rc = libxl__xswait_start(gc, &ss->xswait);
-    if (rc) goto out_err;
-
-    pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death);
-    if (middle ==-1) { rc = ERROR_FAIL; goto out_err; }
-
-    if (middle) {
-        /* parent */
-        return 1;
-    }
-
-    /* we are now the middle process */
-
-    pid_t (*fork_replacement)(void*) =
-        CTX->childproc_hooks
-        ? CTX->childproc_hooks->fork_replacement
-        : 0;
-    child =
-        fork_replacement
-        ? fork_replacement(CTX->childproc_user)
-        : fork();
-
-    if (child == -1)
-        exit(255);
-    if (!child) {
-        return 0; /* caller runs child code */
-    }
-
-    int failsig = ss->midproc_cb(gc, ss, child);
-    if (failsig) {
-        kill(child, failsig);
-        _exit(127);
-    }
-
-    for (;;) {
-        pid_t got = waitpid(child, &status, 0);
-        if (got == -1) {
-            assert(errno == EINTR);
-            continue;
-        }
-        assert(got == child);
-        break;
-    }
-
-    r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) :
-         WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 :
-         -1);
-    _exit(r);
-
- out_err:
-    spawn_cleanup(gc, ss);
-    return rc;
-}
-
-static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss)
-{
-    assert(!libxl__ev_child_inuse(&ss->mid));
-    libxl__xswait_stop(gc, &ss->xswait);
-}
-
-static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss)
-/* Precondition: Attached or Detaching, but caller must have just set
- * at least one of detaching or rc.
- * Results: Detaching or Attached Failed */
-{
-    int r;
-
-    assert(libxl__ev_child_inuse(&ss->mid));
-    assert(ss->detaching || ss->rc);
-    libxl__xswait_stop(gc, &ss->xswait);
-
-    pid_t child = ss->mid.pid;
-    r = kill(child, SIGKILL);
-    if (r && errno != ESRCH)
-        LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)",
-             ss->what, (unsigned long)child);
-}
-
-void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss)
-{
-    ss->detaching = 1;
-    spawn_detach(gc, ss);
-}
-
-void libxl__spawn_initiate_failure(libxl__egc *egc, libxl__spawn_state *ss,
-                                   int rc)
-/* The spawn state must be Attached on entry and will be Attached Failed
- * on return.  */
-{
-    spawn_fail(egc, ss, rc);
-}
-
-static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc)
-/* Caller must have logged.  Must be last thing in calling function,
- * as it may make the callback.  Precondition: Attached or Detaching. */
-{
-    EGC_GC;
-    assert(rc);
-    if (!ss->rc)
-        ss->rc = rc;
-    spawn_detach(gc, ss);
-}
-
-static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
-                              int rc, const char *p)
-{
-    /* On entry, is Attached. */
-    EGC_GC;
-    libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait);
-    if (rc) {
-        if (rc == ERROR_TIMEDOUT)
-            LOG(ERROR, "%s: startup timed out", ss->what);
-        spawn_fail(egc, ss, rc); /* must be last */
-        return;
-    }
-    LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p);
-    ss->confirm_cb(egc, ss, p); /* must be last */
-}
-
-static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
-                               pid_t pid, int status)
-    /* On entry, is Attached or Detaching */
-{
-    EGC_GC;
-    libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid);
-
-    if ((ss->rc || ss->detaching) &&
-        ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
-         (WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) {
-        /* as expected */
-        const char *what = GCSPRINTF("%s (dying as expected)", ss->what);
-        libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status);
-    } else if (!WIFEXITED(status)) {
-        int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR;
-        const char *what =
-            GCSPRINTF("%s intermediate process (startup monitor)", ss->what);
-        libxl_report_child_exitstatus(CTX, loglevel, what, pid, status);
-        ss->rc = ERROR_FAIL;
-    } else {
-        if (!status)
-            LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0,"
-                " when we were waiting for it to confirm startup",
-                ss->what, (unsigned long)pid);
-        else if (status <= 127)
-            LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d",
-                ss->what, (unsigned long)pid, status);
-        else if (status < 255) {
-            int sig = status - 128;
-            const char *str = strsignal(sig);
-            if (str)
-                LOG(ERROR, "%s [%ld]: died during startup due to fatal"
-                    " signal %s", ss->what, (unsigned long)pid, str);
-            else
-                LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal"
-                    " signal number %d", ss->what, (unsigned long)pid, sig);
-        }
-        ss->rc = ERROR_FAIL;
-    }
-
-    spawn_cleanup(gc, ss);
-
-    if (ss->rc && !ss->detaching) {
-        ss->failure_cb(egc, ss, ss->rc); /* must be last */
-        return;
-    }
-    
-    if (ss->rc && ss->detaching)
-        LOG(WARN,"%s underlying machinery seemed to fail (%d),"
-            " but its function seems to have been successful",
-            ss->what, ss->rc);
-
-    assert(ss->detaching);
-    ss->detached_cb(egc, ss);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_flask.c b/tools/libxl/libxl_flask.c
deleted file mode 100644 (file)
index 38347a3..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *  Author: Machon Gregory, <mbgrego@tycho.ncsc.mil>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len,
-                               uint32_t *ssidref)
-{
-    int rc;
-
-    rc = xc_flask_context_to_sid(ctx->xch, buf, len, ssidref);
-
-    return rc;
-}
-
-int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref,
-                               char **buf, size_t *len)
-{
-    int rc;
-    char tmp[XC_PAGE_SIZE];
-
-    rc = xc_flask_sid_to_context(ctx->xch, ssidref, tmp, sizeof(tmp));
-
-    if (!rc) {
-        *len = strlen(tmp);
-        *buf = strdup(tmp);
-    }
-
-    return rc;
-}
-
-int libxl_flask_getenforce(libxl_ctx *ctx)
-{
-    int rc;
-
-    rc = xc_flask_getenforce(ctx->xch);
-
-    return rc;
-}
-
-int libxl_flask_setenforce(libxl_ctx *ctx, int mode)
-{
-    int rc;
-
-    rc = xc_flask_setenforce(ctx->xch, mode);
-
-    return rc;
-}
-
-int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size)
-{
-
-    int rc;
-
-    rc = xc_flask_load(ctx->xch, policy, size);
-
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_fork.c b/tools/libxl/libxl_fork.c
deleted file mode 100644 (file)
index 9a4709b..0000000
+++ /dev/null
@@ -1,734 +0,0 @@
-/*
- * Copyright (C) 2012      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-/*
- * Internal child process machinery for use by other parts of libxl
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*
- * carefd arrangements
- *
- * carefd_begin and _unlock take out the no_forking lock, which we
- * also take and release in our pthread_atfork handlers.  So when this
- * lock is held the whole process cannot fork.  We therefore protect
- * our fds from leaking into children made by other threads.
- *
- * We maintain a list of all the carefds, so that if the application
- * wants to fork a long-running but non-execing child, we can close
- * them all.
- *
- * So the record function sets CLOEXEC for the benefit of execing
- * children, and makes a note of the fd for the benefit of non-execing
- * ones.
- */
-
-struct libxl__carefd {
-    LIBXL_LIST_ENTRY(libxl__carefd) entry;
-    int fd;
-};
-
-static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER;
-static int atfork_registered;
-static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
-    LIBXL_LIST_HEAD_INITIALIZER(carefds);
-
-/* Protected against concurrency by no_forking.  sigchld_users is
- * protected against being interrupted by SIGCHLD (and thus read
- * asynchronously by the signal handler) by sigchld_defer (see
- * below). */
-static bool sigchld_installed; /* 0 means not */
-static pthread_mutex_t sigchld_defer_mutex = PTHREAD_MUTEX_INITIALIZER;
-static LIBXL_LIST_HEAD(, libxl_ctx) sigchld_users =
-    LIBXL_LIST_HEAD_INITIALIZER(sigchld_users);
-static struct sigaction sigchld_saved_action;
-
-static void sigchld_removehandler_core(void); /* idempotent */
-static void sigchld_user_remove(libxl_ctx *ctx); /* idempotent */
-static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old);
-
-static void defer_sigchld(void);
-static void release_sigchld(void);
-
-static void atfork_lock(void)
-{
-    int r = pthread_mutex_lock(&no_forking);
-    assert(!r);
-}
-
-static void atfork_unlock(void)
-{
-    int r = pthread_mutex_unlock(&no_forking);
-    assert(!r);
-}
-
-int libxl__atfork_init(libxl_ctx *ctx)
-{
-    int r, rc;
-    
-    atfork_lock();
-    if (atfork_registered) { rc = 0; goto out; }
-
-    r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock);
-    if (r) {
-        assert(r == ENOMEM);
-        libxl__alloc_failed(ctx, __func__, 0,0);
-    }
-
-    atfork_registered = 1;
-    rc = 0;
- out:
-    atfork_unlock();
-    return rc;
-}
-
-void libxl__carefd_begin(void) { atfork_lock(); }
-void libxl__carefd_unlock(void) { atfork_unlock(); }
-
-libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd)
-{
-    libxl__carefd *cf = 0;
-
-    libxl_fd_set_cloexec(ctx, fd, 1);
-    cf = libxl__zalloc(&ctx->nogc_gc, sizeof(*cf));
-    cf->fd = fd;
-    LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry);
-    return cf;
-}
-
-libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd)
-{
-    libxl__carefd *cf = 0;
-    int saved_errno = errno;
-
-    if (fd >= 0)
-        cf = libxl__carefd_record(ctx, fd);
-    libxl__carefd_unlock();
-    errno = saved_errno;
-    return cf;
-}
-
-void libxl_postfork_child_noexec(libxl_ctx *ctx)
-{
-    /*
-     * Anything running without the no_forking lock (atfork_lock)
-     * might be interrupted by fork.  But libxl functions other than
-     * this one are then forbidden to the child.
-     *
-     * Conversely, this function might interrupt any other libxl
-     * operation (even though that other operation has the libxl ctx
-     * lock).  We don't take the lock ourselves, since we are running
-     * in the child and if the lock is held the thread that took it no
-     * longer exists.  To prevent us being interrupted by another call
-     * to ourselves (whether in another thread or by virtue of another
-     * fork) we take the atfork lock ourselves.
-     */
-    libxl__carefd *cf, *cf_tmp;
-    int r;
-
-    atfork_lock();
-
-    LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) {
-        if (cf->fd >= 0) {
-            r = close(cf->fd);
-            if (r)
-                LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING,
-                                 "failed to close fd=%d"
-                                 " (perhaps of another libxl ctx)", cf->fd);
-        }
-        free(cf);
-    }
-    LIBXL_LIST_INIT(&carefds);
-
-    if (sigchld_installed) {
-        /* We are in theory not at risk of concurrent execution of the
-         * SIGCHLD handler, because the application should call
-         * libxl_postfork_child_noexec before the child forks again.
-         * (If the SIGCHLD was in flight in the parent at the time of
-         * the fork, the thread it was delivered on exists only in the
-         * parent so is not our concern.)
-         *
-         * But in case the application violated this rule (and did so
-         * while multithreaded in the child), we use our deferral
-         * machinery.  The result is that the SIGCHLD may then be lost
-         * (i.e. signaled to the now-defunct libxl ctx(s)).  But at
-         * least we won't execute undefined behaviour (by examining
-         * the list in the signal handler concurrently with clearing
-         * it here), and since we won't actually reap the new children
-         * things will in fact go OK if the application doesn't try to
-         * use SIGCHLD, but instead just waits for the child(ren). */
-        defer_sigchld();
-
-        LIBXL_LIST_INIT(&sigchld_users);
-        /* After this the ->sigchld_user_registered entries in the
-         * now-obsolete contexts may be lies.  But that's OK because
-         * no-one will look at them. */
-
-        release_sigchld();
-        sigchld_removehandler_core();
-    }
-
-    atfork_unlock();
-}
-
-int libxl__carefd_close(libxl__carefd *cf)
-{
-    if (!cf) return 0;
-    atfork_lock();
-    int r = cf->fd < 0 ? 0 : close(cf->fd);
-    int esave = errno;
-    LIBXL_LIST_REMOVE(cf, entry);
-    atfork_unlock();
-    free(cf);
-    errno = esave;
-    return r;
-}
-
-int libxl__carefd_fd(const libxl__carefd *cf)
-{
-    if (!cf) return -1;
-    return cf->fd;
-}
-
-/*
- * Low-level functions for child process handling, including
- * the main SIGCHLD handler.
- */
-
-/* Like waitpid(,,WNOHANG) but handles all errors except ECHILD. */
-static pid_t checked_waitpid(libxl__egc *egc, pid_t want, int *status)
-{
-    EGC_GC;
-    for (;;) {
-        pid_t got = waitpid(want, status, WNOHANG);
-        if (got != -1)
-            return got;
-        if (errno == ECHILD)
-            return got;
-        if (errno == EINTR)
-            continue;
-        LIBXL__EVENT_DISASTER(gc, "waitpid() failed", errno, 0);
-        return 0;
-    }
-}
-
-static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
-                                     int fd, short events, short revents);
-
-static void sigchld_handler(int signo)
-{
-    /* This function has to be reentrant!  Luckily it is. */
-
-    libxl_ctx *notify;
-    int esave = errno;
-
-    int r = pthread_mutex_lock(&sigchld_defer_mutex);
-    assert(!r);
-
-    LIBXL_LIST_FOREACH(notify, &sigchld_users, sigchld_users_entry) {
-        int e = libxl__self_pipe_wakeup(notify->sigchld_selfpipe[1]);
-        if (e) abort(); /* errors are probably EBADF, very bad */
-    }
-
-    r = pthread_mutex_unlock(&sigchld_defer_mutex);
-    assert(!r);
-
-    errno = esave;
-}
-
-static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old)
-{
-    struct sigaction ours;
-    int r;
-
-    memset(&ours,0,sizeof(ours));
-    ours.sa_handler = handler;
-    sigemptyset(&ours.sa_mask);
-    ours.sa_flags = SA_NOCLDSTOP | SA_RESTART;
-    r = sigaction(SIGCHLD, &ours, old);
-    assert(!r);
-}
-
-/*
- * SIGCHLD deferral
- *
- * sigchld_defer and sigchld_release are a bit like using sigprocmask
- * to block the signal only they work for the whole process.  Sadly
- * this has to be done by setting a special handler that records the
- * "pendingness" of the signal here in the program.  How tedious.
- *
- * A property of this approach is that the signal handler itself
- * must be reentrant (see the comment in release_sigchld).
- *
- * Callers have the atfork_lock so there is no risk of concurrency
- * within these functions, aside from the risk of being interrupted by
- * the signal.  We use sigchld_defer_mutex to guard against the
- * possibility of the real signal handler being still running on
- * another thread.
- */
-
-static volatile sig_atomic_t sigchld_occurred_while_deferred;
-
-static void sigchld_handler_when_deferred(int signo)
-{
-    sigchld_occurred_while_deferred = 1;
-}
-
-static void defer_sigchld(void)
-{
-    assert(sigchld_installed);
-
-    sigchld_sethandler_raw(sigchld_handler_when_deferred, 0);
-
-    /* Now _this thread_ cannot any longer be interrupted by the
-     * signal, so we can take the mutex without risk of deadlock.  If
-     * another thread is in the signal handler, either it or we will
-     * block and wait for the other. */
-
-    int r = pthread_mutex_lock(&sigchld_defer_mutex);
-    assert(!r);
-}
-
-static void release_sigchld(void)
-{
-    assert(sigchld_installed);
-
-    int r = pthread_mutex_unlock(&sigchld_defer_mutex);
-    assert(!r);
-
-    sigchld_sethandler_raw(sigchld_handler, 0);
-    if (sigchld_occurred_while_deferred) {
-        sigchld_occurred_while_deferred = 0;
-        /* We might get another SIGCHLD here, in which case
-         * sigchld_handler will be interrupted and re-entered.
-         * This is OK. */
-        sigchld_handler(SIGCHLD);
-    }
-}
-
-/*
- * Meat of the child process handling.
- */
-
-static void sigchld_removehandler_core(void) /* idempotent */
-{
-    struct sigaction was;
-    int r;
-    
-    if (!sigchld_installed)
-        return;
-
-    r = sigaction(SIGCHLD, &sigchld_saved_action, &was);
-    assert(!r);
-    assert(!(was.sa_flags & SA_SIGINFO));
-    assert(was.sa_handler == sigchld_handler);
-
-    sigchld_installed = 0;
-}
-
-static void sigchld_installhandler_core(void) /* idempotent */
-{
-    if (sigchld_installed)
-        return;
-
-    sigchld_installed = 1;
-
-    sigchld_sethandler_raw(sigchld_handler, &sigchld_saved_action);
-
-    assert(((void)"application must negotiate with libxl about SIGCHLD",
-            !(sigchld_saved_action.sa_flags & SA_SIGINFO) &&
-            (sigchld_saved_action.sa_handler == SIG_DFL ||
-             sigchld_saved_action.sa_handler == SIG_IGN)));
-}
-
-static void sigchld_user_remove(libxl_ctx *ctx) /* idempotent */
-{
-    if (!ctx->sigchld_user_registered)
-        return;
-
-    atfork_lock();
-    defer_sigchld();
-
-    LIBXL_LIST_REMOVE(ctx, sigchld_users_entry);
-
-    release_sigchld();
-
-    if (LIBXL_LIST_EMPTY(&sigchld_users))
-        sigchld_removehandler_core();
-
-    atfork_unlock();
-
-    ctx->sigchld_user_registered = 0;
-}
-
-void libxl__sigchld_notneeded(libxl__gc *gc) /* non-reentrant, idempotent */
-{
-    sigchld_user_remove(CTX);
-    libxl__ev_fd_deregister(gc, &CTX->sigchld_selfpipe_efd);
-}
-
-int libxl__sigchld_needed(libxl__gc *gc) /* non-reentrant, idempotent */
-{
-    int rc;
-
-    if (CTX->sigchld_selfpipe[0] < 0) {
-        rc = libxl__pipe_nonblock(CTX, CTX->sigchld_selfpipe);
-        if (rc) goto out;
-    }
-    if (!libxl__ev_fd_isregistered(&CTX->sigchld_selfpipe_efd)) {
-        rc = libxl__ev_fd_register(gc, &CTX->sigchld_selfpipe_efd,
-                                   sigchld_selfpipe_handler,
-                                   CTX->sigchld_selfpipe[0], POLLIN);
-        if (rc) goto out;
-    } else {
-        rc = libxl__ev_fd_modify(gc, &CTX->sigchld_selfpipe_efd, POLLIN);
-        if (rc) goto out;
-    }
-    if (!CTX->sigchld_user_registered) {
-        atfork_lock();
-
-        sigchld_installhandler_core();
-
-        defer_sigchld();
-
-        LIBXL_LIST_INSERT_HEAD(&sigchld_users, CTX, sigchld_users_entry);
-
-        release_sigchld();
-        atfork_unlock();
-
-        CTX->sigchld_user_registered = 1;
-    }
-
-    rc = 0;
- out:
-    return rc;
-}
-
-static bool chldmode_ours(libxl_ctx *ctx, bool creating)
-{
-    switch (ctx->childproc_hooks->chldowner) {
-    case libxl_sigchld_owner_libxl:
-        return creating || !LIBXL_LIST_EMPTY(&ctx->children);
-    case libxl_sigchld_owner_mainloop:
-        return 0;
-    case libxl_sigchld_owner_libxl_always:
-    case libxl_sigchld_owner_libxl_always_selective_reap:
-        return 1;
-    }
-    abort();
-}
-
-static void perhaps_sigchld_notneeded(libxl__gc *gc)
-{
-    if (!chldmode_ours(CTX, 0))
-        libxl__sigchld_notneeded(gc);
-}
-
-static int perhaps_sigchld_needed(libxl__gc *gc, bool creating)
-{
-    int rc;
-
-    if (chldmode_ours(CTX, creating)) {
-        rc = libxl__sigchld_needed(gc);
-        if (rc) return rc;
-    }
-    return 0;
-}
-
-static void childproc_reaped_ours(libxl__egc *egc, libxl__ev_child *ch,
-                                 int status)
-{
-    pid_t pid = ch->pid;
-    LIBXL_LIST_REMOVE(ch, entry);
-    ch->pid = -1;
-    ch->callback(egc, ch, pid, status);
-}
-
-static int childproc_reaped(libxl__egc *egc, pid_t pid, int status)
-{
-    EGC_GC;
-    libxl__ev_child *ch;
-
-    LIBXL_LIST_FOREACH(ch, &CTX->children, entry)
-        if (ch->pid == pid)
-            goto found;
-
-    /* not found */
-    return ERROR_UNKNOWN_CHILD;
-
- found:
-    childproc_reaped_ours(egc, ch, status);
-
-    perhaps_sigchld_notneeded(gc);
-
-    return 0;
-}
-
-int libxl_childproc_reaped(libxl_ctx *ctx, pid_t pid, int status)
-{
-    EGC_INIT(ctx);
-    CTX_LOCK;
-    assert(CTX->childproc_hooks->chldowner
-           == libxl_sigchld_owner_mainloop);
-    int rc = childproc_reaped(egc, pid, status);
-    CTX_UNLOCK_EGC_FREE;
-    return rc;
-}
-
-static void childproc_checkall(libxl__egc *egc)
-{
-    EGC_GC;
-    libxl__ev_child *ch;
-
-    for (;;) {
-        int status;
-        pid_t got;
-
-        LIBXL_LIST_FOREACH(ch, &CTX->children, entry) {
-            got = checked_waitpid(egc, ch->pid, &status);
-            if (got)
-                goto found;
-        }
-        /* not found */
-        return;
-
-    found:
-        if (got == -1) {
-            LIBXL__EVENT_DISASTER
-                (gc, "waitpid() gave ECHILD but we have a child",
-                 ECHILD, 0);
-            /* it must have finished but we don't know its status */
-            status = 255<<8; /* no wait.h macro for this! */
-            assert(WIFEXITED(status));
-            assert(WEXITSTATUS(status)==255);
-            assert(!WIFSIGNALED(status));
-            assert(!WIFSTOPPED(status));
-        }
-        childproc_reaped_ours(egc, ch, status);
-        /* we need to restart the loop, as children may have been edited */
-    }
-}
-
-void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
-{
-    EGC_INIT(ctx);
-    CTX_LOCK;
-    assert(CTX->childproc_hooks->chldowner
-           == libxl_sigchld_owner_mainloop);
-    childproc_checkall(egc);
-    CTX_UNLOCK_EGC_FREE;
-}
-
-static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
-                                     int fd, short events, short revents)
-{
-    /* May make callbacks into the application for child processes.
-     * So, this function may unlock and relock the CTX.  This is OK
-     * because event callback functions are always called with the CTX
-     * locked exactly once, and from code which copes with reentrancy.
-     * (See also the comment in afterpoll_internal.) */
-    EGC_GC;
-
-    int selfpipe = CTX->sigchld_selfpipe[0];
-
-    if (revents & ~POLLIN) {
-        LOG(ERROR, "unexpected poll event 0x%x on SIGCHLD self pipe", revents);
-        LIBXL__EVENT_DISASTER(gc,
-                              "unexpected poll event on SIGCHLD self pipe",
-                              0, 0);
-    }
-    assert(revents & POLLIN);
-
-    int e = libxl__self_pipe_eatall(selfpipe);
-    if (e) LIBXL__EVENT_DISASTER(gc, "read sigchld pipe", e, 0);
-
-    if (CTX->childproc_hooks->chldowner
-        == libxl_sigchld_owner_libxl_always_selective_reap) {
-        childproc_checkall(egc);
-        return;
-    }
-
-    while (chldmode_ours(CTX, 0) /* in case the app changes the mode */) {
-        int status;
-        pid_t pid = checked_waitpid(egc, -1, &status);
-
-        if (pid == 0 || pid == -1 /* ECHILD */)
-            return;
-
-        int rc = childproc_reaped(egc, pid, status);
-
-        if (rc) {
-            if (CTX->childproc_hooks->reaped_callback) {
-                CTX_UNLOCK;
-                rc = CTX->childproc_hooks->reaped_callback
-                        (pid, status, CTX->childproc_user);
-                CTX_LOCK;
-                if (rc != 0 && rc != ERROR_UNKNOWN_CHILD) {
-                    char disasterbuf[200];
-                    snprintf(disasterbuf, sizeof(disasterbuf), " reported by"
-                             " libxl_childproc_hooks->reaped_callback"
-                             " (for pid=%lu, status=%d; error code %d)",
-                             (unsigned long)pid, status, rc);
-                    LIBXL__EVENT_DISASTER(gc, disasterbuf, 0, 0);
-                    return;
-                }
-            } else {
-                rc = ERROR_UNKNOWN_CHILD;
-            }
-            if (rc)
-                libxl_report_child_exitstatus(CTX, XTL_WARN,
-                                "unknown child", (long)pid, status);
-        }
-    }
-}
-
-pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch,
-                           libxl__ev_child_callback *death)
-{
-    CTX_LOCK;
-    int rc;
-
-    perhaps_sigchld_needed(gc, 1);
-
-    pid_t pid =
-        CTX->childproc_hooks->fork_replacement
-        ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user)
-        : fork();
-    if (pid == -1) {
-        LOGE(ERROR, "fork failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (!pid) {
-        /* woohoo! */
-        if (CTX->xsh) {
-            xs_daemon_destroy_postfork(CTX->xsh);
-            CTX->xsh = NULL; /* turns mistakes into crashes */
-        }
-        /* Yes, CTX is left locked in the child. */
-        return 0;
-    }
-
-    ch->pid = pid;
-    ch->callback = death;
-    LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry);
-    rc = pid;
-
- out:
-    perhaps_sigchld_notneeded(gc);
-    CTX_UNLOCK;
-    return rc;
-}
-
-void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
-                             void *user)
-{
-    GC_INIT(ctx);
-    CTX_LOCK;
-
-    assert(LIBXL_LIST_EMPTY(&CTX->children));
-
-    if (!hooks)
-        hooks = &libxl__childproc_default_hooks;
-
-    ctx->childproc_hooks = hooks;
-    ctx->childproc_user = user;
-
-    perhaps_sigchld_notneeded(gc);
-    perhaps_sigchld_needed(gc, 0); /* idempotent, ok to ignore errors for now */
-
-    CTX_UNLOCK;
-    GC_FREE;
-}
-
-const libxl_childproc_hooks libxl__childproc_default_hooks = {
-    libxl_sigchld_owner_libxl, 0, 0
-};
-
-int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what) {
-    int rc;
-
-    assert(!CTX->xsh);
-    CTX->xsh = xs_daemon_open();
-    if (!CTX->xsh) {
-        LOGE(ERROR, "%s: xenstore reopen failed", what);
-        rc = ERROR_FAIL;  goto out;
-    }
-
-    libxl_fd_set_cloexec(CTX, xs_fileno(CTX->xsh), 1);
-
-    return 0;
-
- out:
-    return rc;
-}
-
-typedef struct ev_child_killed {
-    libxl__ao *ao;
-    libxl__ev_child ch;
-} ev_child_killed;
-static void deregistered_child_callback(libxl__egc *, libxl__ev_child *,
-                                        pid_t, int status);
-
-void libxl__ev_child_kill_deregister(libxl__ao *ao, libxl__ev_child *ch,
-                                     int sig)
-{
-    AO_GC;
-
-    if (!libxl__ev_child_inuse(ch))
-        return;
-
-    pid_t pid = ch->pid;
-
-    ev_child_killed *new_ch = GCNEW(new_ch);
-    new_ch->ao = ao;
-    new_ch->ch.pid = pid;
-    new_ch->ch.callback = deregistered_child_callback;
-    LIBXL_LIST_INSERT_HEAD(&CTX->children, &new_ch->ch, entry);
-    ao->outstanding_killed_child++;
-
-    LIBXL_LIST_REMOVE(ch, entry);
-    ch->pid = -1;
-    int r = kill(pid, sig);
-    if (r)
-        LOGED(ERROR, ao->domid,
-              "failed to kill child [%ld] with signal %d",
-             (unsigned long)pid, sig);
-}
-
-static void deregistered_child_callback(libxl__egc *egc,
-                                        libxl__ev_child *ch,
-                                        pid_t pid,
-                                        int status)
-{
-    ev_child_killed *ck = CONTAINER_OF(ch, *ck, ch);
-    EGC_GC;
-
-    libxl_report_child_exitstatus(CTX, XTL_ERROR,
-                                  "killed fork (dying as expected)",
-                                  pid, status);
-    ck->ao->outstanding_killed_child--;
-    libxl__ao_complete_check_progress_reports(egc, ck->ao);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_freebsd.c b/tools/libxl/libxl_freebsd.c
deleted file mode 100644 (file)
index f7ef4a8..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2014
- * Author Roger Pau Monne <roger.pau@entel.upc.edu>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__try_phy_backend(mode_t st_mode)
-{
-    if (S_ISREG(st_mode) || S_ISBLK(st_mode) || S_ISCHR(st_mode))
-        return 1;
-
-    return 0;
-}
-
-char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
-{
-    /* This translation table has been copied from the FreeBSD blkfront code. */
-    const static struct vdev_info {
-        int major;
-        int shift;
-        int base;
-        const char *name;
-    } info[] = {
-        {3,     6,  0,      "ada"}, /* ide0 */
-        {22,    6,  2,      "ada"}, /* ide1 */
-        {33,    6,  4,      "ada"}, /* ide2 */
-        {34,    6,  6,      "ada"}, /* ide3 */
-        {56,    6,  8,      "ada"}, /* ide4 */
-        {57,    6,  10,     "ada"}, /* ide5 */
-        {88,    6,  12,     "ada"}, /* ide6 */
-        {89,    6,  14,     "ada"}, /* ide7 */
-        {90,    6,  16,     "ada"}, /* ide8 */
-        {91,    6,  18,     "ada"}, /* ide9 */
-
-        {8,     4,  0,      "da"},  /* scsi disk0 */
-        {65,    4,  16,     "da"},  /* scsi disk1 */
-        {66,    4,  32,     "da"},  /* scsi disk2 */
-        {67,    4,  48,     "da"},  /* scsi disk3 */
-        {68,    4,  64,     "da"},  /* scsi disk4 */
-        {69,    4,  80,     "da"},  /* scsi disk5 */
-        {70,    4,  96,     "da"},  /* scsi disk6 */
-        {71,    4,  112,    "da"},  /* scsi disk7 */
-        {128,   4,  128,    "da"},  /* scsi disk8 */
-        {129,   4,  144,    "da"},  /* scsi disk9 */
-        {130,   4,  160,    "da"},  /* scsi disk10 */
-        {131,   4,  176,    "da"},  /* scsi disk11 */
-        {132,   4,  192,    "da"},  /* scsi disk12 */
-        {133,   4,  208,    "da"},  /* scsi disk13 */
-        {134,   4,  224,    "da"},  /* scsi disk14 */
-        {135,   4,  240,    "da"},  /* scsi disk15 */
-
-        {202,   4,  0,      "xbd"}, /* xbd */
-
-        {0, 0,  0,  NULL},
-    };
-    int major = devid >> 8;
-    int minor = devid & 0xff;
-    int i;
-
-    if (devid & (1 << 28))
-        return GCSPRINTF("%s%d", "xbd", (devid & ((1 << 28) - 1)) >> 8);
-
-    for (i = 0; info[i].major; i++)
-        if (info[i].major == major)
-            return GCSPRINTF("%s%d", info[i].name,
-                             info[i].base + (minor >> info[i].shift));
-
-    return GCSPRINTF("%s%d", "xbd", minor >> 4);
-}
-
-/* Hotplug scripts caller functions */
-static int libxl__hotplug_env_nic(libxl__gc *gc, libxl__device *dev, char ***env,
-                                  int num_exec)
-{
-    int nr = 0;
-    const int arraysize = 5;
-    libxl_nic_type type;
-
-    assert(dev->backend_kind == LIBXL__DEVICE_KIND_VIF);
-
-    /*
-     * On the first pass the PV interface is added to the bridge,
-     * on the second pass the tap interface will also be added.
-     */
-    type = num_exec == 0 ? LIBXL_NIC_TYPE_VIF : LIBXL_NIC_TYPE_VIF_IOEMU;
-
-    GCNEW_ARRAY(*env, arraysize);
-    (*env)[nr++] = "iface_dev";
-    (*env)[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
-                                                      dev->devid, type);
-    (*env)[nr++] = "emulated";
-    (*env)[nr++] = type == LIBXL_NIC_TYPE_VIF_IOEMU ? "1" : "0";
-    (*env)[nr++] = NULL;
-    assert(nr == arraysize);
-
-    return 0;
-}
-
-static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
-                              char ***args, char ***env,
-                              libxl__device_action action,
-                              int num_exec)
-{
-    libxl_nic_type nictype;
-    char *be_path = libxl__device_backend_path(gc, dev);
-    char *script;
-    int nr = 0, rc;
-
-    rc = libxl__nic_type(gc, dev, &nictype);
-    if (rc) {
-        LOGD(ERROR, dev->domid, "error when fetching nic type");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /*
-     * For PV domains only one pass is needed (because there's no emulated
-     * interface). For HVM domains two passes are needed in order to add
-     * both the PV and the tap interfaces to the bridge.
-     */
-    if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
-        rc = 0;
-        goto out;
-    }
-
-    rc = libxl__hotplug_env_nic(gc, dev, env, num_exec);
-    if (rc)
-        goto out;
-
-    script = libxl__xs_read(gc, XBT_NULL,
-                            GCSPRINTF("%s/%s", be_path, "script"));
-    if (!script) {
-        LOGEVD(ERROR, errno, dev->domid,
-               "unable to read script from %s", be_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    const int arraysize = 4;
-    GCNEW_ARRAY(*args, arraysize);
-    (*args)[nr++] = script;
-    (*args)[nr++] = be_path;
-    (*args)[nr++] = (char *) libxl__device_action_to_string(action);
-    (*args)[nr++] = NULL;
-    assert(nr == arraysize);
-    rc = 1;
-
-out:
-    return rc;
-}
-
-static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
-                               char ***args, char ***env,
-                               libxl__device_action action)
-{
-    char *be_path = libxl__device_backend_path(gc, dev);
-    char *script;
-    int nr = 0, rc;
-
-    script = libxl__xs_read(gc, XBT_NULL,
-                            GCSPRINTF("%s/%s", be_path, "script"));
-    if (!script) {
-        LOGEVD(ERROR, errno, dev->domid,
-               "unable to read script from %s", be_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    const int arraysize = 4;
-    GCNEW_ARRAY(*args, arraysize);
-    (*args)[nr++] = script;
-    (*args)[nr++] = be_path;
-    (*args)[nr++] = (char *) libxl__device_action_to_string(action);
-    (*args)[nr++] = NULL;
-    assert(nr == arraysize);
-    rc = 1;
-
-out:
-    return rc;
-}
-
-int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
-                                   char ***args, char ***env,
-                                   libxl__device_action action,
-                                   int num_exec)
-{
-    int rc;
-
-    switch (dev->backend_kind) {
-    case LIBXL__DEVICE_KIND_VIF:
-        /*
-         * If domain has a stubdom we don't have to execute hotplug scripts
-         * for emulated interfaces
-         */
-        if ((num_exec > 1) ||
-            (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
-            rc = 0;
-            goto out;
-        }
-        rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
-        break;
-    case LIBXL__DEVICE_KIND_VBD:
-        if (num_exec != 0) {
-            rc = 0;
-            goto out;
-        }
-        rc = libxl__hotplug_disk(gc, dev, args, env, action);
-        break;
-    default:
-        /* No need to execute any hotplug scripts */
-        rc = 0;
-        break;
-    }
-
-out:
-    return rc;
-}
-
-libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
-{
-    return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-int libxl__pci_numdevs(libxl__gc *gc)
-{
-    return ERROR_NI;
-}
-
-int libxl__pci_topology_init(libxl__gc *gc,
-                             physdev_pci_device_t *devs,
-                             int num_devs)
-{
-    return ERROR_NI;
-}
-
-int libxl__local_dm_preexec_restrict(libxl__gc *gc)
-{
-    return 0;
-}
diff --git a/tools/libxl/libxl_genid.c b/tools/libxl/libxl_genid.c
deleted file mode 100644 (file)
index 7f52356..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2014 Citrix Systems R&D Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <xenctrl.h>
-#include <xen/hvm/params.h>
-
-/*
- * Generate a random VM generation ID.
- *
- * Returns ERROR_FAIL if a suitable source of random numbers is not
- * available.
- *
- * See Microsoft's "Virtual Machine Generation ID" specification for
- * further details, including when a new generation ID is required.
- *
- *   http://www.microsoft.com/en-us/download/details.aspx?id=30707
- */
-int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id)
-{
-    GC_INIT(ctx);
-    int ret;
-
-    ret = libxl__random_bytes(gc, id->bytes, LIBXL_MS_VM_GENID_LEN);
-
-    GC_FREE;
-    return ret;
-}
-
-/*
- * Is this VM generation ID all zeros?
- */
-bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id)
-{
-    static const libxl_ms_vm_genid zero;
-
-    return memcmp(id->bytes, zero.bytes, LIBXL_MS_VM_GENID_LEN) == 0;
-}
-
-void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst,
-                            const libxl_ms_vm_genid *src)
-{
-    memcpy(dst, src, LIBXL_MS_VM_GENID_LEN);
-}
-
-int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
-                           const libxl_ms_vm_genid *id)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    const char *dom_path;
-    uint64_t genid[2];
-    uint64_t paddr = 0;
-    int rc;
-
-    memcpy(genid, id->bytes, LIBXL_MS_VM_GENID_LEN);
-
-    /*
-     * Set the "platform/generation-id" XenStore key to pass the ID to
-     * hvmloader.
-     */
-    dom_path = libxl__xs_get_dompath(gc, domid);
-    if (!dom_path) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = libxl__xs_printf(gc, XBT_NULL,
-                         GCSPRINTF("%s/platform/generation-id", dom_path),
-                         "%"PRIu64 ":%" PRIu64, genid[0], genid[1]);
-    if (rc < 0)
-        goto out;
-
-    /*
-     * Update the ID in guest memory (if available).
-     */
-    xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_VM_GENERATION_ID_ADDR, &paddr);
-    if (paddr) {
-        void *vaddr;
-
-        vaddr = xc_map_foreign_range(ctx->xch, domid, XC_PAGE_SIZE,
-                                     PROT_READ | PROT_WRITE,
-                                     paddr >> XC_PAGE_SHIFT);
-        if (vaddr == NULL) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        memcpy(vaddr + (paddr & ~XC_PAGE_MASK), genid, 2 * sizeof(*genid));
-        munmap(vaddr, XC_PAGE_SIZE);
-
-        /*
-         * The spec requires an ACPI Notify event is injected into the
-         * guest when the generation ID is changed.
-         *
-         * This is only called for domains that are suspended or newly
-         * created and they won't be in a state to receive such an
-         * event.
-         */
-    }
-
-    rc = 0;
-
-  out:
-    return rc;
-}
diff --git a/tools/libxl/libxl_internal.c b/tools/libxl/libxl_internal.c
deleted file mode 100644 (file)
index d93a755..0000000
+++ /dev/null
@@ -1,806 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-void libxl__alloc_failed(libxl_ctx *ctx, const char *func,
-                         size_t nmemb, size_t size) {
-#define M "libxl: FATAL ERROR: memory allocation failure"
-#define M_SIZE M " (%s, %lu x %lu)\n"
-#define M_NSIZE M " (%s)\n"
-    if (size) {
-       libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
-                  M_SIZE, func, (unsigned long)nmemb, (unsigned long)size);
-       fprintf(stderr, M_SIZE, func, (unsigned long)nmemb,
-               (unsigned long)size);
-    } else {
-       libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
-                  M_NSIZE, func);
-       fprintf(stderr, M_NSIZE, func);
-
-    }
-
-    fflush(stderr);
-    _exit(-1);
-#undef M_NSIZE
-#undef M_SIZE
-#undef M
-}
-
-void libxl__ptr_add(libxl__gc *gc, void *ptr)
-{
-    int i;
-
-    if (!libxl__gc_is_real(gc))
-        return;
-
-    if (!ptr)
-        return;
-
-    /* fast case: we have space in the array for storing the pointer */
-    for (i = 0; i < gc->alloc_maxsize; i++) {
-        if (!gc->alloc_ptrs[i]) {
-            gc->alloc_ptrs[i] = ptr;
-            return;
-        }
-    }
-    int new_maxsize = gc->alloc_maxsize * 2 + 25;
-    assert(new_maxsize < INT_MAX / sizeof(void*) / 2);
-    gc->alloc_ptrs = realloc(gc->alloc_ptrs, new_maxsize * sizeof(void *));
-    if (!gc->alloc_ptrs)
-        libxl__alloc_failed(CTX, __func__, new_maxsize, sizeof(void*));
-
-    gc->alloc_ptrs[gc->alloc_maxsize++] = ptr;
-
-    while (gc->alloc_maxsize < new_maxsize)
-        gc->alloc_ptrs[gc->alloc_maxsize++] = 0;
-
-    return;
-}
-
-void libxl__free_all(libxl__gc *gc)
-{
-    void *ptr;
-    int i;
-
-    assert(libxl__gc_is_real(gc));
-
-    for (i = 0; i < gc->alloc_maxsize; i++) {
-        ptr = gc->alloc_ptrs[i];
-        gc->alloc_ptrs[i] = NULL;
-        free(ptr);
-    }
-    free(gc->alloc_ptrs);
-    gc->alloc_ptrs = 0;
-    gc->alloc_maxsize = 0;
-}
-
-void *libxl__malloc(libxl__gc *gc, size_t size)
-{
-    void *ptr = malloc(size);
-    if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
-
-    libxl__ptr_add(gc, ptr);
-    return ptr;
-}
-
-void *libxl__zalloc(libxl__gc *gc, size_t size)
-{
-    void *ptr = calloc(size, 1);
-    if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
-
-    libxl__ptr_add(gc, ptr);
-    return ptr;
-}
-
-void *libxl__calloc(libxl__gc *gc, size_t nmemb, size_t size)
-{
-    void *ptr = calloc(nmemb, size);
-    if (!ptr) libxl__alloc_failed(CTX, __func__, nmemb, size);
-
-    libxl__ptr_add(gc, ptr);
-    return ptr;
-}
-
-void *libxl__realloc(libxl__gc *gc, void *ptr, size_t new_size)
-{
-    void *new_ptr = realloc(ptr, new_size);
-    int i = 0;
-
-    if (new_ptr == NULL && new_size != 0)
-        libxl__alloc_failed(CTX, __func__, new_size, 1);
-
-    if (ptr == NULL) {
-        libxl__ptr_add(gc, new_ptr);
-    } else if (new_ptr != ptr && libxl__gc_is_real(gc)) {
-        for (i = 0; ; i++) {
-            assert(i < gc->alloc_maxsize);
-            if (gc->alloc_ptrs[i] == ptr) {
-                gc->alloc_ptrs[i] = new_ptr;
-                break;
-            }
-        }
-    }
-
-    return new_ptr;
-}
-
-char *libxl__vsprintf(libxl__gc *gc, const char *fmt, va_list ap)
-{
-    char *s;
-    va_list aq;
-    int ret;
-
-    va_copy(aq, ap);
-    ret = vsnprintf(NULL, 0, fmt, aq);
-    va_end(aq);
-
-    assert(ret >= 0);
-
-    s = libxl__zalloc(gc, ret + 1);
-    va_copy(aq, ap);
-    ret = vsnprintf(s, ret + 1, fmt, aq);
-    va_end(aq);
-
-    return s;
-}
-
-char *libxl__sprintf(libxl__gc *gc, const char *fmt, ...)
-{
-    char *s;
-    va_list ap;
-
-    va_start(ap, fmt);
-    s = libxl__vsprintf(gc, fmt, ap);
-    va_end(ap);
-
-    return s;
-}
-
-char *libxl__strdup(libxl__gc *gc, const char *c)
-{
-    char *s;
-
-    if (!c) return NULL;
-
-    s = strdup(c);
-
-    if (!s) libxl__alloc_failed(CTX, __func__, strlen(c), 1);
-
-    libxl__ptr_add(gc, s);
-
-    return s;
-}
-
-char *libxl__strndup(libxl__gc *gc, const char *c, size_t n)
-{
-    char *s;
-
-    if (!c) return NULL;
-
-    s = strndup(c, n);
-
-    if (!s) libxl__alloc_failed(CTX, __func__, n, 1);
-
-    libxl__ptr_add(gc, s);
-
-    return s;
-}
-
-char *libxl__dirname(libxl__gc *gc, const char *s)
-{
-    char *c = strrchr(s, '/');
-
-    if (!c)
-        return NULL;
-
-    return libxl__strndup(gc, s, c - s);
-}
-
-void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
-             const char *file, int line, const char *func,
-             uint32_t domid, const char *fmt, va_list ap)
-{
-    /* WARNING this function may not call any libxl-provided
-     * memory allocation function, as those may
-     * call libxl__alloc_failed which calls libxl__logv. */
-    char *enomem = "[out of memory formatting log message]";
-    char *base = NULL;
-    int rc, esave;
-    char fileline[256];
-    char domain[256];
-
-    esave = errno;
-
-    rc = vasprintf(&base, fmt, ap);
-    if (rc<0) { base = enomem; goto x; }
-
-    fileline[0] = 0;
-    if (file) snprintf(fileline, sizeof(fileline), "%s:%d",file,line);
-    fileline[sizeof(fileline)-1] = 0;
-
-    domain[0] = 0;
-    if (libxl_domid_valid_guest(domid))
-        snprintf(domain, sizeof(domain), "Domain %"PRIu32":", domid);
- x:
-    xtl_log(ctx->lg, msglevel, errnoval, "libxl",
-            "%s%s%s%s%s" "%s",
-            fileline, func&&file?":":"", func?func:"", func||file?": ":"",
-            domain, base);
-    if (base != enomem) free(base);
-    errno = esave;
-}
-
-void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
-            const char *file, int line, const char *func,
-            uint32_t domid, const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    libxl__logv(ctx, msglevel, errnoval, file, line, func, domid, fmt, ap);
-    va_end(ap);
-}
-
-char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path)
-{
-    if (s[0] == '/') return libxl__strdup(gc, s);
-    return GCSPRINTF("%s/%s", path, s);
-}
-
-
-int libxl__file_reference_map(libxl__file_reference *f)
-{
-    struct stat st_buf;
-    int ret, fd;
-    void *data;
-
-    if (f->mapped)
-        return 0;
-
-    fd = open(f->path, O_RDONLY);
-    if (fd < 0)
-        return ERROR_FAIL;
-
-    ret = fstat(fd, &st_buf);
-    if (ret < 0)
-        goto out;
-
-    ret = -1;
-    data = mmap(NULL, st_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-    if (data == MAP_FAILED)
-        goto out;
-
-    f->mapped = 1;
-    f->data = data;
-    f->size = st_buf.st_size;
-
-    ret = 0;
-out:
-    close(fd);
-
-    return ret == 0 ? 0 : ERROR_FAIL;
-}
-
-int libxl__file_reference_unmap(libxl__file_reference *f)
-{
-    int ret;
-
-    if (!f->mapped)
-        return 0;
-
-    ret = munmap(f->data, f->size);
-    if (ret == 0) {
-        f->mapped = 0;
-        f->data = NULL;
-        f->size = 0;
-        return 0;
-    }
-
-    return ERROR_FAIL;
-}
-
-_hidden int libxl__parse_mac(const char *s, libxl_mac mac)
-{
-    const char *tok;
-    char *endptr;
-    int i;
-
-    for (i = 0, tok = s; *tok && (i < 6); ++i, tok = endptr) {
-        mac[i] = strtol(tok, &endptr, 16);
-        if (endptr != (tok + 2) || (*endptr != '\0' && *endptr != ':') )
-            return ERROR_INVAL;
-        if (*endptr == ':')
-            endptr++;
-    }
-    if ( i != 6 )
-        return ERROR_INVAL;
-
-    return 0;
-}
-
-_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b)
-{
-    int i;
-
-    for (i = 0; i<6; i++) {
-        if ((*a)[i] != (*b)[i])
-            return (*a)[i] - (*b)[i];
-    }
-
-    return 0;
-}
-
-_hidden int libxl__mac_is_default(libxl_mac *mac)
-{
-    return (!(*mac)[0] && !(*mac)[1] && !(*mac)[2] &&
-            !(*mac)[3] && !(*mac)[4] && !(*mac)[5]);
-}
-
-_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock)
-{
-    GC_INIT(ctx);
-    pthread_mutexattr_t attr;
-    int rc = 0;
-
-    if (pthread_mutexattr_init(&attr) != 0) {
-        LOGE(ERROR, "Failed to init mutex attributes");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
-        LOGE(ERROR, "Failed to set mutex attributes");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (pthread_mutex_init(lock, &attr) != 0) {
-        LOGE(ERROR, "Failed to init mutex");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-out:
-    pthread_mutexattr_destroy(&attr);
-    GC_FREE;
-    return rc;
-}
-
-int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid)
-{
-    char *path = NULL;
-    char *dm_version = NULL;
-    libxl_device_model_version value;
-
-    path = libxl__xs_libxl_path(gc, domid);
-    path = GCSPRINTF("%s/dm-version", path);
-    dm_version = libxl__xs_read(gc, XBT_NULL, path);
-    if (!dm_version) {
-        return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
-    }
-
-    if (libxl_device_model_version_from_string(dm_version, &value) < 0) {
-        LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version);
-        return -1;
-    }
-    return value;
-}
-
-/* Portability note: this lock utilises flock(2) so a proper implementation of
- * flock(2) is required.
- */
-libxl__flock *libxl__lock_file(libxl__gc *gc, const char *lockfile)
-{
-    libxl__flock *lock;
-    int fd;
-    struct stat stab, fstab;
-
-    lock = libxl__zalloc(NOGC, sizeof(libxl__flock));
-    lock->path = libxl__strdup(NOGC, lockfile);
-
-    while (true) {
-        libxl__carefd_begin();
-        fd = open(lockfile, O_RDWR|O_CREAT, 0666);
-        if (fd < 0)
-            LOGE(ERROR,
-                 "cannot open lockfile %s, errno=%d",
-                 lockfile, errno);
-        lock->carefd = libxl__carefd_opened(CTX, fd);
-        if (fd < 0) goto out;
-
-        /* Lock the file in exclusive mode, wait indefinitely to
-         * acquire the lock
-         */
-        while (flock(fd, LOCK_EX)) {
-            switch (errno) {
-            case EINTR:
-                /* Signal received, retry */
-                continue;
-            default:
-                /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
-                LOGE(ERROR,
-                     "unexpected error while trying to lock %s, fd=%d, errno=%d",
-                      lockfile, fd, errno);
-                goto out;
-            }
-        }
-
-        if (fstat(fd, &fstab)) {
-            LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d",
-                  lockfile, fd, errno);
-            goto out;
-        }
-        if (stat(lockfile, &stab)) {
-            if (errno != ENOENT) {
-                LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno);
-                goto out;
-            }
-        } else {
-            if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
-                break;
-        }
-
-        libxl__carefd_close(lock->carefd);
-    }
-
-    return lock;
-
-out:
-    if (lock) libxl__unlock_file(lock);
-    return NULL;
-}
-
-void libxl__unlock_file(libxl__flock *lock)
-{
-    /* It's important to unlink the file before closing fd to avoid
-     * the following race (if close before unlink):
-     *
-     *   P1 LOCK                         P2 UNLOCK
-     *   fd1 = open(lockfile)
-     *                                   close(fd2)
-     *   flock(fd1)
-     *   fstat and stat check success
-     *                                   unlink(lockfile)
-     *   return lock
-     *
-     * In above case P1 thinks it has got hold of the lock but
-     * actually lock is released by P2 (lockfile unlinked).
-     */
-    if (lock->path) unlink(lock->path);
-    if (lock->carefd) libxl__carefd_close(lock->carefd);
-    free(lock->path);
-    free(lock);
-}
-
-libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid)
-{
-    const char *lockfile;
-    libxl__flock *lock;
-
-    lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
-    if (!lockfile) return NULL;
-
-    lock = libxl__lock_file(gc, lockfile);
-
-    /* Check the domain is still there, if not we should release the
-     * lock and clean up.
-     */
-    if (libxl_domain_info(CTX, NULL, domid)) {
-        libxl__unlock_file(lock);
-        return NULL;
-    }
-
-    return lock;
-}
-
-libxl__flock *libxl__lock_domid_history(libxl__gc *gc)
-{
-    const char *lockfile;
-
-    lockfile = libxl__domid_history_path(gc, ".lock");
-    if (!lockfile) return NULL;
-
-    return libxl__lock_file(gc, lockfile);
-}
-
-int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
-                                    libxl_domain_config *d_config)
-{
-    uint8_t *data = NULL;
-    int rc, len;
-
-    rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len);
-    if (rc) {
-        LOGEVD(ERROR, rc, domid,
-              "failed to retrieve domain configuration");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (len == 0) {
-        /* No logging, not necessary an error from caller's PoV. */
-        rc = ERROR_JSON_CONFIG_EMPTY;
-        goto out;
-    }
-    rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data);
-
-out:
-    free(data);
-    return rc;
-}
-
-int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
-                                    libxl_domain_config *d_config)
-{
-    char *d_config_json;
-    int rc;
-
-    d_config_json = libxl_domain_config_to_json(CTX, d_config);
-    if (!d_config_json) {
-        LOGED(ERROR, domid,
-              "failed to convert domain configuration to JSON");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__userdata_store(gc, domid, "libxl-json",
-                               (const uint8_t *)d_config_json,
-                               strlen(d_config_json) + 1 /* include '\0' */);
-    if (rc) {
-        LOGEVD(ERROR, rc, domid, "failed to store domain configuration");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-out:
-    free(d_config_json);
-    return rc;
-}
-
-void libxl__update_domain_configuration(libxl__gc *gc,
-                                        libxl_domain_config *dst,
-                                        const libxl_domain_config *src)
-{
-    int i, idx, num;
-    const libxl__device_type *dt;
-
-    for (idx = 0;; idx++) {
-        dt = device_type_tbl[idx];
-        if (!dt)
-            break;
-
-        num = *libxl__device_type_get_num(dt, src);
-        if (!dt->update_config || !num)
-            continue;
-
-        for (i = 0; i < num; i++)
-            dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i),
-                                  libxl__device_type_get_elem(dt, src, i));
-    }
-
-    /* update guest UUID */
-    libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid);
-
-    /* video ram */
-    dst->b_info.video_memkb = src->b_info.video_memkb;
-}
-
-static void ev_slowlock_init_internal(libxl__ev_slowlock *lock,
-                                      const char *userdata_userid)
-{
-    libxl__ev_child_init(&lock->child);
-    lock->userdata_userid = userdata_userid;
-    lock->path = NULL;
-    lock->fd = -1;
-    lock->held = false;
-}
-
-void libxl__ev_devlock_init(libxl__ev_slowlock *lock)
-{
-    ev_slowlock_init_internal(lock, "libxl-device-changes-lock");
-}
-
-void libxl__ev_qmplock_init(libxl__ev_slowlock *lock)
-{
-    ev_slowlock_init_internal(lock, "qmp-socket-lock");
-}
-
-static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock);
-static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
-                                   pid_t pid, int status);
-
-void libxl__ev_slowlock_lock(libxl__egc *egc, libxl__ev_slowlock *lock)
-{
-    STATE_AO_GC(lock->ao);
-    const char *lockfile;
-
-    lockfile = libxl__userdata_path(gc, lock->domid,
-                                    lock->userdata_userid, "l");
-    if (!lockfile) goto out;
-    lock->path = libxl__strdup(NOGC, lockfile);
-
-    ev_lock_prepare_fork(egc, lock);
-    return;
-out:
-    lock->callback(egc, lock, ERROR_LOCK_FAIL);
-}
-
-static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock)
-{
-    STATE_AO_GC(lock->ao);
-    pid_t pid;
-    int fd;
-
-    /* Convenience aliases */
-    libxl_domid domid = lock->domid;
-    const char *lockfile = lock->path;
-
-    lock->fd = open(lockfile, O_RDWR|O_CREAT, 0666);
-    if (lock->fd < 0) {
-        LOGED(ERROR, domid, "cannot open lockfile %s", lockfile);
-        goto out;
-    }
-    fd = lock->fd;
-
-    /* Enable this optimisation only in releases, so the fork code is
-     * exercised while libxl is built with debug=y. */
-#ifndef CONFIG_DEBUG
-    /*
-     * We try to grab the lock before forking as it is likely to be free.
-     * Even though we are supposed to CTX_UNLOCK before attempting to grab
-     * the ev_lock, it is fine to do a non-blocking request now with the
-     * CTX_LOCK held as if that fails we'll try again in a fork (CTX_UNLOCK
-     * will be called in libxl), that will avoid deadlocks.
-     */
-    int r = flock(fd, LOCK_EX | LOCK_NB);
-    if (!r) {
-        libxl_fd_set_cloexec(CTX, fd, 1);
-        /* We held a lock, no need to fork but we need to check it. */
-        ev_lock_child_callback(egc, &lock->child, 0, 0);
-        return;
-    }
-#endif
-
-    pid = libxl__ev_child_fork(gc, &lock->child, ev_lock_child_callback);
-    if (pid < 0)
-        goto out;
-    if (!pid) {
-        /* child */
-        int exit_val = 0;
-
-        /* Lock the file in exclusive mode, wait indefinitely to
-         * acquire the lock */
-        while (flock(fd, LOCK_EX)) {
-            switch (errno) {
-            case EINTR:
-                /* Signal received, retry */
-                continue;
-            default:
-                /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
-                LOGED(ERROR, domid,
-                      "unexpected error while trying to lock %s, fd=%d",
-                      lockfile, fd);
-                exit_val = 1;
-                break;
-            }
-        }
-        _exit(exit_val);
-    }
-
-    /* Now that the child has the fd, set cloexec in the parent to prevent
-     * more leakage than necessary */
-    libxl_fd_set_cloexec(CTX, fd, 1);
-    return;
-out:
-    libxl__ev_slowlock_unlock(gc, lock);
-    lock->callback(egc, lock, ERROR_LOCK_FAIL);
-}
-
-static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
-                                   pid_t pid, int status)
-{
-    EGC_GC;
-    libxl__ev_slowlock *lock = CONTAINER_OF(child, *lock, child);
-    struct stat stab, fstab;
-    int rc = ERROR_LOCK_FAIL;
-
-    /* Convenience aliases */
-    int fd = lock->fd;
-    const char *lockfile = lock->path;
-    libxl_domid domid = lock->domid;
-
-    if (status) {
-        libxl_report_child_exitstatus(CTX, XTL_ERROR, "flock child",
-                                      pid, status);
-        goto out;
-    }
-
-    if (fstat(fd, &fstab)) {
-        LOGED(ERROR, domid, "cannot fstat %s, fd=%d", lockfile, fd);
-        goto out;
-    }
-    if (stat(lockfile, &stab)) {
-        if (errno != ENOENT) {
-            LOGED(ERROR, domid, "cannot stat %s", lockfile);
-            goto out;
-        }
-    } else {
-        if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) {
-            /* We held the lock */
-            lock->held = true;
-            rc = 0;
-            goto out;
-        }
-    }
-
-    /* We didn't grab the lock, let's try again */
-    flock(lock->fd, LOCK_UN);
-    close(lock->fd);
-    lock->fd = -1;
-    ev_lock_prepare_fork(egc, lock);
-    return;
-
-out:
-    if (lock->held) {
-        /* Check the domain is still there, if not we should release the
-         * lock and clean up.  */
-        if (libxl_domain_info(CTX, NULL, domid))
-            rc = ERROR_LOCK_FAIL;
-    }
-    if (rc) {
-        LOGD(ERROR, domid, "Failed to grab lock for %s",
-             lock->userdata_userid);
-        libxl__ev_slowlock_unlock(gc, lock);
-    }
-    lock->callback(egc, lock, rc);
-}
-
-void libxl__ev_slowlock_unlock(libxl__gc *gc, libxl__ev_slowlock *lock)
-{
-    int r;
-
-    assert(!libxl__ev_child_inuse(&lock->child));
-
-    /* See the rationale in libxl__unlock_domain_userdata()
-     * about why we do unlink() before unlock(). */
-
-    if (lock->path && lock->held)
-        unlink(lock->path);
-
-    if (lock->fd >= 0) {
-        /* We need to call unlock as the fd may have leaked into other
-         * processes */
-        r = flock(lock->fd, LOCK_UN);
-        if (r)
-            LOGED(ERROR, lock->domid, "failed to unlock fd=%d, path=%s",
-                  lock->fd, lock->path);
-        close(lock->fd);
-    }
-    free(lock->path);
-    ev_slowlock_init_internal(lock, lock->userdata_userid);
-}
-
-void libxl__ev_slowlock_dispose(libxl__gc *gc, libxl__ev_slowlock *lock)
-{
-    libxl__ev_child_kill_deregister(lock->ao, &lock->child, SIGKILL);
-    libxl__ev_slowlock_unlock(gc, lock);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
deleted file mode 100644 (file)
index 1fcf85c..0000000
+++ /dev/null
@@ -1,4859 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef LIBXL_INTERNAL_H
-#define LIBXL_INTERNAL_H
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "xentoolcore_internal.h"
-
-#include "libxl_sr_stream_format.h"
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-
-#include <sys/mman.h>
-#include <poll.h>
-#include <sys/select.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/file.h>
-#include <sys/ioctl.h>
-
-#include <xenevtchn.h>
-#include <xenstore.h>
-#define XC_WANT_COMPAT_MAP_FOREIGN_API
-#include <xenctrl.h>
-#include <xenguest.h>
-#include <xenhypfs.h>
-#include <xenctrl_dom.h>
-
-#include <xen-tools/libs.h>
-
-#include "xentoollog.h"
-
-#include <xen/io/xenbus.h>
-
-#ifdef LIBXL_H
-# error libxl.h should be included via libxl_internal.h, not separately
-#endif
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
-# define LIBXL_EXTERNAL_CALLERS_ONLY \
-    __attribute__((warning("may not be called from within libxl")))
-#endif
-
-#include "libxl.h"
-#include "_paths.h"
-#include "_libxl_save_msgs_callout.h"
-
-#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
-#define _hidden __attribute__((visibility("hidden")))
-#define _protected __attribute__((visibility("protected")))
-#else
-#define _hidden
-#define _protected
-#endif
-
-#include "flexarray.h"
-#include "libxl_utils.h"
-
-#include "libxl_json.h"
-
-#include "_libxl_types_internal.h"
-#include "_libxl_types_internal_json.h"
-
-#define LIBXL_INIT_TIMEOUT 10
-#define LIBXL_DESTROY_TIMEOUT 10
-#define LIBXL_HOTPLUG_TIMEOUT 40
-/* QEMU may be slow to load and start due to a bug in Linux where the I/O
- * subsystem sometime produce high latency under load. */
-#define LIBXL_DEVICE_MODEL_START_TIMEOUT 60
-#define LIBXL_DEVICE_MODEL_SAVE_FILE XEN_LIB_DIR "/qemu-save" /* .$domid */
-#define LIBXL_DEVICE_MODEL_RESTORE_FILE XEN_LIB_DIR "/qemu-resume" /* .$domid */
-#define LIBXL_QMP_CMD_TIMEOUT 10
-#define LIBXL_STUBDOM_START_TIMEOUT 30
-#define LIBXL_QEMU_BODGE_TIMEOUT 2
-#define LIBXL_XENCONSOLE_LIMIT 1048576
-#define LIBXL_XENCONSOLE_PROTOCOL "vt100"
-#define LIBXL_MAXMEM_CONSTANT 1024
-#define LIBXL_PV_EXTRA_MEMORY 1024
-#define LIBXL_HVM_EXTRA_MEMORY 2048
-#define LIBXL_MIN_DOM0_MEM (128*1024)
-#define LIBXL_INVALID_GFN (~(uint64_t)0)
-#define LIBXL_VGA_HOLE_SIZE 0x20
-/* use 0 as the domid of the toolstack domain for now */
-#define LIBXL_TOOLSTACK_DOMID 0
-#define QEMU_SIGNATURE "DeviceModelRecord0002"
-#define STUBDOM_CONSOLE_LOGGING 0
-#define STUBDOM_CONSOLE_SAVE 1
-#define STUBDOM_CONSOLE_RESTORE 2
-#define STUBDOM_CONSOLE_SERIAL 3
-#define STUBDOM_SPECIAL_CONSOLES 3
-#define LIBXL_LINUX_STUBDOM_MEM 128
-#define TAP_DEVICE_SUFFIX "-emu"
-#define DOMID_XS_PATH "domid"
-#define PVSHIM_BASENAME "xen-shim"
-#define PVSHIM_CMDLINE "pv-shim console=xen,pv"
-
-/* Size macros. */
-#define __AC(X,Y)   (X##Y)
-#define _AC(X,Y)    __AC(X,Y)
-#define MB(_mb)     (_AC(_mb, ULL) << 20)
-#define GB(_gb)     (_AC(_gb, ULL) << 30)
-
-#define DIV_ROUNDUP(n, d) (((n) + (d) - 1) / (d))
-
-#define MASK_EXTR(v, m) (((v) & (m)) / ((m) & -(m)))
-#define MASK_INSR(v, m) (((v) * ((m) & -(m))) & (m))
-
-#define LIBXL__LOGGING_ENABLED
-
-#ifdef LIBXL__LOGGING_ENABLED
-#define LIBXL__LOG(ctx, loglevel, _f, _a...)   libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
-#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...)   libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
-#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...)   libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a)
-
-/* Same log functions as above, but with _d being a domain id. */
-#define LIBXL__LOGD(ctx, loglevel, _d, _f, _a...)   libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, _d, _f, ##_a)
-#define LIBXL__LOGD_ERRNO(ctx, loglevel, _d, _f, _a...)   libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, _d, _f, ##_a)
-#define LIBXL__LOGD_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...)   libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, _d, _f, ##_a)
-#else
-#define LIBXL__LOG(ctx, loglevel, _f, _a...)
-#define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...)
-#define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...)
-
-#define LIBXLD__LOG(ctx, loglevel, _d, _f, _a...)
-#define LIBXLD__LOG_ERRNO(ctx, loglevel, _d, _f, _a...)
-#define LIBXLD__LOG_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...)
-#endif
-  /* all of these macros preserve errno (saving and restoring) */
-
-/* 
- * A macro to help retain the first failure in "do as much as you can"
- * situations.  Note the hard-coded use of the variable name `rc`.
- */
-#define ACCUMULATE_RC(rc_acc) ((rc_acc) = (rc_acc) ?: rc)
-
-/* Convert pfn to physical address space. */
-#define pfn_to_paddr(x) ((uint64_t)(x) << XC_PAGE_SHIFT)
-
-/* logging */
-_hidden void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
-             const char *file /* may be 0 */, int line /* ignored if !file */,
-             const char *func /* may be 0 */,
-             uint32_t domid /* may be INVALID_DOMID */,
-             const char *fmt, va_list al)
-     __attribute__((format(printf,8,0)));
-
-_hidden void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
-            const char *file /* may be 0 */, int line /* ignored if !file */,
-            const char *func /* may be 0 */,
-            uint32_t domid /* may be INVALID_DOMID */,
-            const char *fmt, ...)
-     __attribute__((format(printf,8,9)));
-
-     /* these functions preserve errno (saving and restoring) */
-
-typedef struct libxl__gc libxl__gc;
-typedef struct libxl__egc libxl__egc;
-typedef struct libxl__ao libxl__ao;
-typedef struct libxl__aop_occurred libxl__aop_occurred;
-typedef struct libxl__osevent_hook_nexus libxl__osevent_hook_nexus;
-typedef struct libxl__osevent_hook_nexi libxl__osevent_hook_nexi;
-typedef struct libxl__device_type libxl__device_type;
-typedef struct libxl__json_object libxl__json_object;
-typedef struct libxl__carefd libxl__carefd;
-typedef struct libxl__ev_slowlock libxl__ev_slowlock;
-typedef struct libxl__dm_resume_state libxl__dm_resume_state;
-typedef struct libxl__ao_device libxl__ao_device;
-typedef struct libxl__multidev libxl__multidev;
-typedef struct libxl__ev_immediate libxl__ev_immediate;
-
-typedef struct libxl__domain_create_state libxl__domain_create_state;
-typedef void libxl__domain_create_cb(struct libxl__egc *egc,
-                                     libxl__domain_create_state *dcs,
-                                     int rc, uint32_t domid);
-
-typedef struct libxl__colo_device_nic libxl__colo_device_nic;
-typedef struct libxl__colo_qdisk libxl__colo_qdisk;
-typedef struct libxl__colo_proxy_state libxl__colo_proxy_state;
-typedef struct libxl__colo_save_state libxl__colo_save_state;
-typedef struct libxl__colo_restore_state libxl__colo_restore_state;
-
-_hidden void libxl__alloc_failed(libxl_ctx *, const char *func,
-                         size_t nmemb, size_t size) __attribute__((noreturn));
-  /* func, size and nmemb are used only in the log message.
-   * You may pass size==0 if size and nmemb are not meaningful
-   * and should not be printed. */
-
-typedef struct libxl__ev_fd libxl__ev_fd;
-typedef void libxl__ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev,
-                                   int fd, short events, short revents);
-  /* Note that revents may contain POLLERR or POLLHUP regardless of
-   * events; otherwise revents contains only bits in events.  Contrary
-   * to the documentation for poll(2), POLLERR and POLLHUP can occur
-   * even if only POLLIN was set in events.  (POLLNVAL is a fatal
-   * error and will cause libxl event machinery to fail an assertion.)
-   *
-   * It is not permitted to listen for the same or overlapping events
-   * on the same fd using multiple different libxl__ev_fd's.
-   *
-   * Note that (depending on the underlying event loop implementation)
-   * it is possible that a the fd callback system is `level triggered'
-   * or `event triggered'.  That is, the callback may be called only
-   * once for each transition from not ready to ready.  So the
-   * callback must generally contain a loop which exhausts the fd,
-   * rather than relying on being called again if the fd is still
-   * ready.
-   *
-   * (Spurious wakeups, and spurious bits set in revents, are
-   * suppressed by the libxl event core.)
-   */
-struct libxl__ev_fd {
-    /* caller should include this in their own struct */
-    /* read-only for caller, who may read only when registered: */
-    int fd;
-    short events;
-    libxl__ev_fd_callback *func;
-    /* remainder is private for libxl__ev_fd... */
-    LIBXL_LIST_ENTRY(libxl__ev_fd) entry;
-    libxl__osevent_hook_nexus *nexus;
-};
-
-
-typedef struct libxl__ao_abortable libxl__ao_abortable;
-typedef void libxl__ao_abortable_callback(libxl__egc *egc,
-                  libxl__ao_abortable *ao_abortable, int rc /* ABORTED */);
-
-struct libxl__ao_abortable {
-    /* caller must fill this in and it must remain valid */
-    libxl__ao *ao;
-    libxl__ao_abortable_callback *callback;
-    /* remainder is private for abort machinery */
-    bool registered;
-    LIBXL_LIST_ENTRY(libxl__ao_abortable) entry;
-    /*
-     * For nested aos:
-     *  Semantically, abort affects the whole tree of aos,
-     *    not just the parent.
-     *  libxl__ao_abortable.ao refers to the child, so
-     *    that the child callback sees the right ao.  (After all,
-     *    it was code dealing with the child that set .ao.)
-     *  But, the abortable is recorded on the "abortables" list
-     *    for the ultimate root ao, so that every possible child
-     *    abort occurs as a result of the abort of the parent.
-     *  We set ao->aborting only in the root.
-     */
-};
-
-_hidden int libxl__ao_abortable_register(libxl__ao_abortable*);
-_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable*);
-
-static inline void libxl__ao_abortable_init
-  (libxl__ao_abortable *c) { c->registered = 0; }
-static inline bool libxl__ao_abortable_isregistered
-  (const libxl__ao_abortable *c) { return c->registered; }
-
-int libxl__ao_aborting(libxl__ao *ao); /* -> 0 or ERROR_ABORTED */
-
-
-typedef struct libxl__ev_time libxl__ev_time;
-typedef void libxl__ev_time_callback(libxl__egc *egc, libxl__ev_time *ev,
-                                     const struct timeval *requested_abs,
-                                     int rc); /* TIMEDOUT or ABORTED */
-struct libxl__ev_time {
-    /* caller should include this in their own struct */
-    /* read-only for caller, who may read only when registered: */
-    libxl__ev_time_callback *func;
-    /* remainder is private for libxl__ev_time... */
-    int infinite; /* not registered in list or with app if infinite */
-    LIBXL_TAILQ_ENTRY(libxl__ev_time) entry;
-    struct timeval abs;
-    libxl__osevent_hook_nexus *nexus;
-    libxl__ao_abortable abrt;
-};
-
-typedef struct libxl__ev_xswatch libxl__ev_xswatch;
-typedef void libxl__ev_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch*,
-                            const char *watch_path, const char *event_path);
-struct libxl__ev_xswatch {
-    /* caller should include this in their own struct */
-    /* read-only for caller, who may read only when registered: */
-    char *path;
-    libxl__ev_xswatch_callback *callback;
-    /* remainder is private for libxl__ev_xswatch... */
-    int slotnum; /* registered iff slotnum >= 0 */
-    uint32_t counterval;
-};
-
-typedef struct libxl__ev_evtchn libxl__ev_evtchn;
-typedef void libxl__ev_evtchn_callback(libxl__egc *egc, libxl__ev_evtchn*);
-struct libxl__ev_evtchn {
-    /* caller must fill these in, and they must all remain valid */
-    libxl__ev_evtchn_callback *callback;
-    int port;
-    /* remainder is private for libxl__ev_evtchn_... */
-    bool waiting;
-    LIBXL_LIST_ENTRY(libxl__ev_evtchn) entry;
-};
-
-/*
- * An entry in the watch_slots table is either:
- *  1. an entry in the free list, ie NULL or pointer to next free list entry
- *  2. an pointer to a libxl__ev_xswatch
- *
- * But we don't want to use unions or type-punning because the
- * compiler might "prove" that our code is wrong and misoptimise it.
- *
- * The rules say that all struct pointers have identical
- * representation and alignment requirements (C99+TC1+TC2 6.2.5p26) so
- * what we do is simply declare our array as containing only the free
- * list pointers, and explicitly convert from and to our actual
- * xswatch pointers when we store and retrieve them.
- */
-typedef struct libxl__ev_watch_slot {
-    LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty;
-} libxl__ev_watch_slot;
-
-_hidden libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc,
-                                                      int slotnum);
-
-
-typedef struct libxl__ev_child libxl__ev_child;
-typedef void libxl__ev_child_callback(libxl__egc *egc, libxl__ev_child*,
-                                      pid_t pid, int status);
-struct libxl__ev_child {
-    /* caller should include this in their own struct */
-    /* read-only for caller: */
-    pid_t pid; /* -1 means unused ("unregistered", ie Idle) */
-    libxl__ev_child_callback *callback;
-    /* remainder is private for libxl__ev_... */
-    LIBXL_LIST_ENTRY(struct libxl__ev_child) entry;
-};
-
-/* libxl__ev_immediate
- *
- * Allow to call a non-reentrant callback.
- *
- * `callback' will be called immediately as a new event.
- */
-struct libxl__ev_immediate {
-    /* filled by user */
-    void (*callback)(libxl__egc *, libxl__ev_immediate *);
-    /* private to libxl__ev_immediate */
-    LIBXL_STAILQ_ENTRY(libxl__ev_immediate) entry;
-};
-void libxl__ev_immediate_register(libxl__egc *, libxl__ev_immediate *);
-
-/*
- * Lock for device hotplug, qmp_lock.
- *
- * libxl__ev_slowlock implement a lock that is outside of CTX_LOCK in the
- * lock hierarchy. It can be used when one want to make QMP calls to QEMU,
- * which may take a significant amount time.
- * It is to be acquired by an ao event callback.
- *
- * If libxl__ev_devlock is needed, it should be acquired while every
- * libxl__ev_qmp are Idle for the current domain.
- *
- * It is to be acquired when adding/removing devices or making changes
- * to them when this is a slow operation and json_lock isn't appropriate.
- *
- * Possible states of libxl__ev_slowlock:
- *   Undefined
- *    Might contain anything.
- *  Idle
- *    Struct contents are defined enough to pass to any
- *    libxl__ev_slowlock_* function.
- *    The struct does not contain references to any allocated private
- *    resources so can be thrown away.
- *  Active
- *    Waiting to get a lock.
- *    Needs to wait until the callback is called.
- *  LockAcquired
- *    libxl__ev_slowlock_unlock will need to be called to release the lock
- *    and the resources of libxl__ev_slowlock.
- *
- *  libxl__ev_*lock_init: Undefined/Idle -> Idle
- *  libxl__ev_slowlock_lock: Idle -> Active
- *    May call callback synchronously.
- *  libxl__ev_slowlock_unlock: LockAcquired/Idle -> Idle
- *  libxl__ev_slowlock_dispose: Idle/Active/LockAcquired -> Idle
- *    The callback will not be called anymore.
- *  callback:     When called: Active -> LockAcquired (on error: Idle)
- *    The callback is only called once.
- */
-struct libxl__ev_slowlock {
-    /* filled by user */
-    libxl__ao *ao;
-    libxl_domid domid;
-    void (*callback)(libxl__egc *, libxl__ev_slowlock *, int rc);
-    /* private to libxl__ev_slowlock* */
-    libxl__ev_child child;
-    const char *userdata_userid;
-    char *path; /* path of the lock file itself */
-    int fd;
-    bool held;
-};
-_hidden void libxl__ev_devlock_init(libxl__ev_slowlock *);
-_hidden void libxl__ev_qmplock_init(libxl__ev_slowlock *);
-_hidden void libxl__ev_slowlock_lock(libxl__egc *, libxl__ev_slowlock *);
-_hidden void libxl__ev_slowlock_unlock(libxl__gc *, libxl__ev_slowlock *);
-_hidden void libxl__ev_slowlock_dispose(libxl__gc *, libxl__ev_slowlock *);
-
-/*
- * QMP asynchronous calls
- *
- * This facility allows a command to be sent to QEMU, and the response
- * to be handed to a callback function.
- *
- * Commands can be submited one after an other with the same
- * connection (e.g. the result from the "add-fd" command need to be
- * use in a follow-up command before disconnecting from QMP). A
- * libxl__ev_qmp can be reused when the callback is been called in
- * order to use the same connection.
- *
- * Only one connection at a time can be made to one QEMU, so avoid
- * keeping a libxl__ev_qmp Connected for to long and call
- * libxl__ev_qmp_dispose as soon as it is not needed anymore.
- *
- * Possible states of a libxl__ev_qmp:
- *  Undefined
- *    Might contain anything.
- *  Idle
- *    Struct contents are defined enough to pass to any
- *    libxl__ev_qmp_* function.
- *    The struct does not contain references to any allocated private
- *    resources so can be thrown away.
- *  Active
- *    Currently waiting for the callback to be called.
- *    _dispose must be called to reclaim resources.
- *  Connected
- *    Struct contain allocated ressources.
- *    Calling _send() with this same ev will use the same QMP connection.
- *    _dispose() must be called to reclaim resources.
- *
- * libxl__ev_qmp_init: Undefined/Idle -> Idle
- *
- * libxl__ev_qmp_send: Idle/Connected -> Active (on error: Idle)
- *    Sends a command to QEMU.
- *    callback will be called when a response is received or when an
- *    error as occured.
- *    callback isn't called synchronously.
- *
- * libxl__ev_qmp_dispose: Connected/Active/Idle -> Idle
- *
- * callback: When called: Active -> Connected (on error: Idle/Connected)
- *    When called, ev is Connected and can be reused or disposed of.
- *    On error, the callback is called with response == NULL and the
- *    error code in rc. The new state of ev depending on the value of rc:
- *    - rc == ERROR_QMP_*: This is an error associated with the cmd to
- *      run, ev is Connected.
- *    - otherwise: An other error happend, ev is now Idle.
- *    The callback is only called once.
- */
-typedef struct libxl__ev_qmp libxl__ev_qmp;
-typedef void libxl__ev_qmp_callback(libxl__egc *egc, libxl__ev_qmp *ev,
-                                    const libxl__json_object *response,
-                                    int rc);
-
-_hidden void libxl__ev_qmp_init(libxl__ev_qmp *ev);
-_hidden int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
-                               const char *cmd, libxl__json_object *args);
-_hidden void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev);
-
-typedef enum {
-    /* initial state */
-    qmp_state_disconnected = 1,
-    /* waiting for lock */
-    qmp_state_waiting_lock,
-    /* connected to QMP socket, waiting for greeting message */
-    qmp_state_connecting,
-    /* qmp_capabilities command sent, waiting for reply */
-    qmp_state_capability_negotiation,
-    /* sending user's cmd and waiting for reply */
-    qmp_state_waiting_reply,
-    /* ready to send commands */
-    qmp_state_connected,
-} libxl__qmp_state;
-
-struct libxl__ev_qmp {
-    /* caller should include this in their own struct */
-    /* caller must fill these in, and they must all remain valid */
-    libxl__ao *ao;
-    libxl_domid domid;
-    libxl__ev_qmp_callback *callback;
-    int payload_fd; /* set to send a fd with the command, -1 otherwise */
-
-    /* read-only when Connected
-     * and not to be accessed by the caller otherwise */
-    struct {
-        int major;
-        int minor;
-        int micro;
-    } qemu_version;
-
-    /*
-     * remaining fields are private to libxl_ev_qmp_*
-     */
-
-    libxl__carefd *cfd;
-    libxl__ev_fd efd;
-    libxl__qmp_state state;
-    libxl__ev_slowlock lock;
-    libxl__ev_immediate ei;
-    int rc;
-    int id;
-    int next_id;        /* next id to use */
-    /* receive buffer */
-    char *rx_buf;
-    size_t rx_buf_size; /* current allocated size */
-    size_t rx_buf_used; /* actual data in the buffer */
-    /* sending buffer */
-    char *tx_buf;
-    size_t tx_buf_len;  /* tx_buf size */
-    size_t tx_buf_off;  /* already sent */
-    /* The message to send when ready */
-    char *msg;
-    int msg_id;
-};
-
-/* QMP parameters helpers */
-
-_hidden void libxl__qmp_param_add_string(libxl__gc *gc,
-                                         libxl__json_object **param,
-                                         const char *name, const char *s);
-_hidden void libxl__qmp_param_add_bool(libxl__gc *gc,
-                                       libxl__json_object **param,
-                                       const char *name, bool b);
-_hidden void libxl__qmp_param_add_integer(libxl__gc *gc,
-                                          libxl__json_object **param,
-                                          const char *name, const int i);
-#define QMP_PARAMETERS_SPRINTF(args, name, format, ...) \
-    libxl__qmp_param_add_string(gc, args, name, \
-                                GCSPRINTF(format, __VA_ARGS__))
-
-
-/*
- * evgen structures, which are the state we use for generating
- * events for the caller.
- *
- * In general in each case there's an internal and an external
- * version of the _evdisable_FOO function; the internal one is
- * used during cleanup.
- */
-struct libxl__evgen_domain_death {
-    uint32_t domid;
-    unsigned shutdown_reported:1, death_reported:1;
-    LIBXL_TAILQ_ENTRY(libxl_evgen_domain_death) entry;
-        /* on list .death_reported ? CTX->death_list : CTX->death_reported */
-    libxl_ev_user user;
-};
-_hidden void
-libxl__evdisable_domain_death(libxl__gc*, libxl_evgen_domain_death*);
-
-struct libxl__evgen_disk_eject {
-    libxl__ev_xswatch watch;
-    uint32_t domid;
-    LIBXL_LIST_ENTRY(libxl_evgen_disk_eject) entry;
-    libxl_ev_user user;
-    char *vdev, *be_ptr_path;
-};
-_hidden void
-libxl__evdisable_disk_eject(libxl__gc*, libxl_evgen_disk_eject*);
-
-typedef struct libxl__poller libxl__poller;
-struct libxl__poller {
-    /*
-     * These are used to allow other threads to wake up a thread which
-     * may be stuck in poll, because whatever it was waiting for
-     * hadn't happened yet.  Threads which generate events will write
-     * a byte to each pipe.  A thread which is waiting will empty its
-     * own pipe, and put its poller on the pollers_event list, before
-     * releasing the ctx lock and going into poll; when it comes out
-     * of poll it will take the poller off the pollers_event list.
-     *
-     * A thread which is waiting for completion of a synchronous ao
-     * will allocate a poller and record it in the ao, so that other
-     * threads can wake it up.
-     *
-     * When a thread is done with a poller it should put it onto
-     * pollers_idle, where it can be reused later.
-     *
-     * The "poller_app" is never idle, but is sometimes on
-     * pollers_event.
-     */
-    LIBXL_LIST_ENTRY(libxl__poller) entry;
-
-    struct pollfd *fd_polls;
-    int fd_polls_allocd;
-
-    int fd_rindices_allocd;
-    int (*fd_rindices)[3]; /* see libxl_event.c:beforepoll_internal */
-
-    int wakeup_pipe[2]; /* 0 means no fd allocated */
-    bool pipe_nonempty;
-
-    /*
-     * We also use the poller to record whether any fds have been
-     * deregistered since we entered poll.  Each poller which is not
-     * idle is on the list pollers_active.  fds_deregistered is
-     * cleared by beforepoll, and tested by afterpoll.  Whenever an fd
-     * event is deregistered, we set the fds_deregistered of all non-idle
-     * pollers.  So afterpoll can tell whether any POLLNVAL is
-     * plausibly due to an fd being closed and reopened.
-     *
-     * Additionally, we record whether any fd or time event sources
-     * have been registered.  This is necessary because sometimes we
-     * need to wake up the only libxl thread stuck in
-     * eventloop_iteration so that it will pick up new fds or earlier
-     * timeouts.  osevents_added is cleared by beforepoll, and set by
-     * fd or timeout event registration.  When we are about to leave
-     * libxl (strictly, when we are about to give up an egc), we check
-     * whether there are any pollers.  If there are, then at least one
-     * of them must have osevents_added clear.  If not, we wake up the
-     * first one on the list.  Any entry on pollers_active constitutes
-     * a promise to also make this check, so the baton will never be
-     * dropped.
-     */
-    LIBXL_LIST_ENTRY(libxl__poller) active_entry;
-    bool fds_deregistered;
-    bool osevents_added;
-};
-
-struct libxl__gc {
-    /* mini-GC */
-    int alloc_maxsize; /* -1 means this is the dummy non-gc gc */
-    void **alloc_ptrs;
-    libxl_ctx *owner;
-};
-
-struct libxl__ctx {
-    xentoollog_logger *lg;
-    xc_interface *xch;
-    struct xs_handle *xsh;
-    libxl__gc nogc_gc;
-
-    const libxl_event_hooks *event_hooks;
-    void *event_hooks_user;
-
-    pthread_mutex_t lock; /* protects data structures hanging off the ctx */
-      /* Always use libxl__ctx_lock and _unlock (or the convenience
-       * macors CTX_LOCK and CTX_UNLOCK) to manipulate this.
-       *
-       * You may acquire this mutex recursively if it is convenient to
-       * do so.  You may not acquire this lock at the same time as any
-       * other lock.  If you need to call application code outside
-       * libxl (ie, a callback) with this lock held then it is
-       * necessaray to impose restrictions on the caller to maintain a
-       * proper lock hierarchy, and these restrictions must then be
-       * documented in the libxl public interface.
-       */
-
-    LIBXL_TAILQ_HEAD(libxl__event_list, libxl_event) occurred;
-
-    int osevent_in_hook;
-    const libxl_osevent_hooks *osevent_hooks;
-    void *osevent_user;
-      /* See the comment for OSEVENT_HOOK_INTERN in libxl_event.c
-       * for restrictions on the use of the osevent fields. */
-
-    libxl__poller *poller_app; /* libxl_osevent_beforepoll and _afterpoll */
-    LIBXL_LIST_HEAD(, libxl__poller) pollers_event, pollers_idle;
-    LIBXL_LIST_HEAD(, libxl__poller) pollers_active;
-
-    LIBXL_SLIST_HEAD(libxl__osevent_hook_nexi, libxl__osevent_hook_nexus)
-        hook_fd_nexi_idle, hook_timeout_nexi_idle;
-    LIBXL_LIST_HEAD(, libxl__ev_fd) efds;
-    LIBXL_TAILQ_HEAD(, libxl__ev_time) etimes;
-
-    libxl__ev_watch_slot *watch_slots;
-    int watch_nslots, nwatches;
-    LIBXL_SLIST_HEAD(, libxl__ev_watch_slot) watch_freeslots;
-    uint32_t watch_counter; /* helps disambiguate slot reuse */
-    libxl__ev_fd watch_efd;
-
-    xenevtchn_handle *xce; /* waiting must be done only with libxl__ev_evtchn* */
-    LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting;
-    libxl__ev_fd evtchn_efd;
-
-    LIBXL_LIST_HEAD(, libxl__ao) aos_inprogress;
-
-    LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death)
-        death_list /* sorted by domid */,
-        death_reported;
-    libxl__ev_xswatch death_watch;
-
-    LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens;
-
-    const libxl_childproc_hooks *childproc_hooks;
-    void *childproc_user;
-    int sigchld_selfpipe[2]; /* [0]==-1 means handler not installed */
-    libxl__ev_fd sigchld_selfpipe_efd;
-    LIBXL_LIST_HEAD(, libxl__ev_child) children;
-    bool sigchld_user_registered;
-    LIBXL_LIST_ENTRY(libxl_ctx) sigchld_users_entry;
-
-    libxl_version_info version_info;
-
-    bool libxl_domain_need_memory_0x041200_called,
-         libxl_domain_need_memory_called;
-};
-
-/*
- * libxl__device is a transparent structure that doesn't contain private fields
- * or external memory references, and as such can be copied by assignment.
- */
-typedef struct {
-    uint32_t backend_devid;
-    uint32_t backend_domid;
-    uint32_t devid;
-    uint32_t domid;
-    libxl__device_kind backend_kind;
-    libxl__device_kind kind;
-} libxl__device;
-
-/* Used to know if backend of given device is QEMU */
-#define QEMU_BACKEND(dev) (\
-    (dev)->backend_kind == LIBXL__DEVICE_KIND_QDISK || \
-    (dev)->backend_kind == LIBXL__DEVICE_KIND_VFB || \
-    (dev)->backend_kind == LIBXL__DEVICE_KIND_QUSB || \
-    (dev)->backend_kind == LIBXL__DEVICE_KIND_9PFS || \
-    (dev)->backend_kind == LIBXL__DEVICE_KIND_VKBD)
-
-#define XC_PCI_BDF             "0x%x, 0x%x, 0x%x, 0x%x"
-#define PCI_DEVFN(slot, func)   ((((slot) & 0x1f) << 3) | ((func) & 0x07))
-#define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
-#define PCI_FUNC(devfn)         ((devfn) & 0x07)
-#define AUTO_PHP_SLOT          0x100
-
-#define PROC_PCI_NUM_RESOURCES 7
-#define PCI_BAR_IO             0x01
-
-#define PRINTF_ATTRIBUTE(x, y) __attribute__((format(printf, x, y)))
-
-struct libxl__egc {
-    /* For event-generating functions only.
-     * The egc and its gc may be accessed only on the creating thread. */
-    struct libxl__gc gc;
-    struct libxl__event_list occurred_for_callback;
-    LIBXL_TAILQ_HEAD(, libxl__ao) aos_for_callback;
-    LIBXL_TAILQ_HEAD(, libxl__aop_occurred) aops_for_callback;
-    LIBXL_STAILQ_HEAD(, libxl__ev_immediate) ev_immediates;
-};
-
-struct libxl__aop_occurred {
-    /*
-     * An aop belongs to, and may be accessed only on, the thread
-     * which created it.  It normally lives in that thread's egc.
-     *
-     * While an aop exists, it corresponds to one refcount in
-     * ao->progress_reports_outstanding, preventing ao destruction.
-     */
-    LIBXL_TAILQ_ENTRY(libxl__aop_occurred) entry;
-    libxl__ao *ao;
-    libxl_event *ev;
-    const libxl_asyncprogress_how *how;
-};
-
-#define LIBXL__AO_MAGIC              0xA0FACE00ul
-#define LIBXL__AO_MAGIC_DESTROYED    0xA0DEAD00ul
-
-struct libxl__ao {
-    /*
-     * An ao and its gc may be accessed only with the ctx lock held.
-     *
-     * Special exception: If an ao has been added to
-     * egc->aos_for_callback, the thread owning the egc may remove the
-     * ao from that list and make the callback without holding the
-     * lock.
-     *
-     * Corresponding restriction: An ao may be added only to one
-     * egc->aos_for_callback, once; rc and how must already have been
-     * set and may not be subsequently modified.  (This restriction is
-     * easily and obviously met since the ao is queued for callback
-     * only in libxl__ao_complete.)
-     */
-    uint32_t magic;
-    unsigned constructing:1, in_initiator:1, complete:1, notified:1,
-        aborting:1;
-    int manip_refcnt;
-    libxl__ao *nested_root;
-    int nested_progeny;
-    int progress_reports_outstanding;
-    int rc;
-    LIBXL_LIST_HEAD(, libxl__ao_abortable) abortables;
-    LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry;
-    libxl__gc gc;
-    libxl_asyncop_how how;
-    libxl__poller *poller;
-    uint32_t domid;
-    LIBXL_TAILQ_ENTRY(libxl__ao) entry_for_callback;
-    int outstanding_killed_child;
-};
-
-#define LIBXL_INIT_GC(gc,ctx) do{               \
-        (gc).alloc_maxsize = 0;                 \
-        (gc).alloc_ptrs = 0;                    \
-        (gc).owner = (ctx);                     \
-    } while(0)
-    /* NB, also, a gc struct ctx->nogc_gc is initialised in libxl_ctx_alloc */
-
-static inline libxl_ctx *libxl__gc_owner(libxl__gc *gc)
-{
-    return gc->owner;
-}
-
-static inline int libxl__gc_is_real(const libxl__gc *gc)
-{
-    return gc->alloc_maxsize >= 0;
-}
-
-/*
- * Memory allocation tracking/helpers
- *
- * See comment "libxl memory management" in libxl.h for a description
- * of the framework which these calls belong to.
- *
- * These functions deal with memory allocations of type (a) and (d) in
- * that description.
- *
- * All pointers returned by these functions are registered for garbage
- * collection on exit from the outermost libxl callframe.
- *
- * However, where the argument is stated to be "gc_opt", &ctx->nogc_gc
- * may be passed instead, in which case no garbage collection will
- * occur; the pointer must later be freed with free().  (Passing NULL
- * for gc_opt is not permitted.)  This is for memory allocations of
- * types (b) and (c).  The convenience macro NOGC should be used where
- * possible.
- *
- * NOGC (and ctx->nogc_gc) may ONLY be used with functions which
- * explicitly declare that it's OK.  Use with nonconsenting functions
- * may result in leaks of those functions' internal allocations on the
- * psuedo-gc.
- */
-/* register ptr in gc for free on exit from outermost libxl callframe. */
-
-#define NN(...) __attribute__((nonnull(__VA_ARGS__)))
-#define NN1 __attribute__((nonnull(1)))
- /* It used to be legal to pass NULL for gc_opt.  Get the compiler to
-  * warn about this if any slip through. */
-
-_hidden void libxl__ptr_add(libxl__gc *gc_opt, void *ptr /* may be NULL */) NN1;
-/* if this is the outermost libxl callframe then free all pointers in @gc */
-_hidden void libxl__free_all(libxl__gc *gc);
-/* allocate @size bytes. (a gc'd malloc(3)) */
-_hidden void *libxl__malloc(libxl__gc *gc_opt, size_t size) NN1;
-/* allocate and zero @size. (similar to a gc'd malloc(3)+memzero()) */
-_hidden void *libxl__zalloc(libxl__gc *gc_opt, size_t size) NN1;
-/* allocate and zero memory for an array of @nmemb members of @size each.
- * (similar to a gc'd calloc(3)). */
-_hidden void *libxl__calloc(libxl__gc *gc_opt, size_t nmemb, size_t size) NN1;
-/* change the size of the memory block pointed to by @ptr to @new_size bytes.
- * unlike other allocation functions here any additional space between the
- * oldsize and @new_size is not initialised (similar to a gc'd realloc(3)).
- * if @ptr is non-NULL and @gc_opt is not nogc_gc then @ptr must have been
- * registered with @gc_opt previously. */
-_hidden void *libxl__realloc(libxl__gc *gc_opt, void *ptr, size_t new_size) NN1;
-/* print @fmt into an allocated string large enoughto contain the result.
- * (similar to gc'd asprintf(3)). */
-_hidden char *libxl__sprintf(libxl__gc *gc_opt, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3) NN1;
-_hidden char *libxl__vsprintf(libxl__gc *gc, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0);
-/* duplicate the string @c (similar to a gc'd strdup(3)). */
-_hidden char *libxl__strdup(libxl__gc *gc_opt,
-                            const char *c /* may be NULL */) NN1;
-/* duplicate at most @n bytes of string @c (similar to a gc'd strndup(3)). */
-_hidden char *libxl__strndup(libxl__gc *gc_opt,
-                             const char *c /* may be NULL */,
-                             size_t n) NN1;
-/* strip the last path component from @s and return as a newly allocated
- * string. (similar to a gc'd dirname(3)). */
-_hidden char *libxl__dirname(libxl__gc *gc_opt, const char *s) NN1;
-
-/* Make a pipe and set both ends nonblocking.  On error, nothing
- * is left open and both fds[]==-1, and a message is logged.
- * Useful for self-pipes. */
-_hidden int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2]);
-/* Closes the pipe fd(s).  Either or both of fds[] may be -1 meaning
- * `not open'.  Ignores any errors.  Sets fds[] to -1. */
-_hidden void libxl__pipe_close(int fds[2]);
-
-/* Change the flags for the file description associated with fd to
- *    (flags & mask) | val.
- * If r_oldflags != NULL then sets *r_oldflags to the original set of
- * flags.
- */
-_hidden int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
-                                        int mask, int val, int *r_oldflags);
-/* Restores the flags for the file description associated with fd to
- * to the previous value (returned by libxl__fd_flags_modify_save)
- */
-_hidden int libxl__fd_flags_restore(libxl__gc *gc, int fd, int old_flags);
-
-/* Each of these logs errors and returns a libxl error code.
- * They do not mind if path is already removed.
- * For _file, path must not be a directory; for _directory it must be. */
-_hidden int libxl__remove_file(libxl__gc *gc, const char *path);
-_hidden int libxl__remove_directory(libxl__gc *gc, const char *path);
-_hidden int libxl__remove_file_or_directory(libxl__gc *gc, const char *path);
-
-
-_hidden char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array);
-
-/* treats kvs as pairs of keys and values and writes each to dir. */
-_hidden int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
-                             const char *dir, char **kvs);
-/* as writev but also sets the permissions on each path */
-_hidden int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
-                                   const char *dir, char *kvs[],
-                                   struct xs_permissions *perms,
-                                   unsigned int num_perms);
-/* _atonce creates a transaction and writes all keys at once */
-_hidden int libxl__xs_writev_atonce(libxl__gc *gc,
-                             const char *dir, char **kvs);
-   /* Each fn returns 0 on success.
-    * On error: returns -1, sets errno (no logging) */
-
-_hidden char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid);
-   /* On error: logs, returns NULL, sets errno. */
-
-_hidden char *libxl__xs_read(libxl__gc *gc, xs_transaction_t t,
-                             const char *path);
-_hidden char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
-                                   const char *path, unsigned int *nb);
-   /* On error: returns NULL, sets errno (no logging) */
-_hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid);
-
-_hidden int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path,
-                                           libxl_domid *domid_out);
-
-/*----- "checked" xenstore access functions -----*/
-/* Each of these functions will check that it succeeded; if it
- * fails it logs and returns ERROR_FAIL.
- */
-
-int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
-                      const char *path, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(4, 0);
-int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
-                     const char *path, const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5);
-
-/* On success, path will exist and will have an empty value */
-int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
-                    const char *path, struct xs_permissions *perms,
-                    unsigned int num_perms);
-
-/* On success, *result_out came from the gc.
- * On error, *result_out is undefined.
- * ENOENT is regarded as error.
- */
-int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
-                             const char *path, const char **result_out);
-
-/* On success, *result_out came from the gc.
- * On error, *result_out is undefined.
- * ENOENT counts as success but sets *result_out=0
- */
-int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
-                           const char *path, const char **result_out);
-
-/* Does not include a trailing null.
- * May usefully be combined with GCSPRINTF if the format string
- * behaviour of libxl__xs_printf is desirable. */
-int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
-                            const char *path, const char *string);
-
-/* ENOENT is not an error (even if the parent directories don't exist) */
-int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path);
-
-/* Transaction functions, best used together.
- * The caller should initialise *t to 0 (XBT_NULL) before calling start.
- * Each function leaves *t!=0 iff the transaction needs cleaning up.
- *
- * libxl__xs_transaction_commit returns:
- *   <0  failure - a libxl error code
- *   +1  commit conflict; transaction has been destroyed and caller
- *        must go round again (call _start again and retry)
- *    0  committed successfully
- *
- * The intended usage pattern looks like this:
- *    int some_function()
- *    {
- *        int rc;
- *        xs_transaction_t t = 0;
- *        // other initialisations
- *
- *        // do whatever you need to do before the xenstore stuff
- *        // errors?  set rc and goto out.
- *
- *        for (;;) {
- *            rc = libxl__xs_transaction_start(gc, &t);
- *            if (rc) goto out;
- *
- *            // do your work here, including all xenstore reads and writes
- *            // libxl__xs_*_checked are useful; pass them t.
- *            // errors?  set rc and goto out.
- *
- *            rc = libxl__xs_transaction_commit(gc, &t);
- *            if (!rc) break;
- *            if (rc<0) goto out;
- *        }
- *
- *        // now the xenstore transaction succeeded
- *        // do whatever else you need to do
- *        // errors?  set rc and goto out.
- *
- *        return something;
- *
- *     out:
- *        // other cleanups
- *        libxl__xs_transaction_abort(gc, &t);
- *        // other cleanups
- *        return rc;
- *    }
- *
- * Formally the states of *t are:
- *
- *  name     value of *t  description
- *   Idle         0         no transaction exists
- *   Ready        non-0     ready for work, nothing done yet
- *   Busy         non-0     writes have been made but we are not finished
- *   Uncommitted  non-0     writes have been made and should be committed
- *
- * libxl__xs_transaction_start:  Idle -> Ready (on error: Idle)
- *
- * The transaction goes from Ready to Busy, and from Busy to
- * Uncommitted, by the use of xenstore read and write operations
- * (libxl__xs_..., xs_...) made by libxl__xs_transaction's caller.
- *
- * libxl__xs_transaction_commit:  Ready/Uncommitted -> Idle
- *     on success (returns 0): xenstore has been updated
- *     on error (<0) or conflict (+1): updates discarded
- *
- * libxl__xs_transaction_abort:  Any -> Idle  (any updates discarded)
- */
-int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t);
-int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t);
-void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t);
-
-
-
-/*
- * This is a recursive delete, from top to bottom. What this function does
- * is remove empty folders that contained the deleted entry.
- *
- * It mimics xenstore-rm -t behaviour.
- */
-_hidden int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
-                                   const char *user_path);
-
-/*
- * Event generation functions provided by the libxl event core to the
- * rest of libxl.  Implemented in terms of _beforepoll/_afterpoll
- * and/or the fd registration machinery, as provided by the
- * application.
- *
- * Semantics are similar to those of the fd and timeout registration
- * functions provided to libxl_osevent_register_hooks.
- *
- * Non-0 returns from libxl__ev_{modify,deregister} have already been
- * logged by the core and should be returned unmodified to libxl's
- * caller; NB that they may be valid libxl error codes but they may
- * also be positive numbers supplied by the caller.
- *
- * In each case, there is a libxl__ev_FOO structure which can be in
- * one of three states:
- *
- *   Undefined   - Might contain anything.  All-bits-zero is
- *                 an undefined state.
- *
- *   Idle        - Struct contents are defined enough to pass to any
- *                 libxl__ev_FOO function but not registered and
- *                 callback will not be called.  The struct does not
- *                 contain references to any allocated resources so
- *                 can be thrown away.
- *
- *   Active      - Request for events has been registered and events
- *                 may be generated.  _deregister must be called to
- *                 reclaim resources.
- *
- * These functions are provided for each kind of event KIND:
- *
- *   int libxl__ev_KIND_register(libxl__gc *gc, libxl__ev_KIND *GEN,
- *                              libxl__ev_KIND_callback *FUNC,
- *                              DETAILS);
- *      On entry *GEN must be in state Undefined or Idle.
- *      Returns a libxl error code; on error return *GEN is Idle.
- *      On successful return *GEN is Active and FUNC wil be
- *      called by the event machinery in future.  FUNC will
- *      not be called from within the call to _register.
- *      FUNC will be called with the context locked (with CTX_LOCK).
- *
- *  void libxl__ev_KIND_deregister(libxl__gc *gc, libxl__ev_KIND *GEN_upd);
- *      On entry *GEN must be in state Active or Idle.
- *      On return it is Idle.  (Idempotent.)
- *
- *  void libxl__ev_KIND_init(libxl__ev_KIND *GEN);
- *      Provided for initialising an Undefined KIND.
- *      On entry *GEN must be in state Idle or Undefined.
- *      On return it is Idle.  (Idempotent.)
- *
- *  int libxl__ev_KIND_isregistered(const libxl__ev_KIND *GEN);
- *      On entry *GEN must be Idle or Active.
- *      Returns nonzero if it is Active, zero otherwise.
- *      Cannot fail.
- *
- *  int libxl__ev_KIND_modify(libxl__gc*, libxl__ev_KIND *GEN,
- *                            DETAILS);
- *      Only provided for some kinds of generator.
- *      On entry *GEN must be Active and on return, whether successful
- *      or not, it will be Active.
- *      Returns a libxl error code; on error the modification
- *      is not effective.
- *
- * All of these functions are fully threadsafe and may be called by
- * general code in libxl even from within event callback FUNCs.
- * The ctx will be locked on entry to each FUNC and FUNC should not
- * unlock it.
- *
- * Callers of libxl__ev_KIND_register must ensure that the
- * registration is undone, with _deregister, in libxl_ctx_free.
- * This means that normally each kind of libxl__evgen (ie each
- * application-requested event source) needs to be on a list so that
- * it can be automatically deregistered as promised in libxl_event.h.
- */
-
-
-_hidden int libxl__ev_fd_register(libxl__gc*, libxl__ev_fd *ev_out,
-                                  libxl__ev_fd_callback*,
-                                  int fd, short events /* as for poll(2) */);
-_hidden int libxl__ev_fd_modify(libxl__gc*, libxl__ev_fd *ev,
-                                short events);
-_hidden void libxl__ev_fd_deregister(libxl__gc*, libxl__ev_fd *ev);
-static inline void libxl__ev_fd_init(libxl__ev_fd *efd)
-                    { efd->fd = -1; }
-static inline int libxl__ev_fd_isregistered(const libxl__ev_fd *efd)
-                    { return efd->fd >= 0; }
-
-_hidden int libxl__ev_time_register_rel(libxl__ao*, libxl__ev_time *ev_out,
-                                        libxl__ev_time_callback*,
-                                        int milliseconds /* as for poll(2) */);
-_hidden int libxl__ev_time_register_abs(libxl__ao*, libxl__ev_time *ev_out,
-                                        libxl__ev_time_callback*,
-                                        struct timeval);
-_hidden int libxl__ev_time_modify_rel(libxl__gc*, libxl__ev_time *ev,
-                                      int milliseconds /* as for poll(2) */);
-_hidden int libxl__ev_time_modify_abs(libxl__gc*, libxl__ev_time *ev,
-                                      struct timeval);
-_hidden void libxl__ev_time_deregister(libxl__gc*, libxl__ev_time *ev);
-static inline void libxl__ev_time_init(libxl__ev_time *ev)
-                { ev->func = 0; libxl__ao_abortable_init(&ev->abrt); }
-static inline int libxl__ev_time_isregistered(const libxl__ev_time *ev)
-                { return !!ev->func; }
-
-
-_hidden int libxl__ev_xswatch_register(libxl__gc*, libxl__ev_xswatch *xsw_out,
-                                       libxl__ev_xswatch_callback*,
-                                       const char *path /* copied */);
-_hidden void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch*);
-
-static inline void libxl__ev_xswatch_init(libxl__ev_xswatch *xswatch_out)
-                { xswatch_out->slotnum = -1; }
-static inline int libxl__ev_xswatch_isregistered(const libxl__ev_xswatch *xw)
-                { return xw->slotnum >= 0; }
-
-
-/*
- * The evtchn facility is one-shot per call to libxl__ev_evtchn_wait.
- * You should:
- *   Use libxl__ctx_evtchn_init to make sure CTX->xce is valid;
- *   Call some suitable xc bind function on (or to obtain) the port;
- *   Then call libxl__ev_evtchn_wait.
- *
- * When the event is signaled then the callback will be made, once.
- * Then you must call libxl__ev_evtchn_wait again, if desired.
- *
- * You must NOT call xenevtchn_unmask.  wait will do that for you.
- *
- * Calling libxl__ev_evtchn_cancel will arrange for libxl to disregard
- * future occurrences of event.  Both libxl__ev_evtchn_wait and
- * libxl__ev_evtchn_cancel are idempotent.
- *
- * (Note of course that an event channel becomes signaled when it is
- * first bound, so you will get one call to libxl__ev_evtchn_wait
- * "right away"; unless you have won a very fast race, the condition
- * you were waiting for won't exist yet so when you check for it
- * you'll find you need to call wait again.)
- *
- * You must not wait on the same port twice at once (that is, with
- * two separate libxl__ev_evtchn's).
- */
-_hidden int libxl__ev_evtchn_wait(libxl__gc*, libxl__ev_evtchn *evev);
-_hidden void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev);
-
-static inline void libxl__ev_evtchn_init(libxl__ev_evtchn *evev)
-                { evev->waiting = 0; }
-static inline bool libxl__ev_evtchn_iswaiting(const libxl__ev_evtchn *evev)
-                { return evev->waiting; }
-
-_hidden int libxl__ctx_evtchn_init(libxl__gc *gc); /* for libxl_ctx_alloc */
-
-/*
- * For making subprocesses.  This is the only permitted mechanism for
- * code in libxl to do so.
- *
- * In the parent, returns the pid, filling in childw_out.
- * In the child, returns 0.
- * If it fails, returns a libxl error (all of which are -ve).
- *
- * The child should go on to exec (or exit) soon.  The child may not
- * make any further calls to libxl infrastructure, except for memory
- * allocation and logging.  If the child needs to use xenstore it
- * must open its own xs handle and use it directly, rather than via
- * the libxl event machinery.
- *
- * The parent may signal the child but it must not reap it.  That will
- * be done by the event machinery.
- *
- * The child death event will generate exactly one event callback; until
- * then the childw is Active and may not be reused.
- *
- * libxl__ev_child_kill_deregister: Active -> Idle
- *   This will transfer ownership of the child process death event from
- *   `ch' to `ao', thus deregister the callback.
- *   The `ao' completion will wait until the child have been reaped by the
- *   event machinery.
- */
-_hidden pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *childw_out,
-                                 libxl__ev_child_callback *death);
-static inline void libxl__ev_child_init(libxl__ev_child *childw_out)
-                { childw_out->pid = -1; }
-static inline int libxl__ev_child_inuse(const libxl__ev_child *childw_out)
-                { return childw_out->pid >= 0; }
-_hidden void libxl__ev_child_kill_deregister(libxl__ao *ao,
-                                             libxl__ev_child *ch,
-                                             int sig);
-
-/* Useable (only) in the child to once more make the ctx useable for
- * xenstore operations.  logs failure in the form "what: <error
- * message>". */
-_hidden int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what);
-
-
-/*
- * Other event-handling support provided by the libxl event core to
- * the rest of libxl.
- */
-
-_hidden void libxl__event_occurred(libxl__egc*, libxl_event *event);
-  /* Arranges to notify the application that the event has occurred.
-   * event should be suitable for passing to libxl_event_free. */
-
-_hidden libxl_event *libxl__event_new(libxl__egc*, libxl_event_type,
-                                      uint32_t domid,
-                                      libxl_ev_user for_user);
-  /* Convenience function.
-   * Allocates a new libxl_event, fills in domid and type.
-   * Cannot fail. */
-
-#define NEW_EVENT(egc, type, domid, user)                        \
-    libxl__event_new((egc), LIBXL_EVENT_TYPE_##type, (domid), (user))
-    /* Convenience macro. */
-
-/*
- * In general, call this via the macro LIBXL__EVENT_DISASTER.
- *
- * Event-generating functions, or ao machinery, may call this if they
- * might have wanted to generate an event (either an internal one ie a
- * libxl__ev_FOO_callback or an application event), but are prevented
- * from doing so due to eg lack of memory.
- *
- * NB that this function may return and the caller isn't supposed to
- * then crash, although it may fail (and henceforth leave things in a
- * state where many or all calls fail).
- */
-_hidden void libxl__event_disaster(libxl__gc*, const char *msg, int errnoval,
-                                   libxl_event_type type /* may be 0 */,
-                                   const char *file, int line,
-                                   const char *func);
-#define LIBXL__EVENT_DISASTER(gc, msg, errnoval, type) \
-    libxl__event_disaster(gc, msg, errnoval, type, __FILE__,__LINE__,__func__)
-
-
-/* Fills in, or disposes of, the resources held by, a poller whose
- * space the caller has allocated.  ctx must be locked. */
-_hidden int libxl__poller_init(libxl__gc *gc, libxl__poller *p);
-_hidden void libxl__poller_dispose(libxl__poller *p);
-
-/* Obtain a fresh poller from malloc or the idle list, and put it
- * away again afterwards.  _get can fail, returning NULL.
- * ctx must be locked. */
-_hidden libxl__poller *libxl__poller_get(libxl__gc *gc);
-_hidden void libxl__poller_put(libxl_ctx*, libxl__poller *p /* may be NULL */);
-
-/* Notifies whoever is polling using p that they should wake up.
- * ctx must be locked. */
-_hidden void libxl__poller_wakeup(libxl__gc *egc, libxl__poller *p);
-
-/* Internal to fork and child reaping machinery */
-extern const libxl_childproc_hooks libxl__childproc_default_hooks;
-int libxl__sigchld_needed(libxl__gc*); /* non-reentrant idempotent, logs errs */
-void libxl__sigchld_notneeded(libxl__gc*); /* non-reentrant idempotent */
-void libxl__sigchld_check_stale_handler(void);
-int libxl__self_pipe_wakeup(int fd); /* returns 0 or -1 setting errno */
-int libxl__self_pipe_eatall(int fd); /* returns 0 or -1 setting errno */
-
-
-_hidden int libxl__atfork_init(libxl_ctx *ctx);
-
-
-/* File references */
-typedef struct {
-    /*
-     * Path is always set if the file reference is valid. However if
-     * mapped is true then the actual file may already be unlinked.
-     */
-    const char * path;
-    int mapped;
-    void * data;
-    size_t size;
-} libxl__file_reference;
-_hidden int libxl__file_reference_map(libxl__file_reference *f);
-_hidden int libxl__file_reference_unmap(libxl__file_reference *f);
-
-/* from xl_dom */
-_hidden libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid);
-_hidden int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid);
-_hidden libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid);
-_hidden int libxl__sched_set_params(libxl__gc *gc, uint32_t domid,
-                                    libxl_domain_sched_params *scparams);
-_hidden int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
-                                              libxl_domain_config *const d_config);
-
-typedef struct {
-    uint32_t store_port;
-    uint32_t store_domid;
-    unsigned long store_mfn;
-
-    uint32_t console_port;
-    uint32_t console_domid;
-    unsigned long console_mfn;
-    char *console_tty;
-
-    char *saved_state;
-    int dm_monitor_fd;
-
-    libxl__file_reference pv_kernel;
-    libxl__file_reference pv_ramdisk;
-    const char *shim_path;
-    const char *shim_cmdline;
-    const char *pv_cmdline;
-
-    /* 
-     * dm_runas: If set, pass qemu the `-runas` parameter with this
-     *  string as an argument
-     * dm_kill_uid: If set, the devicemodel should be killed by
-     *  destroying all processes with this uid.
-     */
-    char *dm_runas, *dm_kill_uid;
-
-    xen_vmemrange_t *vmemranges;
-    uint32_t num_vmemranges;
-
-    xen_pfn_t vuart_gfn;
-    evtchn_port_t vuart_port;
-
-    /* ARM only to deal with broken firmware */
-    uint32_t clock_frequency;
-
-    /* Whether this domain is being migrated/restored, or booting fresh.  Only
-     * applicable to the primary domain, not support domains (e.g. stub QEMU). */
-    bool restore;
-} libxl__domain_build_state;
-
-_hidden void libxl__domain_build_state_init(libxl__domain_build_state *s);
-_hidden void libxl__domain_build_state_dispose(libxl__domain_build_state *s);
-
-_hidden int libxl__build_pre(libxl__gc *gc, uint32_t domid,
-              libxl_domain_config * const d_config,
-              libxl__domain_build_state *state);
-_hidden int libxl__build_post(libxl__gc *gc, uint32_t domid,
-               libxl_domain_build_info *info, libxl__domain_build_state *state,
-               char **vms_ents, char **local_ents);
-
-_hidden int libxl__build_pv(libxl__gc *gc, uint32_t domid,
-             libxl_domain_config *const d_config, libxl__domain_build_state *state);
-_hidden int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
-              libxl_domain_config *d_config,
-              libxl__domain_build_state *state);
-
-_hidden int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid,
-                                        const char *cmd);
-_hidden int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
-                                 const char *old_name, const char *new_name,
-                                 xs_transaction_t trans);
-
-/* Deprecated, use libxl__dm_resume instead. */
-_hidden int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid);
-
-_hidden const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid,
-                                         const char *userdata_userid,
-                                         const char *wh);
-_hidden void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid);
-/* Caller must hold userdata store lock before calling
- * libxl__userdata_{retrieve,store}
- * See libxl__{un,}lock_domain_userdata.
- */
-_hidden int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid,
-                                     const char *userdata_userid,
-                                     uint8_t **data_r, int *datalen_r);
-_hidden int libxl__userdata_store(libxl__gc *gc, uint32_t domid,
-                                  const char *userdata_userid,
-                                  const uint8_t *data, int datalen);
-
-/* Deprecated, use libxl__domain_resume instead */
-_hidden int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid,
-                                            int suspend_cancel);
-/* Deprecated, use libxl__domain_unpause instead */
-_hidden int libxl__domain_unpause_deprecated(libxl__gc *,
-                                             libxl_domid domid);
-
-/* Call libxl__dm_resume_init() and fill the first few fields,
- * then call one of libxl__domain_resume / libxl__domain_unpause
- * or directly libxl__dm_resume if only the device model needs to be
- * "resumed". */
-struct libxl__dm_resume_state {
-    /* caller must fill these in, and they must all remain valid */
-    libxl__ao *ao;
-    libxl_domid domid;
-    void (*callback)(libxl__egc *, libxl__dm_resume_state *, int rc);
-
-    /* private to libxl__domain_resume and libxl__domain_unpause */
-    void (*dm_resumed_callback)(libxl__egc *,
-                                libxl__dm_resume_state *, int rc);
-    /* private to libxl__domain_resume */
-    bool suspend_cancel;
-
-    /* private to libxl__dm_resume */
-    libxl__ev_qmp qmp;
-    libxl__ev_time time;
-    libxl__ev_xswatch watch;
-};
-_hidden void libxl__dm_resume(libxl__egc *egc,
-                              libxl__dm_resume_state *dmrs);
-_hidden void libxl__domain_resume(libxl__egc *egc,
-                                  libxl__dm_resume_state *dmrs,
-                                  bool suspend_cancel);
-_hidden void libxl__domain_unpause(libxl__egc *,
-                                   libxl__dm_resume_state *dmrs);
-
-/* returns 0 or 1, or a libxl error code */
-_hidden int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid);
-
-_hidden const char *libxl__domain_pvcontrol_xspath(libxl__gc*, uint32_t domid);
-_hidden char * libxl__domain_pvcontrol_read(libxl__gc *gc,
-                                            xs_transaction_t t, uint32_t domid);
-
-/* from xl_device */
-_hidden char *libxl__device_disk_string_of_backend(libxl_disk_backend backend);
-_hidden char *libxl__device_disk_string_of_format(libxl_disk_format format);
-_hidden const char *libxl__qemu_disk_format_string(libxl_disk_format format);
-_hidden int libxl__device_disk_set_backend(libxl__gc*, libxl_device_disk*);
-
-_hidden int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor);
-_hidden int libxl__device_disk_dev_number(const char *virtpath,
-                                          int *pdisk, int *ppartition);
-_hidden char *libxl__devid_to_vdev(libxl__gc *gc, int devid);
-
-_hidden int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
-                                      libxl__device_console *console,
-                                      libxl__domain_build_state *state,
-                                      libxl__device *device);
-_hidden int libxl__device_vuart_add(libxl__gc *gc, uint32_t domid,
-                                    libxl__device_console *console,
-                                    libxl__domain_build_state *state);
-
-/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
-_hidden int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
-                                 libxl__device *device);
-_hidden int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
-        libxl__device *device, char **bents, char **fents, char **ro_fents);
-_hidden char *libxl__domain_device_frontend_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
-                                                 libxl__device_kind device_kind);
-_hidden char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device);
-_hidden char *libxl__domain_device_backend_path(libxl__gc *gc, uint32_t backend_domid,
-                                                uint32_t domid, uint32_t devid,
-                                                libxl__device_kind device_kind);
-_hidden char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device);
-_hidden char *libxl__domain_device_libxl_path(libxl__gc *gc, uint32_t domid, uint32_t devid,
-                                              libxl__device_kind device_kind);
-_hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path,
-                                      libxl__device *dev);
-_hidden int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num,
-                                    libxl_console_type type, char **tty_path);
-_hidden int libxl__device_destroy(libxl__gc *gc, libxl__device *dev);
-_hidden int libxl__wait_for_backend(libxl__gc *gc, const char *be_path,
-                                    const char *state);
-_hidden int libxl__nic_type(libxl__gc *gc, libxl__device *dev,
-                            libxl_nic_type *nictype);
-_hidden int libxl__init_console_from_channel(libxl__gc *gc,
-                                             libxl__device_console *console,
-                                             int dev_num,
-                                             libxl_device_channel *channel);
-_hidden int libxl__device_nextid(libxl__gc *gc, uint32_t domid,
-                                 libxl__device_kind device);
-_hidden int libxl__resolve_domid(libxl__gc *gc, const char *name,
-                                 uint32_t *domid);
-
-/*
- * For each aggregate type which can be used as an input we provide:
- *
- * int libxl__<type>_setdefault(gc, <type> *p):
- *
- *     Idempotently sets any members of "p" which is currently set to
- *     a special value indicating that the defaults should be used
- *     (per libxl_<type>_init) to a specific value.
- *
- *     All libxl API functions are expected to have arranged for this
- *     to be called before using any values within these structures.
- */
-_hidden int libxl__domain_config_setdefault(libxl__gc *gc,
-                                            libxl_domain_config *d_config,
-                                            uint32_t domid /* logging only */);
-_hidden int libxl__domain_create_info_setdefault(libxl__gc *gc,
-                                        libxl_domain_create_info *c_info,
-                                        const libxl_physinfo *info);
-_hidden int libxl__domain_build_info_setdefault(libxl__gc *gc,
-                                        libxl_domain_build_info *b_info);
-_hidden void libxl__rdm_setdefault(libxl__gc *gc,
-                                   libxl_domain_build_info *b_info);
-
-_hidden int libxl__domain_need_memory_calculate(libxl__gc *gc,
-                                      libxl_domain_build_info *b_info,
-                                      uint64_t *need_memkb);
-
-_hidden const char *libxl__device_nic_devname(libxl__gc *gc,
-                                              uint32_t domid,
-                                              uint32_t devid,
-                                              libxl_nic_type type);
-
-_hidden int libxl__get_domid(libxl__gc *gc, uint32_t *domid);
-
-/*----- xswait: wait for a xenstore node to be suitable -----*/
-
-typedef struct libxl__xswait_state libxl__xswait_state;
-
-/*
- * rc describes the circumstances of this callback:
- *
- * rc==0
- *
- *     The xenstore path (may have) changed.  It has been read for
- *     you.  The result is in data (allocated from the ao gc).
- *     data may be NULL, which means that the xenstore read gave
- *     ENOENT.
- *
- *     If you are satisfied, you MUST call libxl__xswait_stop.
- *     Otherwise, xswait will continue waiting and watching and
- *     will call you back later.
- *
- * rc==ERROR_TIMEDOUT, rc==ERROR_ABORTED
- *
- *     The specified timeout was reached.
- *     This has NOT been logged (except to the debug log).
- *     xswait will not continue (but calling libxl__xswait_stop is OK).
- *
- * rc!=0, !=ERROR_TIMEDOUT, !=ERROR_ABORTED
- *
- *     Some other error occurred.
- *     This HAS been logged.
- *     xswait will not continue (but calling libxl__xswait_stop is OK).
- *
- * xswait.path may start with with '@', in which case no read is done
- * and the callback will always get data==0.
- */
-typedef void libxl__xswait_callback(libxl__egc *egc,
-      libxl__xswait_state *xswa, int rc, const char *data);
-
-struct libxl__xswait_state {
-    /* caller must fill these in, and they must all remain valid */
-    libxl__ao *ao;
-    const char *what; /* for error msgs: noun phrase, what we're waiting for */
-    const char *path;
-    int timeout_ms; /* as for poll(2) */
-    libxl__xswait_callback *callback;
-    /* remaining fields are private to xswait */
-    libxl__ev_time time_ev;
-    libxl__ev_xswatch watch_ev;
-};
-
-void libxl__xswait_init(libxl__xswait_state*);
-void libxl__xswait_stop(libxl__gc*, libxl__xswait_state*); /*idempotent*/
-bool libxl__xswait_inuse(const libxl__xswait_state *ss);
-
-int libxl__xswait_start(libxl__gc*, libxl__xswait_state*);
-
-/*
- * libxl__ev_devstate - waits a given time for a device to
- * reach a given state.  Follows the libxl_ev_* conventions.
- * Will generate only one event, and after that is automatically
- * cancelled.
- */
-typedef struct libxl__ev_devstate libxl__ev_devstate;
-typedef void libxl__ev_devstate_callback(libxl__egc *egc, libxl__ev_devstate*,
-                                         int rc);
-  /* rc will be 0, ERROR_TIMEDOUT, ERROR_ABORTED, ERROR_INVAL
-   * (meaning path was removed), or ERROR_FAIL if other stuff went
-   * wrong (in which latter case, logged) */
-
-struct libxl__ev_devstate {
-    /* read-only for caller, who may read only when waiting: */
-    int wanted;
-    libxl__ev_devstate_callback *callback;
-    /* as for the remainder, read-only public parts may also be
-     * read by the caller (notably, watch.path), but only when waiting: */
-    libxl__xswait_state w;
-};
-
-static inline void libxl__ev_devstate_init(libxl__ev_devstate *ds)
-{
-    libxl__xswait_init(&ds->w);
-}
-
-static inline void libxl__ev_devstate_cancel(libxl__gc *gc,
-                                             libxl__ev_devstate *ds)
-{
-    libxl__xswait_stop(gc,&ds->w);
-}
-
-_hidden int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds,
-                                    libxl__ev_devstate_callback cb,
-                                    const char *state_path,
-                                    int state, int milliseconds);
-
-/*
- * libxl__ev_domaindeathcheck_register - arranges to call back (once)
- * if the domain is destroyed.  If the domain dies, we log a message
- * of the form "<what>: <explanation of the situation, including the domid>".
- */
-
-typedef struct libxl__domaindeathcheck libxl__domaindeathcheck;
-typedef void libxl___domaindeathcheck_callback(libxl__egc *egc,
-        libxl__domaindeathcheck*,
-        int rc /* DESTROYED or ABORTED */);
-
-struct libxl__domaindeathcheck {
-    /* must be filled in by caller, and remain valid: */
-    const char *what;
-    uint32_t domid;
-    libxl___domaindeathcheck_callback *callback;
-    /* private */
-    libxl__ao_abortable abrt;
-    libxl__ev_xswatch watch;
-};
-
-_hidden int libxl__domaindeathcheck_start(libxl__ao *ao,
-                                          libxl__domaindeathcheck *dc);
-
-void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc);
-void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc);
-
-
-/*
- * libxl__try_phy_backend - Check if there's support for the passed
- * type of file using the PHY backend
- * st_mode: mode_t of the file, as returned by stat function
- *
- * Returns 1 on success, and 0 if not suitable for phy backend.
- */
-_hidden int libxl__try_phy_backend(mode_t st_mode);
-
-
-_hidden char *libxl__devid_to_localdev(libxl__gc *gc, int devid);
-
-_hidden int libxl__pci_numdevs(libxl__gc *gc);
-_hidden int libxl__pci_topology_init(libxl__gc *gc,
-                                     physdev_pci_device_t *devs,
-                                     int num_devs);
-
-/* from libxl_pci */
-
-_hidden void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
-                                   libxl_device_pci *pcidev, bool starting,
-                                   libxl__ao_device *aodev);
-_hidden void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
-                                           libxl__multidev *);
-_hidden int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
-                                         libxl_device_pci *pci, bool hotplug);
-_hidden bool libxl__is_igd_vga_passthru(libxl__gc *gc,
-                                        const libxl_domain_config *d_config);
-
-/* from libxl_dtdev */
-
-_hidden int libxl__device_dt_add(libxl__gc *gc, uint32_t domid,
-                                 const libxl_device_dtdev *dtdev);
-
-/*
- *----- spawn -----
- *
- * Higher-level double-fork and separate detach eg as for device models
- *
- * Each libxl__spawn_state is in one of these states
- *    Undefined, Idle, Attached, Detaching
- */
-
-typedef struct libxl__obsolete_spawn_starting libxl__spawn_starting;
-/* this type is never defined, so no objects of this type exist
- * fixme-ao  This should go away completely.  */
-
-typedef struct libxl__spawn_state libxl__spawn_state;
-
-/* Clears out a spawn state; idempotent. */
-_hidden void libxl__spawn_init(libxl__spawn_state*);
-
-/*
- * libxl__spawn_spawn - Create a new process which will become daemonic
- * Forks twice, to allow the child to detach entirely from the parent.
- *
- * We call the two generated processes the "middle child" (result of
- * the first fork) and the "inner child" (result of the second fork
- * which takes place in the middle child).
- *
- * The inner child must soon exit or exec.  It must also soon exit or
- * notify the parent of its successful startup by writing to the
- * xenstore path xspath OR via other means that the parent will have
- * to set up.
- *
- * The user (in the parent) will be called back (confirm_cb) every
- * time that xenstore path is modified.
- *
- * In both children, the ctx is not fully usable: gc and logging
- * operations are OK, but operations on Xen and xenstore are not.
- * (The restrictions are the same as those which apply to children
- * made with libxl__ev_child_fork.)
- *
- * midproc_cb will be called in the middle child, with the pid of the
- * inner child; this could for example record the pid.  midproc_cb
- * should be fast, and should return.  It will be called (reentrantly)
- * within libxl__spawn_init.
- *
- * failure_cb will be called in the parent on failure of the
- * intermediate or final child; an error message will have been
- * logged.
- *
- * confirm_cb, failure_cb and detached_cb will not be called
- * reentrantly from within libxl__spawn_spawn.
- *
- * what: string describing the spawned process, used for logging
- *
- * Logs errors.  A copy of "what" is taken.
- * Return values:
- *  < 0   error, *spawn is now Idle and need not be detached
- *   +1   caller is the parent, *spawn is Attached and must be detached
- *    0   caller is now the inner child, should probably call libxl__exec
- *
- * The spawn state must be Undefined or Idle on entry.
- */
-_hidden int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *spawn);
-
-/*
- * libxl__spawn_request_detach - Detaches the daemonic child.
- *
- * Works by killing the intermediate process from spawn_spawn.
- * After this function returns, failures of either child are no
- * longer reported via failure_cb.
- *
- * This is not synchronous: there will be a further callback when
- * the detach is complete.
- *
- * If called before the inner child has been created, this may prevent
- * it from running at all.  Thus this should be called only when the
- * inner child has notified that it is ready.  Normally it will be
- * called from within confirm_cb.
- *
- * Logs errors.
- *
- * The spawn state must be Attached entry and will be Detaching
- * on return.
- */
-_hidden void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state*);
-
-/*
- * libxl__spawn_initiate_failure - Propagate failure from the caller to the
- * callee.
- *
- * Works by killing the intermediate process from spawn_spawn.
- * After this function returns, a failure will be reported.
- *
- * This is not synchronous: there will be a further callback when
- * the detach is complete.
- *
- * Caller must have logged a failure reason.
- *
- * The spawn state must be Attached on entry and will remain Attached. It
- * is possible for a spawn to fail for multiple reasons, for example
- * call(s) to libxl__spawn_initiate_failure and also for some other reason.
- * In that case the first rc value from any source will take precedence.
- */
-_hidden void libxl__spawn_initiate_failure(libxl__egc *egc,
-                                           libxl__spawn_state *ss, int rc);
-
-/*
- * If successful, this should return 0.
- *
- * Otherwise it should return a signal number, which will be
- * sent to the inner child; the overall spawn will then fail.
- */
-typedef int /* signal number */
-libxl__spawn_midproc_cb(libxl__gc*, libxl__spawn_state*, pid_t inner);
-
-/*
- * Called if the spawn failed.  The reason will have been logged.
- * The spawn state will be Idle on entry to the callback (and
- * it may be reused immediately if desired).
- */
-typedef void libxl__spawn_failure_cb(libxl__egc*, libxl__spawn_state*,
-                                     int rc);
-
-/*
- * Called when the xspath watch triggers.  xspath will have been read
- * and the result placed in xsdata; if that failed because the key
- * didn't exist, xspath==0.  (If it failed for some other reason,
- * the spawn machinery calls failure_cb instead.)
- *
- * If the child has indicated its successful startup, or a failure
- * has occurred, this should call libxl__spawn_detach.
- *
- * If the child is still starting up, should simply return, doing
- * nothing.
- *
- * The spawn state will be Active on entry to the callback; there
- * are no restrictions on the state on return; it may even have
- * been detached and reused.
- */
-typedef void libxl__spawn_confirm_cb(libxl__egc*, libxl__spawn_state*,
-                                     const char *xsdata);
-
-/*
- * Called when the detach (requested by libxl__spawn_initiate_detach) has
- * completed.  On entry to the callback the spawn state is Idle.
- */
-typedef void libxl__spawn_detached_cb(libxl__egc*, libxl__spawn_state*);
-
-struct libxl__spawn_state {
-    /* must be filled in by user and remain valid */
-    libxl__ao *ao;
-    const char *what;
-    const char *xspath;
-    const char *pidpath; /* only used by libxl__spawn_midproc_record_pid */
-    int timeout_ms; /* -1 means forever */
-    libxl__spawn_midproc_cb *midproc_cb;
-    libxl__spawn_failure_cb *failure_cb;
-    libxl__spawn_confirm_cb *confirm_cb;
-    libxl__spawn_detached_cb *detached_cb;
-
-    /* remaining fields are private to libxl_spawn_... */
-    int detaching; /* we are in Detaching */
-    int rc; /* might be non-0 whenever we are not Idle */
-    libxl__ev_child mid; /* always in use whenever we are not Idle */
-    libxl__xswait_state xswait;
-};
-
-static inline int libxl__spawn_inuse(const libxl__spawn_state *ss)
-    { return libxl__ev_child_inuse(&ss->mid); }
-
-/*
- * libxl_spawner_record_pid - Record given pid in xenstore
- *
- * This function can be passed directly as an intermediate_hook to
- * libxl__spawn_spawn.  On failure, returns the value SIGTERM.
- */
-_hidden int libxl__spawn_record_pid(libxl__gc*, libxl__spawn_state*,
-                                    pid_t innerchild);
-
-/*
- * libxl__xenstore_child_wait_deprecated - Wait for daemonic child IPC
- *
- * This is a NOT function for waiting for ordinary child processes.
- * If you want to run (fork/exec/wait) subprocesses from libxl:
- *  - Make your libxl entrypoint use the ao machinery
- *  - Use libxl__ev_child_fork, and use the callback programming style
- *
- * This function is intended for interprocess communication with a
- * service process.  If the service process does not respond quickly,
- * the whole caller may be blocked.  Therefore this function is
- * deprecated.  This function is currently used only by
- * libxl__wait_for_device_model_deprecated.
- *
- * gc: allocation pool
- * domid: guest to work with
- * timeout: how many seconds to wait for the state to appear
- * what: string describing the spawned process
- * path: path to the state file in xenstore
- * state: expected string to wait for in path (optional)
- * spawning: malloc'd pointer to libxl__spawn_starting (optional)
- * check_callback: (optional)
- * check_callback_userdata: data to pass to the callback function
- *
- * Returns 0 on success, and < 0 on error.
- *
- * This function waits the given timeout for the given path to appear
- * in xenstore, and optionally for state in path.
- * If path appears and state matches, check_callback is called.
- * If check_callback returns > 0, waiting for path or state continues.
- * Otherwise libxl__xenstore_child_wait_deprecated returns.
- */
-_hidden int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
-                                 uint32_t domid,
-                                 uint32_t timeout, char *what,
-                                 char *path, char *state,
-                                 libxl__spawn_starting *spawning,
-                                 int (*check_callback)(libxl__gc *gc,
-                                                       uint32_t domid,
-                                                       const char *state,
-                                                       void *userdata),
-                                 void *check_callback_userdata);
-
-
- /* low-level stuff, for synchronous subprocesses etc. */
-
-/*
- * env should be passed using the following format,
- *
- * env[0]: name of env variable
- * env[1]: value of env variable
- * env[n]: ...
- *
- * So it efectively becomes something like:
- * export env[n]=env[n+1]
- * (where n%2 = 0)
- *
- * The last entry of the array always has to be NULL.
- *
- * stdinfd, stdoutfd, stderrfd will be dup2'd onto the corresponding
- * fd in the child, if they are not -1.  The original copy of the
- * descriptor will be closed in the child (unless it's 0, 1 or 2
- * ie the source descriptor is itself stdin, stdout or stderr).
- *
- * Logs errors, never returns.
- */
-_hidden  void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd,
-                          int stderrfd, const char *arg0, char *const args[],
-                          char *const env[]) __attribute__((noreturn));
-
-/* from xl_create */
-
- /* on entry, libxl_domid_valid_guest(domid) must be false;
-  * on exit (even error exit), domid may be valid and refer to a domain */
-_hidden int libxl__domain_make(libxl__gc *gc,
-                               libxl_domain_config *d_config,
-                               libxl__domain_build_state *state,
-                               uint32_t *domid, bool soft_reset);
-
-_hidden int libxl__domain_build(libxl__gc *gc,
-                                libxl_domain_config *d_config,
-                                uint32_t domid,
-                                libxl__domain_build_state *state);
-
-/* for device model creation */
-_hidden const char *libxl__domain_device_model(libxl__gc *gc,
-                                        const libxl_domain_build_info *info);
-_hidden int libxl__need_xenpv_qemu(libxl__gc *gc,
-                                   libxl_domain_config *d_config);
-_hidden bool libxl__query_qemu_backend(libxl__gc *gc,
-                                       uint32_t domid,
-                                       uint32_t backend_id,
-                                       const char *type,
-                                       bool def);
-_hidden int libxl__dm_active(libxl__gc *gc, uint32_t domid);
-_hidden int libxl__dm_check_start(libxl__gc *gc,
-                                  libxl_domain_config *d_config,
-                                  uint32_t domid);
-
-/*
- * This function will fix reserved device memory conflict
- * according to user's configuration.
- */
-_hidden int libxl__domain_device_construct_rdm(libxl__gc *gc,
-                                   libxl_domain_config *d_config,
-                                   uint64_t rdm_mem_guard,
-                                   struct xc_dom_image *dom);
-
-/*
- * This function will cause the whole libxl process to hang
- * if the device model does not respond.  It is deprecated.
- *
- * Instead of calling this function:
- *  - Make your libxl entrypoint use the ao machinery
- *  - Use libxl__ev_xswatch_register, and use the callback programming
- *    style
- */
-_hidden int libxl__wait_for_device_model_deprecated(libxl__gc *gc,
-                                uint32_t domid, char *state,
-                                libxl__spawn_starting *spawning
-                                                    /* NULL allowed */,
-                                int (*check_callback)(libxl__gc *gc,
-                                                      uint32_t domid,
-                                                      const char *state,
-                                                      void *userdata),
-                                void *check_callback_userdata);
-
-_hidden const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *g_cfg);
-
-_hidden char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path);
-
-#define LIBXL__LOG_DEBUG   XTL_DEBUG
-#define LIBXL__LOG_VERBOSE XTL_VERBOSE
-#define LIBXL__LOG_INFO    XTL_INFO
-#define LIBXL__LOG_WARNING XTL_WARN
-#define LIBXL__LOG_ERROR   XTL_ERROR
-
-_hidden char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid);
-_hidden char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid);
-
-_hidden int libxl__enum_from_string(const libxl_enum_string_table *t,
-                                    const char *s, int *e) NN(2);
-
-_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
-
-_hidden yajl_gen_status libxl__string_gen_json(yajl_gen hand, const char *p);
-
-typedef yajl_gen_status (*libxl__gen_json_callback)(yajl_gen hand, void *);
-_hidden char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
-                                    libxl__gen_json_callback gen, void *p);
-
-_hidden void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool retore,
-                                 libxl_domain_build_info *info);
-
-/* Calls poll() again - useful to check whether a signaled condition
- * is still true.  Cannot fail.  Returns currently-true revents. */
-_hidden short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events);
-
-_hidden char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid);
-
-struct libxl__xen_console_reader {
-    char *buffer;
-    unsigned int size;
-    unsigned int count;
-    unsigned int clear;
-    unsigned int incremental;
-    unsigned int index;
-};
-
-/* parse the string @s as a sequence of 6 colon separated bytes in to @mac */
-_hidden int libxl__parse_mac(const char *s, libxl_mac mac);
-/* compare mac address @a and @b. 0 if the same, -ve if a<b and +ve if a>b */
-_hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b);
-/* return true if mac address is all zero (the default value) */
-_hidden int libxl__mac_is_default(libxl_mac *mac);
-/* init a recursive mutex */
-_hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock);
-
-_hidden int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r);
-
-#define STRINGIFY(x) #x
-#define TOSTRING(x) STRINGIFY(x)
-
-/* from libxl_qmp */
-typedef struct libxl__qmp_handler libxl__qmp_handler;
-
-/* Initialise and connect to the QMP socket.
- *   Return an handler or NULL if there is an error
- */
-_hidden libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc,
-                                                  uint32_t domid);
-/* Resume QEMU. */
-_hidden int libxl__qmp_resume(libxl__gc *gc, int domid);
-/* Load current QEMU state from file. */
-_hidden int libxl__qmp_restore(libxl__gc *gc, int domid, const char *filename);
-/* Start NBD server */
-_hidden int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
-                                        const char *host, const char *port);
-/* Add a disk to NBD server */
-_hidden int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid,
-                                      const char *disk);
-/* Start replication */
-_hidden int libxl__qmp_start_replication(libxl__gc *gc, int domid,
-                                         bool primary);
-/* Get replication error that occurs when the vm is running */
-_hidden int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid);
-/* Do checkpoint */
-_hidden int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid);
-/* Stop replication */
-_hidden int libxl__qmp_stop_replication(libxl__gc *gc, int domid,
-                                        bool primary);
-/* Stop NBD server */
-_hidden int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid);
-/* Add or remove a child to/from quorum */
-_hidden int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid,
-                                         const char *parant,
-                                         const char *child, const char *node);
-/* run a hmp command in qmp mode */
-_hidden int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
-                           char **out);
-/* close and free the QMP handler */
-_hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
-/* remove the socket file, if the file has already been removed,
- * nothing happen */
-_hidden void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid);
-
-/* `data' should contain a byte to send.
- * When dealing with a non-blocking fd, it returns
- *   ERROR_NOT_READY on EWOULDBLOCK
- * logs on other failures. */
-int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
-                       const char data,
-                       int nfds, const int fds[], const char *what);
-
-/* Insists on receiving exactly nfds and datalen.  On failure, logs
- * and leaves *fds untouched. */
-int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
-                       void *databuf, size_t datalen,
-                       int nfds, int fds[], const char *what);
-
-/* from libxl_json */
-#include <yajl/yajl_gen.h>
-
-_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str);
-_hidden yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str);
-
-typedef enum {
-    JSON_NULL    = (1 << 0),
-    JSON_BOOL    = (1 << 1),
-    JSON_INTEGER = (1 << 2),
-    JSON_DOUBLE  = (1 << 3),
-    /* number is store in string, it's too big to be a long long or a double */
-    JSON_NUMBER  = (1 << 4),
-    JSON_STRING  = (1 << 5),
-    JSON_MAP     = (1 << 6),
-    JSON_ARRAY   = (1 << 7),
-    JSON_ANY     = 255 /* this is a mask of all values above, adjust as needed */
-} libxl__json_node_type;
-
-struct libxl__json_object {
-    libxl__json_node_type type;
-    union {
-        bool b;
-        long long i;
-        double d;
-        char *string;
-        /* List of libxl__json_object */
-        flexarray_t *array;
-        /* List of libxl__json_map_node */
-        flexarray_t *map;
-    } u;
-    struct libxl__json_object *parent;
-};
-
-typedef int (*libxl__json_parse_callback)(libxl__gc *gc,
-                                          libxl__json_object *o,
-                                          void *p);
-_hidden int libxl__object_from_json(libxl_ctx *ctx, const char *type,
-                                    libxl__json_parse_callback parse,
-                                    void *p,
-                                    const char *s);
-
-typedef struct {
-    char *map_key;
-    libxl__json_object *obj;
-} libxl__json_map_node;
-
-static inline bool libxl__json_object_is_null(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_NULL;
-}
-static inline bool libxl__json_object_is_bool(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_BOOL;
-}
-static inline bool libxl__json_object_is_string(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_STRING;
-}
-static inline bool libxl__json_object_is_integer(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_INTEGER;
-}
-static inline bool libxl__json_object_is_double(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_DOUBLE;
-}
-static inline bool libxl__json_object_is_number(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_NUMBER;
-}
-static inline bool libxl__json_object_is_map(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_MAP;
-}
-static inline bool libxl__json_object_is_array(const libxl__json_object *o)
-{
-    return o != NULL && o->type == JSON_ARRAY;
-}
-
-/*
- * `o` may be NULL for all libxl__json_object_get_* functions.
- */
-
-static inline bool libxl__json_object_get_bool(const libxl__json_object *o)
-{
-    if (libxl__json_object_is_bool(o))
-        return o->u.b;
-    else
-        return false;
-}
-static inline
-const char *libxl__json_object_get_string(const libxl__json_object *o)
-{
-    if (libxl__json_object_is_string(o))
-        return o->u.string;
-    else
-        return NULL;
-}
-static inline
-const char *libxl__json_object_get_number(const libxl__json_object *o)
-{
-    if (libxl__json_object_is_number(o))
-        return o->u.string;
-    else
-        return NULL;
-}
-static inline
-flexarray_t *libxl__json_object_get_map(const libxl__json_object *o)
-{
-    if (libxl__json_object_is_map(o))
-        return o->u.map;
-    else
-        return NULL;
-}
-static inline
-flexarray_t *libxl__json_object_get_array(const libxl__json_object *o)
-{
-    if (libxl__json_object_is_array(o))
-        return o->u.array;
-    else
-        return NULL;
-}
-static inline long long libxl__json_object_get_integer(const libxl__json_object *o)
-{
-    if (libxl__json_object_is_integer(o))
-        return o->u.i;
-    else
-        return -1;
-}
-
-/*
- * `o` may be NULL for the following libxl__json_*_get functions.
- */
-
-_hidden libxl__json_object *libxl__json_array_get(const libxl__json_object *o,
-                                                  int i);
-_hidden
-libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
-                                               int i);
-_hidden const libxl__json_object *libxl__json_map_get(const char *key,
-                                          const libxl__json_object *o,
-                                          libxl__json_node_type expected_type);
-
-/*
- * NOGC can be used with those json_object functions, but the
- * libxl__json_object* will need to be freed with libxl__json_object_free.
- */
-_hidden libxl__json_object *libxl__json_object_alloc(libxl__gc *gc_opt,
-                                                     libxl__json_node_type type);
-_hidden yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc_opt,
-                                                   yajl_gen hand,
-                                                   const libxl__json_object *param);
-_hidden void libxl__json_object_free(libxl__gc *gc_opt,
-                                     libxl__json_object *obj);
-
-_hidden libxl__json_object *libxl__json_parse(libxl__gc *gc_opt, const char *s);
-
-/* `args` may be NULL */
-_hidden char *libxl__json_object_to_json(libxl__gc *gc,
-                                         const libxl__json_object *args);
-/* Always return a valid string, but invalid json on error. */
-#define JSON(o) \
-    (libxl__json_object_to_json(gc, (o)) ? : "<invalid-json-object>")
-
-  /* Based on /local/domain/$domid/dm-version xenstore key
-   * default is qemu xen traditional */
-_hidden int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid);
-  /* Return the system-wide default device model */
-_hidden libxl_device_model_version libxl__default_device_model(libxl__gc *gc);
-
-static inline
-bool libxl__stubdomain_is_linux_running(libxl__gc *gc, uint32_t domid)
-{
-    /* same logic as in libxl__stubdomain_is_linux */
-    return libxl__device_model_version_running(gc, domid)
-        == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-static inline
-bool libxl__stubdomain_is_linux(libxl_domain_build_info *b_info)
-{
-    /* right now qemu-tranditional implies MiniOS stubdomain and qemu-xen
-     * implies Linux stubdomain */
-    return libxl_defbool_val(b_info->device_model_stubdomain) &&
-        b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-#define DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, fmt, _a...)              \
-    libxl__sprintf(gc, "/local/domain/%u/device-model/%u" fmt, dm_domid,   \
-                   domid, ##_a)
-
-/*
- * Calling context and GC for event-generating functions:
- *
- * These are for use by parts of libxl which directly or indirectly
- * call libxl__event_occurred.  These contain a gc but also a list of
- * deferred events.
- *
- * You should never need to initialise an egc unless you are part of
- * the event machinery itself.  Otherwise you will always be given an
- * egc if you need one.  Even functions which generate specific kinds
- * of events don't need to - rather, they will be passed an egc into
- * their own callback function and should just use the one they're
- * given.
- *
- * Functions using LIBXL_INIT_EGC may *not* generally be called from
- * within libxl, because libxl__egc_cleanup may call back into the
- * application.  This should be enforced by declaring all such
- * functions in libxl.h or libxl_event.h with
- * LIBXL_EXTERNAL_CALLERS_ONLY.  You should in any case not find it
- * necessary to call egc-creators from within libxl.
- *
- * The callbacks must all take place with the ctx unlocked because
- * the application is entitled to reenter libxl from them.  This
- * would be bad not because the lock is not recursive (it is) but
- * because the application might make blocking libxl calls which
- * would hold the lock unreasonably long.
- *
- * For the same reason libxl__egc_cleanup (or EGC_FREE) must be called
- * with the ctx *unlocked*.  So the right pattern has the EGC_...
- * macro calls on the outside of the CTX_... ones.
- */
-
-/* useful for all functions which take an egc: */
-
-#define EGC_GC                                  \
-    libxl__gc *const gc __attribute__((unused)) = &egc->gc
-
-/* egc initialisation and destruction: */
-
-#define LIBXL_INIT_EGC(egc,ctx) do{                     \
-        LIBXL_INIT_GC((egc).gc,ctx);                    \
-        LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \
-        LIBXL_TAILQ_INIT(&(egc).aos_for_callback);      \
-        LIBXL_TAILQ_INIT(&(egc).aops_for_callback);     \
-        LIBXL_STAILQ_INIT(&(egc).ev_immediates);        \
-    } while(0)
-
-_hidden void libxl__egc_ao_cleanup_1_baton(libxl__gc *gc);
-  /* Passes the baton for added osevents.  See comment for
-   * osevents_added in struct libxl__poller. */
-_hidden void libxl__egc_cleanup_2_ul_cb_gc(libxl__egc *egc);
-  /* Frees memory allocated within this egc's gc, and and report all
-   * occurred events via callback, if applicable.  May reenter the
-   * application; see restrictions above.  The ctx must be UNLOCKED. */
-
-/* convenience macros: */
-
-#define EGC_INIT(ctx)                       \
-    libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx);      \
-    EGC_GC
-
-#define CTX_UNLOCK_EGC_FREE  do{                        \
-        libxl__egc_ao_cleanup_1_baton(&egc->gc);        \
-        CTX_UNLOCK;                                     \
-        libxl__egc_cleanup_2_ul_cb_gc(egc);             \
-    }while(0)
-
-
-/*
- * Machinery for asynchronous operations ("ao")
- *
- * All "slow" functions (see below for the exact definition) need to
- * use the asynchronous operation ("ao") machinery.  The function
- * should take a parameter const libxl_asyncop_how *ao_how and must
- * start with a call to AO_CREATE or equivalent.  These functions MAY
- * NOT be called from inside libxl (regardless of what is passed for
- * ao_how), because they can cause reentrancy hazards due to
- * callbacks.
- *
- * For the same reason functions taking an ao_how may make themselves
- * an egc with EGC_INIT (and they will generally want to, to be able
- * to immediately complete an ao during its setup).
- *
- *
- * "Slow" functions includes any that might block on a guest or an
- * external script.  More broadly, it includes any operations which
- * are sufficiently slow that an application might reasonably want to
- * initiate them, and then carry on doing something else, while the
- * operation completes.  That is, a "fast" function must be fast
- * enough that we do not mind blocking all other management operations
- * on the same host while it completes.
- *
- * There are certain primitive functions which make a libxl operation
- * necessarily "slow" for API reasons.  These are:
- *  - awaiting xenstore watches (although read-modify-write xenstore
- *    transactions are OK for fast functions)
- *  - spawning subprocesses
- *  - anything with a timeout
- *
- *
- * Lifecycle of an ao:
- *
- * - Created by libxl__ao_create (or the AO_CREATE convenience macro).
- *
- * - After creation, can be used by code which implements
- *   the operation as follows:
- *      - the ao's gc, for allocating memory for the lifetime
- *        of the operation (possibly with the help of the AO_GC
- *        macro to introduce the gc into scope)
- *      - the ao itself may be passed about to sub-functions
- *        so that they can stash it away etc.
- *      - in particular, the ao pointer must be stashed in some
- *        per-operation structure which is also passed as a user
- *        pointer to the internal event generation request routines
- *        libxl__evgen_FOO, so that at some point a CALLBACK will be
- *        made when the operation is complete.
- *      - if the operation provides progress reports, the aop_how(s)
- *        must be copied into the per-operation structure using
- *        libxl__ao_progress_gethow.
- *
- * - If the initiation is unsuccessful, the initiating function must
- *   call libxl__ao_create_fail before unlocking and returning whatever
- *   error code is appropriate (AO_CREATE_FAIL macro).
- *
- * If initiation is successful:
- *
- * - The initiating function must run libxl__ao_inprogress right
- *   before unlocking and returning, and return whatever it returns.
- *   This is best achieved with the AO_INPROGRESS macro.
- *
- * - If the operation supports progress reports, it may generate
- *   suitable events with NEW_EVENT and report them with
- *   libxl__ao_progress_report (with the ctx locked).
- *
- * - Eventually, some callback function, whose callback has been
- *   requested directly or indirectly, should call libxl__ao_complete
- *   (with the ctx locked, as it will generally already be in any
- *   event callback function).  This must happen exactly once for each
- *   ao, as the last that happens with that ao.
- *
- * - However, it is permissible for the initiating function to call
- *   libxl__ao_inprogress and/or libxl__ao_complete (directly or
- *   indirectly), before it uses AO_INPROGRESS to return.  (The ao
- *   infrastructure will arrange to defer destruction of the ao, etc.,
- *   until the proper time.)  An initiating function should do this
- *   if it takes a codepath which completes synchronously.
- *
- * - Conversely it is forbidden to call libxl__ao_complete in the
- *   initiating function _after_ AO_INPROGRESS, because
- *   libxl__ao_complete requires the ctx to be locked.
- *
- * - Note that during callback functions, two gcs are available:
- *    - The one in egc, whose lifetime is only this callback
- *    - The one in ao, whose lifetime is the asynchronous operation
- *   Usually a callback function should use CONTAINER_OF to obtain its
- *   own state structure, containing a pointer to the ao.  It should
- *   then obtain the ao and use the ao's gc; this is most easily done
- *   using the convenience macro STATE_AO_GC.
- */
-
-#define AO_CREATE(ctx, domid, ao_how)                           \
-    libxl__ctx_lock(ctx);                                       \
-    libxl__ao *ao = libxl__ao_create(ctx, domid, ao_how,        \
-                               __FILE__, __LINE__, __func__);   \
-    if (!ao) { libxl__ctx_unlock(ctx); return ERROR_NOMEM; }    \
-    libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx);              \
-    AO_GC;
-
-#define AO_INPROGRESS ({                                        \
-        libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc);          \
-        /* __ao_inprogress will do egc..1_baton if needed */   \
-        CTX_UNLOCK;                                             \
-        libxl__egc_cleanup_2_ul_cb_gc(egc);                     \
-        CTX_LOCK;                                               \
-        int ao__rc = libxl__ao_inprogress(ao,                   \
-                               __FILE__, __LINE__, __func__);   \
-        libxl__ctx_unlock(ao__ctx); /* gc is now invalid */     \
-        (ao__rc);                                               \
-   })
-
-#define AO_CREATE_FAIL(rc) ({                                   \
-        libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc);          \
-        assert(rc);                                             \
-        libxl__ao_create_fail(ao);                              \
-        libxl__egc_ao_cleanup_1_baton(&egc->gc);                \
-        libxl__ctx_unlock(ao__ctx); /* gc is now invalid */     \
-        libxl__egc_cleanup_2_ul_cb_gc(egc);                     \
-        (rc);                                                   \
-    })
-
-
-/*
- * Given, in scope,
- *   libxl__ao *ao;
- * produces, in scope,
- *   libxl__gc *gc;
- */
-#define AO_GC                                   \
-    libxl__gc *const gc __attribute__((unused)) = &ao->gc
-
-/*
- * void STATE_AO_GC(libxl__ao *ao_spec);
- * // Produces, in scope:
- *   libxl__ao *ao;  // set from ao_spec
- *   libxl__gc *gc;
- */
-#define STATE_AO_GC(op_ao)                      \
-    libxl__ao *const ao = (op_ao);              \
-    libxl__gc *const gc __attribute__((unused)) = libxl__ao_inprogress_gc(ao)
-
-
-/* All of these MUST be called with the ctx locked.
- * libxl__ao_inprogress MUST be called with the ctx locked exactly once. */
-_hidden libxl__ao *libxl__ao_create(libxl_ctx*, uint32_t domid,
-                                    const libxl_asyncop_how*,
-       const char *file, int line, const char *func);
-_hidden int libxl__ao_inprogress(libxl__ao *ao,
-       const char *file, int line, const char *func); /* temporarily unlocks */
-_hidden void libxl__ao_create_fail(libxl__ao *ao);
-_hidden void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc);
-_hidden libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao);
-
-/* Can be called at any time.  Use is essential for any aop user. */
-_hidden void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
-                                       const libxl_asyncprogress_how *from_app);
-
-/* Must be called with the ctx locked.  Will fill in ev->for_user,
- * so caller need not do that. */
-_hidden void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
-   const libxl_asyncprogress_how *how, libxl_event *ev /* consumed */);
-
-/* For use by ao machinery ONLY */
-_hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao);
-_hidden void libxl__ao_complete_check_progress_reports(libxl__egc*, libxl__ao*);
-
-
-/*
- * Short-lived sub-ao, aka "nested ao".
- *
- * Some asynchronous operations are very long-running.  Generally,
- * since an ao has a gc, any allocations made in that ao will live
- * until the ao is completed.  When this is not desirable, these
- * functions may be used to manage a "sub-ao".
- *
- * The returned sub-ao is suitable for passing to gc-related functions
- * and macros such as libxl__ao_inprogress_gc, AO_GC, and STATE_AO_GC.
- *
- * It MUST NOT be used with AO_INPROGRESS, AO_CREATE_FAIL,
- * libxl__ao_complete, libxl__ao_progress_report, and so on.
- *
- * The caller must ensure that all of the sub-ao's are freed before
- * the parent is.  Multiple levels of nesting are OK (although
- * hopefully they won't be necessary).
- */
-
-_hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent); /* cannot fail */
-_hidden void libxl__nested_ao_free(libxl__ao *child);
-
-
-/*
- * File descriptors and CLOEXEC
- */
-
-/*
- * For libxl functions which create file descriptors, at least one
- * of the following must be true:
- *  (a) libxl does not care if copies of this open-file are inherited
- *      by random children and might remain open indefinitely
- *  (b) libxl must take extra care for the fd (the actual descriptor,
- *      not the open-file) as below.  We call this a "carefd".
- *
- * The rules for opening a carefd are:
- *  (i)   Before bringing any carefds into existence,
- *        libxl code must call libxl__carefd_begin.
- *  (ii)  Then for each carefd brought into existence,
- *        libxl code must call libxl__carefd_record
- *        and remember the libxl__carefd_record*.
- *  (iii) Then it must call libxl__carefd_unlock.
- *  (iv)  When in a child process the fd is to be passed across
- *        exec by libxl, the libxl code must unset FD_CLOEXEC
- *        on the fd eg by using libxl_fd_set_cloexec.
- *  (v)   Later, when the fd is to be closed in the same process,
- *        libxl code must not call close.  Instead, it must call
- *        libxl__carefd_close.
- * Steps (ii) and (iii) can be combined by calling the convenience
- * function libxl__carefd_opened.
- */
-/* libxl__carefd_begin and _unlock (or _opened) must be called always
- * in pairs.  They may be called with the CTX lock held.  In between
- * _begin and _unlock, the following are prohibited:
- *   - anything which might block
- *   - any callbacks to the application
- *   - nested calls to libxl__carefd_begin
- *   - fork (libxl__fork)
- * In general nothing should be done before _unlock that could be done
- * afterwards.
- */
-
-_hidden void libxl__carefd_begin(void);
-_hidden void libxl__carefd_unlock(void);
-
-/* fd may be -1, in which case this returns a dummy libxl__fd_record
- * on which it _carefd_close is a no-op.  Cannot fail. */
-_hidden libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd);
-
-/* Combines _record and _unlock in a single call.  If fd==-1,
- * still does the unlock, but returns 0. */
-_hidden libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd);
-
-/* Works just like close(2).  You may pass NULL, in which case it's
- * a successful no-op. */
-_hidden int libxl__carefd_close(libxl__carefd*);
-
-/* You may pass NULL in which case the answer is -1. */
-_hidden int libxl__carefd_fd(const libxl__carefd*);
-
-/* common paths */
-_hidden const char *libxl__private_bindir_path(void);
-_hidden const char *libxl__xenfirmwaredir_path(void);
-_hidden const char *libxl__xen_config_dir_path(void);
-_hidden const char *libxl__xen_script_dir_path(void);
-_hidden const char *libxl__lock_dir_path(void);
-_hidden const char *libxl__run_dir_path(void);
-_hidden const char *libxl__seabios_path(void);
-_hidden const char *libxl__ovmf_path(void);
-_hidden const char *libxl__ipxe_path(void);
-
-/*----- subprocess execution with timeout -----*/
-
-typedef struct libxl__async_exec_state libxl__async_exec_state;
-
-typedef void libxl__async_exec_callback(libxl__egc *egc,
-                        libxl__async_exec_state *aes, int rc, int status);
-/*
- * Meaning of status and rc:
- *  rc==0, status==0    all went well
- *  rc==0, status!=0    everything OK except child exited nonzero (logged)
- *  rc!=0               something else went wrong (status is real
- *                       exit status; maybe reflecting SIGKILL, and
- *                       therefore not very interesting, if aes code
- *                       killed the child).  Logged unless ABORTED.
- */
-
-struct libxl__async_exec_state {
-    /* caller must fill these in */
-    libxl__ao *ao;
-    const char *what; /* for error msgs, what we're executing */
-    int timeout_ms;
-    libxl__async_exec_callback *callback;
-    /* caller must fill in; as for libxl__exec */
-    int stdfds[3];
-    char **args; /* execution arguments */
-    char **env; /* execution environment */
-
-    /* private */
-    libxl__ev_time time;
-    libxl__ev_child child;
-    int rc;
-};
-
-void libxl__async_exec_init(libxl__async_exec_state *aes);
-int libxl__async_exec_start(libxl__async_exec_state *aes);
-bool libxl__async_exec_inuse(const libxl__async_exec_state *aes);
-
-_hidden void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what);
-/* kill SIGHUP a pid stored in xenstore */
-_hidden int libxl__kill_xs_path(libxl__gc *gc, const char *xs_path_pid,
-                                const char *what);
-
-/*----- device addition/removal -----*/
-
-typedef void libxl__device_callback(libxl__egc*, libxl__ao_device*);
-
-/* This functions sets the necessary libxl__ao_device struct values to use
- * safely inside functions. It marks the operation as "active"
- * since we need to be sure that all device status structs are set
- * to active before start queueing events, or we might call
- * ao_complete before all devices had finished
- *
- * libxl__initiate_device_{remove/addition} should not be called without
- * calling libxl__prepare_ao_device first, since it initializes the private
- * fields of the struct libxl__ao_device to what this functions expect.
- *
- * Once _prepare has been called on a libxl__ao_device, it is safe to just
- * discard this struct, there's no need to call any destroy function.
- * _prepare can also be called multiple times with the same libxl__ao_device.
- *
- * But if any of the fields `backend_ds', `timeout', `xswait', `qmp' is
- * used by a caller of _prepare, the caller will have to arrange to clean
- * or dispose of them.
- */
-_hidden void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev);
-
-/* generic callback for devices that only need to set ao_complete */
-_hidden void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev);
-
-typedef struct {
-    enum {
-        LIBXL__FORCE_AUTO, /* Re-execute with FORCE_ON if op times out */
-        LIBXL__FORCE_ON,
-        LIBXL__FORCE_OFF,
-    } flag;
-} libxl__force;
-
-struct libxl__ao_device {
-    /* filled in by user */
-    libxl__ao *ao;
-    libxl__device_action action;
-    libxl__device *dev;
-    libxl__force force;
-    libxl__device_callback *callback;
-    /* return value, zeroed by user on entry, is valid on callback */
-    int rc;
-    /* private for multidev */
-    int active;
-    libxl__multidev *multidev; /* reference to the containing multidev */
-    /* private for add/remove implementation */
-    libxl__ev_devstate backend_ds;
-    /* Bodge for Qemu devices */
-    libxl__ev_time timeout;
-    /* xenstore watch for backend path of driver domains */
-    libxl__xswait_state xswait;
-    int num_exec;
-    /* for calling hotplug scripts */
-    libxl__async_exec_state aes;
-    /* If we need to update JSON config */
-    bool update_json;
-    /* for asynchronous execution of synchronous-only syscalls etc. */
-    libxl__ev_child child;
-    libxl__ev_qmp qmp;
-    /* 'device_config' can be used to to pass to callbacks a pointer of one
-     * of the type 'libxl_device_$type' corresponding to the device been
-     * hotplug. 'device_type' should have the corresponding
-     * 'libxl__$type_devtype'. */
-    void *device_config;
-    const libxl__device_type *device_type;
-};
-
-/*
- * Multiple devices "multidev" handling.
- *
- * Firstly, you should
- *    libxl__multidev_begin
- *    multidev->callback = ...
- * Then zero or more times
- *    libxl__multidev_prepare
- *    libxl__initiate_device_{remove/addition}
- *       (or some other thing which will eventually call
- *        aodev->callback or libxl__multidev_one_callback)
- * Finally, once
- *    libxl__multidev_prepared
- * which will result (perhaps reentrantly) in one call to
- * multidev->callback().
- */
-
-/* Starts preparing to add/remove a bunch of devices. */
-_hidden void libxl__multidev_begin(libxl__ao *ao, libxl__multidev*);
-
-/* Prepares to add/remove one of many devices.
- * Calls libxl__prepare_ao_device on libxl__ao_device argument provided and
- * also sets the aodev->callback (to libxl__multidev_one_callback)
- * The user should not mess with aodev->callback.
- */
-_hidden void libxl__multidev_prepare_with_aodev(libxl__multidev*,
-                                                libxl__ao_device*);
-
-/* A wrapper function around libxl__multidev_prepare_with_aodev.
- * Allocates a libxl__ao_device and prepares it for addition/removal.
- * Returns the newly allocated libxl__ao_dev.
- */
-_hidden libxl__ao_device *libxl__multidev_prepare(libxl__multidev*);
-
-/* Indicates to multidev that this one device has been processed.
- * Normally the multidev user does not need to touch this function, as
- * multidev_prepare will name it in aodev->callback.  However, if you
- * want to do something more complicated you can set aodev->callback
- * yourself to something else, so long as you eventually call
- * libxl__multidev_one_callback.
- */
-_hidden void libxl__multidev_one_callback(libxl__egc *egc,
-                                          libxl__ao_device *aodev);
-
-/* Notifies the multidev machinery that we have now finished preparing
- * and initiating devices.  multidev->callback may then be called as
- * soon as there are no prepared but not completed operations
- * outstanding, perhaps reentrantly.  If rc!=0 (error should have been
- * logged) multidev->callback will get a non-zero rc.
- * callback may be set by the user at any point before prepared. */
-_hidden void libxl__multidev_prepared(libxl__egc*, libxl__multidev*, int rc);
-
-typedef void libxl__devices_callback(libxl__egc*, libxl__multidev*, int rc);
-struct libxl__multidev {
-    /* set by user: */
-    libxl__devices_callback *callback;
-    /* for private use by libxl__...ao_devices... machinery: */
-    libxl__ao *ao;
-    libxl__ao_device **array;
-    int used, allocd;
-    libxl__ao_device *preparation;
-};
-
-/*
- * Algorithm for handling device removal (including domain
- * destruction).  This is somewhat subtle because we may already have
- * killed the domain and caused the death of qemu.
- *
- * In current versions of qemu there is no mechanism for ensuring that
- * the resources used by its devices (both emulated and any PV devices
- * provided by qemu) are freed (eg, fds closed) before it shuts down,
- * and no confirmation from a terminating qemu back to the toolstack.
- *
- * This will need to be fixed in future Xen versions. In the meantime
- * (Xen 4.2) we implement a bodge.
- *
- *      WE WANT TO UNPLUG         WE WANT TO SHUT DOWN OR DESTROY
- *                    |                           |
- *                    |             LIBXL SENDS SIGHUP TO QEMU
- *                    |      .....................|........................
- *                    |      : XEN 4.3+ PLANNED   |                       :
- *                    |      :      QEMU TEARS DOWN ALL DEVICES           :
- *                    |      :      FREES RESOURCES (closing fds)         :
- *                    |      :      SETS PV BACKENDS TO STATE 5,          :
- *                    |      :       waits for PV frontends to shut down  :
- *                    |      :       SETS PV BACKENDS TO STATE 6          :
- *                    |      :                    |                       :
- *                    |      :      QEMU NOTIFIES TOOLSTACK (via          :
- *                    |      :       xenstore) that it is exiting         :
- *                    |      :      QEMU EXITS (parent may be init)       :
- *                    |      :                    |                       :
- *                    |      :        TOOLSTACK WAITS FOR QEMU            :
- *                    |      :        notices qemu has finished           :
- *                    |      :....................|.......................:
- *                    |      .--------------------'
- *                    V      V
- *                  for each device
- *                 we want to unplug/remove
- *       ..................|...........................................
- *       :                 V                       XEN 4.2 RACY BODGE :
- *       :      device is provided by    qemu                         :
- *       :            |            `-----------.                      :
- *       :   something|                        V                      :
- *       :    else, eg|             domain (that is domain for which  :
- *       :     blkback|              this PV device is the backend,   :
- *       :            |              which might be the stub dm)      :
- *       :            |                is still alive?                :
- *       :            |                  |        |                   :
- *       :            |                  |alive   |dead               :
- *       :            |<-----------------'        |                   :
- *       :            |    hopefully qemu is      |                   :
- *       :            |       still running       |                   :
- *       :............|.................          |                   :
- *             ,----->|                :     we may be racing         :
- *             |    backend state?     :      with qemu's death       :
- *             ^      |         |      :          |                   :
- *     xenstore|      |other    |6     :      WAIT 2.0s               :
- *     conflict|      |         |      :       TIMEOUT                :
- *             |   WRITE B.E.   |      :          |                   :
- *             |    STATE:=5    |      :     hopefully qemu has       :
- *             `---'  |         |      :      gone by now and         :
- *                    |ok       |      :      freed its resources     :
- *                    |         |      :          |                   :
- *              WAIT FOR        |      :     SET B.E.                 :
- *              STATE==6        |      :      STATE:=6                :
- *              /     |         |      :..........|...................:
- *      timeout/    ok|         |                 |
- *            /       |         |                 |
- *           |    RUN HOTPLUG <-'<----------------'
- *           |      SCRIPT
- *           |        |
- *           `---> NUKE
- *                  BACKEND
- *                    |
- *                   DONE.
- */
-
-/*
- * As of Xen 4.5 we maintain various information, including hotplug
- * device information, in JSON files, so that we can use this JSON
- * file as a template to reconstruct domain configuration.
- *
- * In essense there are now two views of device state, one is the
- * primary config (xenstore or QEMU), the other is JSON file.
- *
- * Here we maintain one invariant: every device in the primary config
- * must have an entry in JSON file.
- *
- * All device hotplug routines should comply to following pattern:
- *   lock json config (json_lock)
- *       read json config
- *       update in-memory json config with new entry, replacing
- *          any stale entry
- *       for loop -- xs transaction
- *           open xs transaction
- *           check device existence, bail if it exists
- *           write in-memory json config to disk
- *           commit xs transaction
- *       end for loop
- *   unlock json config
- *
- * Or in case QEMU is the primary config, this pattern can be use:
- *   qmp_lock (libxl__ev_devlock_init)
- *      lock json config (json_lock)
- *          read json config
- *          update in-memory json config with new entry, replacing
- *             any stale entry
- *      unlock json config
- *      apply new config to primary config
- *      lock json config (json_lock)
- *          read json config
- *          update in-memory json config with new entry, replacing
- *             any stale entry
- *          write in-memory json config to disk
- *      unlock json config
- *   unlock qmp_lock
- *   (CTX_LOCK can be acquired and released several time while holding the
- *    qmp_lock)
- *
- * Device removal routines are not touched.
- *
- * Here is the proof that we always maintain that invariant and we
- * don't leak files during interaction of hotplug thread and other
- * threads / processes.
- *
- * # Safe against parallel add
- *
- * When another thread / process tries to add same device, it's
- * blocked by json_lock. The loser of two threads will bail at
- * existence check, so that we don't overwrite anything.
- *
- * # Safe against domain destruction
- *
- * If the thread / process trying to destroy domain loses the race, it's
- * blocked by json_lock. If the hotplug thread is loser, it bails at
- * acquiring lock because lock acquisition function checks existence of
- * the domain.
- *
- * # Safe against parallel removal
- *
- * When another thread / process tries to remove a device, it's _NOT_
- * blocked by json_lock, but xenstore transaction can help maintain
- * invariant. The removal threads either a) sees that device in
- * xenstore, b) doesn't see that device in xenstore.
- *
- * In a), it sees that device in xenstore. At that point hotplug is
- * already finished (both JSON and xenstore changes committed). So that
- * device can be safely removed. JSON entry is left untouched and
- * becomes stale, but this is a valid state -- next time when a
- * device with same identifier gets added, the stale entry gets
- * overwritten.
- *
- * In b), it doesn't see that device in xenstore, but it will commence
- * anyway. Eventually a forcibly removal is initiated, which will forcely
- * remove xenstore entry.
- *
- * If hotplug threads creates xenstore entry (therefore JSON entry as
- * well) before force removal, that xenstore entry is removed. We're
- * left with JSON stale entry but not xenstore entry, which is a valid
- * state.
- *
- * If hotplug thread has not created xenstore entry when the removal
- * is committed, we're obviously safe. Hotplug thread will add in
- * xenstore entry afterwards. We have both JSON and xenstore entry,
- * it's a valid state.
- */
-
-/* Waits for the passed device to reach state XenbusStateInitWait.
- * This is not really useful by itself, but is important when executing
- * hotplug scripts, since we need to be sure the device is in the correct
- * state before executing them.
- *
- * Once finished, aodev->callback will be executed.
- */
-_hidden void libxl__wait_device_connection(libxl__egc*,
-                                           libxl__ao_device *aodev);
-
-/* Arranges that dev will be removed to the guest, and the
- * hotplug scripts will be executed (if necessary). When
- * this is done (or an error happens), the callback in
- * aodev->callback will be called.
- *
- * The libxl__ao_device passed to this function should be
- * prepared using libxl__prepare_ao_device prior to calling
- * this function.
- *
- * Once finished, aodev->callback will be executed.
- */
-_hidden void libxl__initiate_device_generic_remove(libxl__egc *egc,
-                                                   libxl__ao_device *aodev);
-
-_hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
-                                                   libxl__ao_device *aodev);
-
-/*
- * libxl__get_hotplug_script_info returns the args and env that should
- * be passed to the hotplug script for the requested device.
- *
- * Since a device might not need to execute any hotplug script, this function
- * can return the following values:
- * < 0: Error
- * 0: No need to execute hotplug script
- * 1: Execute hotplug script
- *
- * The last parameter, "num_exec" refeers to the number of times hotplug
- * scripts have been called for this device.
- *
- * The main body of libxl will, for each device, keep calling
- * libxl__get_hotplug_script_info, with incrementing values of
- * num_exec, and executing the resulting script accordingly,
- * until libxl__get_hotplug_script_info returns<=0.
- */
-_hidden int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
-                                           char ***args, char ***env,
-                                           libxl__device_action action,
-                                           int num_exec);
-
-/*----- local disk attach: attach a disk locally to run the bootloader -----*/
-
-typedef struct libxl__disk_local_state libxl__disk_local_state;
-typedef void libxl__disk_local_state_callback(libxl__egc*,
-                                              libxl__disk_local_state*,
-                                              int rc);
-
-/* A libxl__disk_local_state may be in the following states:
- * Undefined, Idle, Attaching, Attached, Detaching.
- */
-struct libxl__disk_local_state {
-    /* filled by the user */
-    libxl__ao *ao;
-    const libxl_device_disk *in_disk;
-    libxl_device_disk disk;
-    const char *blkdev_start;
-    libxl__disk_local_state_callback *callback;
-    /* filled by libxl__device_disk_local_initiate_attach */
-    char *diskpath;
-    /* private for implementation of local detach */
-    libxl__ao_device aodev;
-    int rc;
-};
-
-/*
- * Prepares a dls for use.
- * State Undefined -> Idle
- */
-static inline void libxl__device_disk_local_init(libxl__disk_local_state *dls)
-{
-    dls->rc = 0;
-}
-
-/*
- * See if we can find a way to access a disk locally
- */
-_hidden char * libxl__device_disk_find_local_path(libxl__gc *gc,
-                                                  libxl_domid guest_domid,
-                                                  const libxl_device_disk *disk,
-                                                  bool qdisk_direct);
-
-
-/* Make a disk available in this (the control) domain. Always calls
- * dls->callback when finished.
- * State Idle -> Attaching
- *
- * The state of dls on entry to the callback depends on the value
- * of rc passed to the callback:
- *     rc == 0: Attached if rc == 0
- *     rc != 0: Idle
- */
-_hidden void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
-                                                libxl__disk_local_state *dls);
-
-/* Disconnects a disk device form the control domain. If the passed
- * dls is not attached (or has already been detached),
- * libxl__device_disk_local_initiate_detach will just call the callback
- * directly.
- * State Idle/Attached -> Detaching
- *
- * The state of dls on entry to the callback is Idle.
- */
-_hidden void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
-                                                libxl__disk_local_state *dls);
-
-/*----- datacopier: copies data from one fd to another -----*/
-
-typedef struct libxl__datacopier_state libxl__datacopier_state;
-typedef struct libxl__datacopier_buf libxl__datacopier_buf;
-
-/* onwrite==1 means problem happened when writing
- *     rc==FAIL    errnoval >0    we had a write error, logged
- * onwrite==0 means problem happened when reading
- *     rc==0       errnoval==0    we got eof and all data was written
- *     rc==FAIL    errnoval >0    we had a read error, logged
- * onwrite==-1 means some other internal problem
- *     rc==FAIL    errnoval==EIO  some other internal failure, logged
- *     rc==ABORTED errnoval==0    abort requested, not logged
- * If we get POLLHUP, we call callback_pollhup with
- *     rc==FAIL    errnoval==-1   POLLHUP signalled
- * or if callback_pollhup==0 this is treated as eof (if POLLIN|POLLHUP
- * on the reading fd) or an internal failure (otherwise), as above.
- * In all cases copier is killed before calling this callback */
-typedef void libxl__datacopier_callback(libxl__egc *egc,
-     libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
-
-struct libxl__datacopier_buf {
-    /* private to datacopier */
-    LIBXL_TAILQ_ENTRY(libxl__datacopier_buf) entry;
-    int used;
-    char buf[1000];
-};
-
-struct libxl__datacopier_state {
-    /* caller must fill these in, and they must all remain valid */
-    libxl__ao *ao;
-    int readfd, writefd;
-    ssize_t maxsz;
-    ssize_t bytes_to_read; /* set to -1 to read until EOF */
-    const char *copywhat, *readwhat, *writewhat; /* for error msgs */
-    FILE *log; /* gets a copy of everything */
-    libxl__datacopier_callback *callback;
-    libxl__datacopier_callback *callback_pollhup;
-    void *readbuf; /* Set this to read data into it without writing to an
-                      fd. The buffer should be at least as large as the
-                      bytes_to_read parameter, which should not be -1. */
-    /* remaining fields are private to datacopier */
-    libxl__ao_abortable abrt;
-    libxl__ev_fd toread, towrite;
-    ssize_t used;
-    LIBXL_TAILQ_HEAD(libxl__datacopier_bufs, libxl__datacopier_buf) bufs;
-};
-
-_hidden void libxl__datacopier_init(libxl__datacopier_state *dc);
-_hidden void libxl__datacopier_kill(libxl__datacopier_state *dc);
-_hidden int libxl__datacopier_start(libxl__datacopier_state *dc);
-
-/* Inserts literal data into the output stream.  The data is copied.
- * May safely be used only immediately after libxl__datacopier_start
- * (before the ctx is unlocked).  But may be called multiple times.
- * NB exceeding maxsz will fail an assertion! */
-_hidden void libxl__datacopier_prefixdata(libxl__egc*, libxl__datacopier_state*,
-                                          const void *data, size_t len);
-
-/*----- Save/restore helper (used by creation and suspend) -----*/
-
-typedef struct libxl__srm_save_callbacks {
-    libxl__srm_save_autogen_callbacks a;
-} libxl__srm_save_callbacks;
-
-typedef struct libxl__srm_restore_callbacks {
-    libxl__srm_restore_autogen_callbacks a;
-} libxl__srm_restore_callbacks;
-
-/* a pointer to this struct is also passed as "user" to the
- * save callout helper callback functions */
-typedef struct libxl__save_helper_state {
-    /* public, caller of run_helper initialises */
-    libxl__ao *ao;
-    uint32_t domid;
-    union {
-        libxl__srm_save_callbacks save;
-        libxl__srm_restore_callbacks restore;
-    } callbacks;
-    int (*recv_callback)(const unsigned char *msg, uint32_t len, void *user);
-    void (*completion_callback)(libxl__egc *egc, void *caller_state,
-                                int rc, int retval, int errnoval);
-    void *caller_state;
-    int need_results; /* set to 0 or 1 by caller of run_helper;
-                       * if set to 1 then the ultimate caller's
-                       * results function must set it to 0 */
-    /* private */
-    int rc;
-    int completed; /* retval/errnoval valid iff completed */
-    int retval, errnoval; /* from xc_domain_save / xc_domain_restore */
-    libxl__ao_abortable abrt;
-    libxl__carefd *pipes[2]; /* 0 = helper's stdin, 1 = helper's stdout */
-    libxl__ev_fd readable;
-    libxl__ev_child child;
-    const char *stdin_what, *stdout_what;
-
-    libxl__egc *egc; /* valid only for duration of each event callback;
-                      * is here in this struct for the benefit of the
-                      * marshalling and xc callback functions */
-} libxl__save_helper_state;
-
-/*----- checkpoint device related state structure -----*/
-/*
- * The abstract checkpoint device layer exposes a common
- * set of API to [external] libxl for manipulating devices attached to
- * a guest protected by Remus/COLO. The device layer also exposes a set of
- * [internal] interfaces that every device type must implement.
- *
- * The following API are exposed to libxl:
- *
- * One-time configuration operations:
- *  +libxl__checkpoint_devices_setup
- *    > Enable output buffering for NICs, setup disk replication, etc.
- *  +libxl__checkpoint_devices_teardown
- *    > Disable output buffering and disk replication; teardown any
- *       associated external setups like qdiscs for NICs.
- *
- * Operations executed every checkpoint (in order of invocation):
- *  +libxl__checkpoint_devices_postsuspend
- *  +libxl__checkpoint_devices_preresume
- *  +libxl__checkpoint_devices_commit
- *
- * Each device type needs to implement the interfaces specified in
- * the libxl__checkpoint_device_instance_ops if it wishes to support Remus/COLO.
- *
- * The high-level control flow through the checkpoint device layer is shown
- * below:
- *
- * xl remus
- *  |->  libxl_domain_remus_start
- *    |-> libxl__checkpoint_devices_setup
- *      |-> Per-checkpoint libxl__checkpoint_devices_[postsuspend,preresume,commit]
- *        ...
- *        |-> On backup failure, network error or other internal errors:
- *            libxl__checkpoint_devices_teardown
- */
-
-typedef struct libxl__checkpoint_device libxl__checkpoint_device;
-typedef struct libxl__checkpoint_devices_state libxl__checkpoint_devices_state;
-typedef struct libxl__checkpoint_device_instance_ops libxl__checkpoint_device_instance_ops;
-
-/*
- * Interfaces to be implemented by every device subkind that wishes to
- * support Remus/COLO. Functions must be implemented unless otherwise
- * stated. Many of these functions are asynchronous. They call
- * dev->aodev.callback when done.  The actual implementations may be
- * synchronous and call dev->aodev.callback directly (as the last
- * thing they do).
- */
-struct libxl__checkpoint_device_instance_ops {
-    /* the device kind this ops belongs to... */
-    libxl__device_kind kind;
-
-    /*
-     * Checkpoint operations. May be NULL, meaning the op is not
-     * implemented and the caller should treat them as a no-op (and do
-     * nothing when checkpointing).
-     * Asynchronous.
-     */
-
-    void (*postsuspend)(libxl__egc *egc, libxl__checkpoint_device *dev);
-    void (*preresume)(libxl__egc *egc, libxl__checkpoint_device *dev);
-    void (*commit)(libxl__egc *egc, libxl__checkpoint_device *dev);
-
-    /*
-     * setup() and teardown() are refer to the actual checkpoint device.
-     * Asynchronous.
-     * teardown is called even if setup fails.
-     */
-    /*
-     * setup() should first determines whether the subkind matches the specific
-     * device. If matched, the device will then be managed with this set of
-     * subkind operations.
-     * Yields 0 if the device successfully set up.
-     * CHECKPOINT_DEVOPS_DOES_NOT_MATCH if the ops does not match the device.
-     * any other rc indicates failure.
-     */
-    void (*setup)(libxl__egc *egc, libxl__checkpoint_device *dev);
-    void (*teardown)(libxl__egc *egc, libxl__checkpoint_device *dev);
-};
-
-int init_subkind_nic(libxl__checkpoint_devices_state *cds);
-void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds);
-int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
-void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds);
-
-typedef void libxl__checkpoint_callback(libxl__egc *,
-                                        libxl__checkpoint_devices_state *,
-                                        int rc);
-
-/*
- * State associated with a checkpoint invocation, including parameters
- * passed to the checkpoint abstract device layer by the remus
- * save/restore machinery.
- */
-struct libxl__checkpoint_devices_state {
-    /*-- must be set by caller of libxl__checkpoint_device_(setup|teardown) --*/
-
-    libxl__ao *ao;
-    uint32_t domid;
-    libxl__checkpoint_callback *callback;
-    void *concrete_data;
-    int device_kind_flags;
-    /* The ops must be pointer array, and the last ops must be NULL. */
-    const libxl__checkpoint_device_instance_ops **ops;
-
-    /*----- private for abstract layer only -----*/
-
-    int num_devices;
-    /*
-     * this array is allocated before setup the checkpoint devices by the
-     * checkpoint abstract layer.
-     * devs may be NULL, means there's no checkpoint devices that has been
-     * set up.
-     * the size of this array is 'num_devices', which is the total number
-     * of libxl nic devices and disk devices(num_nics + num_disks).
-     */
-    libxl__checkpoint_device **devs;
-
-    libxl_device_nic *nics;
-    int num_nics;
-    libxl_device_disk *disks;
-    int num_disks;
-
-    libxl__multidev multidev;
-};
-
-/*
- * Information about a single device being handled by remus.
- * Allocated by the checkpoint abstract layer.
- */
-struct libxl__checkpoint_device {
-    /*----- shared between abstract and concrete layers -----*/
-    /*
-     * if this is true, that means the subkind ops match the device
-     */
-    bool matched;
-
-    /*----- set by checkpoint device abstruct layer -----*/
-    /* libxl__device_* which this checkpoint device related to */
-    const void *backend_dev;
-    libxl__device_kind kind;
-    libxl__checkpoint_devices_state *cds;
-    libxl__ao_device aodev;
-
-    /*----- private for abstract layer only -----*/
-
-    /*
-     * Control and state variables for the asynchronous callback
-     * based loops which iterate over device subkinds, and over
-     * individual devices.
-     */
-    int ops_index;
-    const libxl__checkpoint_device_instance_ops *ops;
-
-    /*----- private for concrete (device-specific) layer -----*/
-
-    /* concrete device's private data */
-    void *concrete_data;
-};
-
-/* the following 5 APIs are async ops, call cds->callback when done */
-_hidden void libxl__checkpoint_devices_setup(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_teardown(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_postsuspend(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_preresume(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds);
-_hidden void libxl__checkpoint_devices_commit(libxl__egc *egc,
-                                        libxl__checkpoint_devices_state *cds);
-
-/*----- Remus related state structure -----*/
-typedef struct libxl__remus_state libxl__remus_state;
-struct libxl__remus_state {
-    /* private */
-    libxl__ev_time checkpoint_timeout; /* used for Remus checkpoint */
-    int interval; /* checkpoint interval */
-
-    /*----- private for concrete (device-specific) layer only -----*/
-    /* private for nic device subkind ops */
-    char *netbufscript;
-    struct nl_sock *nlsock;
-    struct nl_cache *qdisc_cache;
-
-    /* private for drbd disk subkind ops */
-    char *drbd_probe_script;
-};
-_hidden int libxl__netbuffer_enabled(libxl__gc *gc);
-
-/*----- Legacy conversion helper -----*/
-typedef struct libxl__conversion_helper_state libxl__conversion_helper_state;
-
-struct libxl__conversion_helper_state {
-    /* Public - Must be filled by caller unless noted. */
-    libxl__ao *ao;
-    int legacy_fd;             /* fd to read the legacy stream from. */
-    bool hvm;                  /* pv or hvm domain? */
-    libxl__carefd *v2_carefd;  /* Filled by successful call to
-                                * libxl__convert_legacy_stream().  Caller
-                                * assumes ownership of the fd. */
-    void (*completion_callback)(
-        libxl__egc *egc, libxl__conversion_helper_state *chs, int rc);
-    /* private */
-    int rc;
-    libxl__ao_abortable abrt;
-    libxl__ev_child child;
-};
-
-_hidden void libxl__conversion_helper_init
-                    (libxl__conversion_helper_state *chs);
-_hidden int libxl__convert_legacy_stream(libxl__egc *egc,
-                    libxl__conversion_helper_state *chs);
-_hidden void libxl__conversion_helper_abort(libxl__egc *egc,
-                    libxl__conversion_helper_state *chs, int rc);
-static inline bool libxl__conversion_helper_inuse
-                    (const libxl__conversion_helper_state *chs)
-{ return libxl__ev_child_inuse(&chs->child); }
-
-/* State for reading a libxl migration v2 stream */
-typedef struct libxl__stream_read_state libxl__stream_read_state;
-
-typedef struct libxl__sr_record_buf {
-    /* private to stream read helper */
-    LIBXL_STAILQ_ENTRY(struct libxl__sr_record_buf) entry;
-    libxl__sr_rec_hdr hdr;
-    void *body; /* iff hdr.length != 0 */
-} libxl__sr_record_buf;
-
-struct libxl__stream_read_state {
-    /* filled by the user */
-    libxl__ao *ao;
-    libxl__domain_create_state *dcs;
-    int fd;
-    bool legacy;
-    bool back_channel;
-    void (*completion_callback)(libxl__egc *egc,
-                                libxl__stream_read_state *srs,
-                                int rc);
-    void (*checkpoint_callback)(libxl__egc *egc,
-                                libxl__stream_read_state *srs,
-                                int rc);
-    /* Private */
-    int rc;
-    bool running;
-    bool in_checkpoint;
-    bool sync_teardown; /* Only used to coordinate shutdown on error path. */
-    bool in_checkpoint_state;
-    libxl__save_helper_state shs;
-    libxl__conversion_helper_state chs;
-
-    /* Main stream-reading data. */
-    libxl__datacopier_state dc; /* Only used when reading a record */
-    libxl__sr_hdr hdr;
-    LIBXL_STAILQ_HEAD(, libxl__sr_record_buf) record_queue; /* NOGC */
-    enum {
-        SRS_PHASE_NORMAL,
-        SRS_PHASE_BUFFERING,
-        SRS_PHASE_UNBUFFERING,
-    } phase;
-    bool recursion_guard;
-
-    /* Only used while actively reading a record from the stream. */
-    libxl__sr_record_buf *incoming_record; /* NOGC */
-
-    /* Both only used when processing an EMULATOR record. */
-    libxl__datacopier_state emu_dc;
-    libxl__carefd *emu_carefd;
-};
-
-_hidden void libxl__stream_read_init(libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_start(libxl__egc *egc,
-                                      libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_start_checkpoint(libxl__egc *egc,
-                                                 libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_checkpoint_state(libxl__egc *egc,
-                                                 libxl__stream_read_state *stream);
-_hidden void libxl__stream_read_abort(libxl__egc *egc,
-                                      libxl__stream_read_state *stream, int rc);
-static inline bool
-libxl__stream_read_inuse(const libxl__stream_read_state *stream)
-{
-    return stream->running;
-}
-
-#include "libxl_colo.h"
-
-/*----- Domain suspend (save) state structure -----*/
-/*
- * "suspend" refers to quiescing the VM, so pausing qemu, making a
- * remote_shutdown(SHUTDOWN_suspend) hypercall etc.
- *
- * "save" refers to the actions involved in actually shuffling the
- * state of the VM, so xc_domain_save() etc.
- */
-
-typedef struct libxl__domain_suspend_state libxl__domain_suspend_state;
-typedef struct libxl__domain_save_state libxl__domain_save_state;
-
-typedef void libxl__domain_save_cb(libxl__egc*,
-                                   libxl__domain_save_state*, int rc);
-typedef void libxl__save_device_model_cb(libxl__egc*,
-                                         libxl__domain_save_state*, int rc);
-
-/* State for writing a libxl migration v2 stream */
-typedef struct libxl__stream_write_state libxl__stream_write_state;
-typedef void (*sws_record_done_cb)(libxl__egc *egc,
-                                   libxl__stream_write_state *sws);
-struct libxl__stream_write_state {
-    /* filled by the user */
-    libxl__ao *ao;
-    libxl__domain_save_state *dss;
-    int fd;
-    bool back_channel;
-    void (*completion_callback)(libxl__egc *egc,
-                                libxl__stream_write_state *sws,
-                                int rc);
-    void (*checkpoint_callback)(libxl__egc *egc,
-                                libxl__stream_write_state *sws,
-                                int rc);
-    /* Private */
-    int rc;
-    bool running;
-    bool in_checkpoint;
-    bool sync_teardown;  /* Only used to coordinate shutdown on error path. */
-    bool in_checkpoint_state;
-    libxl__save_helper_state shs;
-
-    /* Main stream-writing data. */
-    libxl__datacopier_state dc;
-    sws_record_done_cb record_done_callback;
-
-    /* Cache device model version. */
-    libxl_device_model_version device_model_version;
-
-    /* Only used when constructing EMULATOR records. */
-    libxl__datacopier_state emu_dc;
-    libxl__carefd *emu_carefd;
-    libxl__sr_rec_hdr emu_rec_hdr;
-    libxl__sr_emulator_hdr emu_sub_hdr;
-    void *emu_body;
-};
-
-_hidden void libxl__stream_write_init(libxl__stream_write_state *stream);
-_hidden void libxl__stream_write_start(libxl__egc *egc,
-                                       libxl__stream_write_state *stream);
-_hidden void
-libxl__stream_write_start_checkpoint(libxl__egc *egc,
-                                     libxl__stream_write_state *stream);
-_hidden void
-libxl__stream_write_checkpoint_state(libxl__egc *egc,
-                                     libxl__stream_write_state *stream,
-                                     libxl_sr_checkpoint_state *srcs);
-_hidden void libxl__stream_write_abort(libxl__egc *egc,
-                                       libxl__stream_write_state *stream,
-                                       int rc);
-static inline bool
-libxl__stream_write_inuse(const libxl__stream_write_state *stream)
-{
-    return stream->running;
-}
-
-typedef struct libxl__logdirty_switch {
-    /* Set by caller of libxl__domain_common_switch_qemu_logdirty */
-    libxl__ao *ao;
-    void (*callback)(libxl__egc *egc, struct libxl__logdirty_switch *lds,
-                     int rc);
-
-    const char *cmd;
-    const char *cmd_path;
-    const char *ret_path;
-    libxl__ev_xswatch watch;
-    libxl__ev_time timeout;
-    libxl__ev_qmp qmp;
-} libxl__logdirty_switch;
-
-_hidden void libxl__logdirty_init(libxl__logdirty_switch *lds);
-
-struct libxl__domain_suspend_state {
-    /* set by caller of libxl__domain_suspend_init */
-    libxl__ao *ao;
-    uint32_t domid;
-    bool live;
-
-    /* private */
-    libxl_domain_type type;
-
-    libxl__ev_evtchn guest_evtchn;
-    int guest_evtchn_lockfd;
-    int guest_responded;
-
-    libxl__xswait_state pvcontrol;
-    libxl__ev_xswatch guest_watch;
-    libxl__ev_time guest_timeout;
-    libxl__ev_qmp qmp;
-
-    const char *dm_savefile;
-    void (*callback_device_model_done)(libxl__egc*,
-                              struct libxl__domain_suspend_state*, int rc);
-    void (*callback_common_done)(libxl__egc*,
-                                 struct libxl__domain_suspend_state*, int ok);
-};
-int libxl__domain_suspend_init(libxl__egc *egc,
-                               libxl__domain_suspend_state *dsps,
-                               libxl_domain_type type);
-
-/* calls dsps->callback_device_model_done when done
- * may synchronously calls this callback */
-_hidden void libxl__qmp_suspend_save(libxl__egc *egc,
-                                     libxl__domain_suspend_state *dsps);
-
-struct libxl__domain_save_state {
-    /* set by caller of libxl__domain_save */
-    libxl__ao *ao;
-    libxl__domain_save_cb *callback;
-
-    uint32_t domid;
-    int fd;
-    int fdfl; /* original flags on fd */
-    int recv_fd;
-    libxl_domain_type type;
-    int live;
-    int debug;
-    int checkpointed_stream;
-    const libxl_domain_remus_info *remus;
-    /* private */
-    int rc;
-    int xcflags;
-    libxl__domain_suspend_state dsps;
-    union {
-        /* for Remus */
-        libxl__remus_state rs;
-        /* for COLO */
-        libxl__colo_save_state css;
-    };
-    libxl__checkpoint_devices_state cds;
-    libxl__stream_write_state sws;
-    libxl__logdirty_switch logdirty;
-};
-
-
-/*----- openpty -----*/
-
-/*
- * opens count (>0) ptys like count calls to openpty, and then
- * calls back.  On entry, all op[].master and op[].slave must be
- * 0.  On callback, either rc==0 and master and slave are non-0,
- * or rc is a libxl error and they are both 0.  If libxl__openpty
- * returns non-0 no callback will happen and everything is left
- * cleaned up.
- */
-
-typedef struct libxl__openpty_state libxl__openpty_state;
-typedef struct libxl__openpty_result libxl__openpty_result;
-typedef void libxl__openpty_callback(libxl__egc *egc, libxl__openpty_state *op);
-
-struct libxl__openpty_state {
-    /* caller must fill these in, and they must all remain valid */
-    libxl__ao *ao;
-    libxl__openpty_callback *callback;
-    int count;
-    libxl__openpty_result *results; /* actual size is count, out parameter */
-    /* public, result, caller may only read in callback */
-    int rc;
-    /* private for implementation */
-    libxl__ev_child child;
-};
-
-struct libxl__openpty_result {
-    libxl__carefd *master, *slave;
-};
-
-int libxl__openptys(libxl__openpty_state *op,
-                    struct termios *termp,
-                    struct winsize *winp);
-
-
-/*----- bootloader -----*/
-
-typedef struct libxl__bootloader_state libxl__bootloader_state;
-typedef void libxl__run_bootloader_callback(libxl__egc*,
-                                libxl__bootloader_state*, int rc);
-typedef void libxl__bootloader_console_callback(libxl__egc*,
-                                libxl__bootloader_state*);
-
-struct libxl__bootloader_state {
-    /* caller must fill these in, and they must all remain valid */
-    libxl__ao *ao;
-    libxl__run_bootloader_callback *callback;
-    libxl__bootloader_console_callback *console_available;
-    const libxl_domain_build_info *info;
-    libxl_device_disk *disk;
-    /* Should be zeroed by caller on entry.  Will be filled in by
-     * bootloader machinery; represents the local attachment of the
-     * disk for the benefit of the bootloader.  Must be detached by
-     * the caller using libxl__device_disk_local_initiate_detach.
-     * (This is safe to do after ->callback() has happened since
-     * the domain's kernel and initramfs will have been copied
-     * out of the guest's disk into a temporary directory, mapped
-     * as file references, and deleted. */
-    libxl__disk_local_state dls;
-    uint32_t domid;
-    /* outputs:
-     *  - caller must initialise kernel and ramdisk to point to file
-     *    references, these will be updated and mapped;
-     *  - caller must initialise cmdline to NULL, it will be updated with a
-     *    string allocated from the gc;
-     */
-    libxl__file_reference *kernel, *ramdisk;
-    const char *cmdline;
-    /* private to libxl__run_bootloader */
-    char *outputpath, *outputdir, *logfile;
-    libxl__openpty_state openpty;
-    libxl__openpty_result ptys[2];  /* [0] is for bootloader */
-    libxl__ev_child child;
-    libxl__domaindeathcheck deathcheck;
-    int nargs, argsspace;
-    const char **args;
-    libxl__datacopier_state keystrokes, display;
-    int rc, got_pollhup;
-};
-
-_hidden void libxl__bootloader_init(libxl__bootloader_state *bl);
-
-/* Will definitely call st->callback, perhaps reentrantly.
- * If callback is passed rc==0, will have updated st->info appropriately */
-_hidden void libxl__bootloader_run(libxl__egc*, libxl__bootloader_state *st);
-
-/*----- Generic Device Handling -----*/
-#define LIBXL_DEFINE_DEVICE_ADD(type)                                   \
-    int libxl_device_##type##_add(libxl_ctx *ctx,                       \
-        uint32_t domid, libxl_device_##type *type,                      \
-        const libxl_asyncop_how *ao_how)                                \
-    {                                                                   \
-        AO_CREATE(ctx, domid, ao_how);                                  \
-        libxl__ao_device *aodev;                                        \
-                                                                        \
-        GCNEW(aodev);                                                   \
-        libxl__prepare_ao_device(ao, aodev);                            \
-        aodev->action = LIBXL__DEVICE_ACTION_ADD;                       \
-        aodev->callback = device_addrm_aocomplete;                      \
-        aodev->update_json = true;                                      \
-        libxl__device_##type##_add(egc, domid, type, aodev);            \
-                                                                        \
-        return AO_INPROGRESS;                                           \
-    }
-
-#define LIBXL_DEFINE_DEVICES_ADD(type)                                  \
-    void libxl__add_##type##s(libxl__egc *egc, libxl__ao *ao, uint32_t domid, \
-                              libxl_domain_config *d_config,            \
-                              libxl__multidev *multidev)                \
-    {                                                                   \
-        AO_GC;                                                          \
-        int i;                                                          \
-        for (i = 0; i < d_config->num_##type##s; i++) {                 \
-            libxl__ao_device *aodev = libxl__multidev_prepare(multidev);  \
-            libxl__device_##type##_add(egc, domid, &d_config->type##s[i], \
-                                       aodev);                          \
-        }                                                               \
-    }
-
-#define LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, remtype, removedestroy, f) \
-    int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \
-        uint32_t domid, libxl_device_##type *type,                      \
-        const libxl_asyncop_how *ao_how)                                \
-    {                                                                   \
-        AO_CREATE(ctx, domid, ao_how);                                  \
-        libxl__device *device;                                          \
-        libxl__ao_device *aodev;                                        \
-        int rc;                                                         \
-                                                                        \
-        GCNEW(device);                                                  \
-        rc = libxl__device_from_##type(gc, domid, type, device);        \
-        if (rc != 0) goto out;                                          \
-                                                                        \
-        GCNEW(aodev);                                                   \
-        libxl__prepare_ao_device(ao, aodev);                            \
-        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;                    \
-        aodev->dev = device;                                            \
-        aodev->callback = device_addrm_aocomplete;                      \
-        aodev->force.flag = f;                                          \
-        libxl__initiate_device_##remtype##_remove(egc, aodev);          \
-                                                                        \
-    out:                                                                \
-        if (rc) return AO_CREATE_FAIL(rc);                              \
-        return AO_INPROGRESS;                                           \
-    }
-
-#define LIBXL_DEFINE_UPDATE_DEVID(name)                                 \
-    int libxl__device_##name##_update_devid(libxl__gc *gc,              \
-                                            uint32_t domid,             \
-                                            libxl_device_##name *type)  \
-    {                                                                   \
-        if (type->devid == -1)                                          \
-            type->devid = libxl__device_nextid(gc, domid,               \
-                          libxl__##name##_devtype.type);                \
-        if (type->devid < 0)                                            \
-            return ERROR_FAIL;                                          \
-        return 0;                                                       \
-    }
-
-#define LIBXL_DEFINE_DEVICE_FROM_TYPE(name)                             \
-    int libxl__device_from_##name(libxl__gc *gc, uint32_t domid,        \
-                                  libxl_device_##name *type,            \
-                                  libxl__device *device)                \
-    {                                                                   \
-        device->backend_devid   = type->devid;                          \
-        device->backend_domid   = type->backend_domid;                  \
-        device->backend_kind    = libxl__##name##_devtype.type;         \
-        device->devid           = type->devid;                          \
-        device->domid           = domid;                                \
-        device->kind            = libxl__##name##_devtype.type;         \
-                                                                        \
-        return 0;                                                       \
-    }
-
-#define LIBXL_DEFINE_DEVID_TO_DEVICE(name)                              \
-    int libxl_devid_to_device_##name(libxl_ctx *ctx, uint32_t domid,    \
-                                     int devid,                         \
-                                     libxl_device_##name *type)         \
-    {                                                                   \
-        GC_INIT(ctx);                                                   \
-                                                                        \
-        char *device_path;                                              \
-        const char *tmp;                                                \
-        int rc;                                                         \
-                                                                        \
-        libxl_device_##name##_init(type);                               \
-                                                                        \
-        device_path = GCSPRINTF("%s/device/%s/%d",                      \
-                                libxl__xs_libxl_path(gc, domid),        \
-                                libxl__device_kind_to_string(           \
-                                libxl__##name##_devtype.type),          \
-                                devid);                                 \
-                                                                        \
-        if (libxl__xs_read_mandatory(gc, XBT_NULL, device_path, &tmp)) {\
-            rc = ERROR_NOTFOUND; goto out;                              \
-        }                                                               \
-                                                                        \
-        if (libxl__##name##_devtype.from_xenstore) {                    \
-            rc = libxl__##name##_devtype.from_xenstore(gc, device_path, \
-                                                       devid, type);    \
-            if (rc) goto out;                                           \
-        }                                                               \
-                                                                        \
-        rc = 0;                                                         \
-                                                                        \
-    out:                                                                \
-                                                                        \
-        GC_FREE;                                                        \
-        return rc;                                                      \
-    }
-
-
-#define LIBXL_DEFINE_DEVICE_REMOVE(type)                                \
-    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, remove,               \
-                                   LIBXL__FORCE_AUTO)                   \
-    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, destroy,              \
-                                   LIBXL__FORCE_ON)
-
-#define LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(type)                         \
-    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, remove,                  \
-                                   LIBXL__FORCE_AUTO)                   \
-    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, destroy,                 \
-                                   LIBXL__FORCE_ON)
-
-#define LIBXL_DEFINE_DEVICE_SAFE_REMOVE(type)                           \
-    LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, safe_remove,          \
-                                   LIBXL__FORCE_OFF)
-
-#define LIBXL_DEFINE_DEVICE_LIST(type)                                  \
-    libxl_device_##type *libxl_device_##type##_list(libxl_ctx *ctx,     \
-                                                    uint32_t domid,     \
-                                                    int *num)           \
-    {                                                                   \
-        libxl_device_##type *r;                                         \
-        GC_INIT(ctx);                                                   \
-        r = libxl__device_list(gc, &libxl__##type##_devtype,            \
-                               domid, num);                             \
-        GC_FREE;                                                        \
-        return r;                                                       \
-    }                                                                   \
-                                                                        \
-    void libxl_device_##type##_list_free(libxl_device_##type *list,     \
-                                         int num)                       \
-    {                                                                   \
-        libxl__device_list_free(&libxl__##type##_devtype, list, num);   \
-    }
-
-typedef void (*device_add_fn_t)(libxl__egc *, libxl__ao *, uint32_t,
-                                libxl_domain_config *, libxl__multidev *);
-typedef int (*device_set_default_fn_t)(libxl__gc *, uint32_t, void *, bool);
-typedef int (*device_to_device_fn_t)(libxl__gc *, uint32_t, void *,
-                                     libxl__device *);
-typedef void (*device_init_fn_t)(void *);
-typedef void (*device_copy_fn_t)(libxl_ctx *, void *dst, const void *src);
-typedef void (*device_dispose_fn_t)(void *);
-typedef int (*device_compare_fn_t)(const void *, const void *);
-typedef void (*device_merge_fn_t)(libxl_ctx *, void *, void *);
-typedef int (*device_dm_needed_fn_t)(void *, unsigned);
-typedef void (*device_update_config_fn_t)(libxl__gc *, void *, void *);
-typedef int (*device_update_devid_fn_t)(libxl__gc *, uint32_t, void *);
-typedef int (*device_get_num_fn_t)(libxl__gc *, const char *, unsigned int *);
-typedef int (*device_from_xenstore_fn_t)(libxl__gc *, const char *,
-                                         libxl_devid, void *);
-typedef int (*device_set_xenstore_config_fn_t)(libxl__gc *, uint32_t, void *,
-                                               flexarray_t *, flexarray_t *,
-                                               flexarray_t *);
-
-struct libxl__device_type {
-    libxl__device_kind type;
-    int skip_attach;   /* Skip entry in domcreate_attach_devices() if 1 */
-    int ptr_offset;    /* Offset of device array ptr in libxl_domain_config */
-    int num_offset;    /* Offset of # of devices in libxl_domain_config */
-    int dev_elem_size; /* Size of one device element in array */
-    device_add_fn_t                 add;
-    device_set_default_fn_t         set_default;
-    device_to_device_fn_t           to_device;
-    device_init_fn_t                init;
-    device_copy_fn_t                copy;
-    device_dispose_fn_t             dispose;
-    device_compare_fn_t             compare;
-    device_merge_fn_t               merge;
-    device_dm_needed_fn_t           dm_needed;
-    device_update_config_fn_t       update_config;
-    device_update_devid_fn_t        update_devid;
-    device_get_num_fn_t             get_num;
-    device_from_xenstore_fn_t       from_xenstore;
-    device_set_xenstore_config_fn_t set_xenstore_config;
-};
-
-#define DEFINE_DEVICE_TYPE_STRUCT_X(name, sname, kind, ...)                    \
-    const libxl__device_type libxl__ ## name ## _devtype = {                   \
-        .type          = LIBXL__DEVICE_KIND_ ## kind,                       \
-        .ptr_offset    = offsetof(libxl_domain_config, name ## s),             \
-        .num_offset    = offsetof(libxl_domain_config, num_ ## name ## s),     \
-        .dev_elem_size = sizeof(libxl_device_ ## sname),                       \
-        .add           = libxl__add_ ## name ## s,                             \
-        .set_default   = (device_set_default_fn_t)                             \
-                         libxl__device_ ## sname ## _setdefault,               \
-        .to_device     = (device_to_device_fn_t)libxl__device_from_ ## name,   \
-        .init          = (device_init_fn_t)libxl_device_ ## sname ## _init,    \
-        .copy          = (device_copy_fn_t)libxl_device_ ## sname ## _copy,    \
-        .dispose       = (device_dispose_fn_t)                                 \
-                         libxl_device_ ## sname ## _dispose,                   \
-        .compare       = (device_compare_fn_t)                                 \
-                         libxl_device_ ## sname ## _compare,                   \
-        .update_devid  = (device_update_devid_fn_t)                            \
-                         libxl__device_ ## sname ## _update_devid,             \
-        __VA_ARGS__                                                            \
-    }
-
-#define DEFINE_DEVICE_TYPE_STRUCT(name, kind, ...)                             \
-    DEFINE_DEVICE_TYPE_STRUCT_X(name, name, kind, __VA_ARGS__)
-
-static inline void **libxl__device_type_get_ptr(
-    const libxl__device_type *dt, const libxl_domain_config *d_config)
-{
-    return (void **)((void *)d_config + dt->ptr_offset);
-}
-
-static inline void *libxl__device_type_get_elem(
-    const libxl__device_type *dt, const libxl_domain_config *d_config,
-    int e)
-{
-    return *libxl__device_type_get_ptr(dt, d_config) + dt->dev_elem_size * e;
-}
-
-static inline int *libxl__device_type_get_num(
-    const libxl__device_type *dt, const libxl_domain_config *d_config)
-{
-    return (int *)((void *)d_config + dt->num_offset);
-}
-
-extern const libxl__device_type libxl__vfb_devtype;
-extern const libxl__device_type libxl__vkb_devtype;
-extern const libxl__device_type libxl__disk_devtype;
-extern const libxl__device_type libxl__nic_devtype;
-extern const libxl__device_type libxl__vtpm_devtype;
-extern const libxl__device_type libxl__usbctrl_devtype;
-extern const libxl__device_type libxl__usbdev_devtype;
-extern const libxl__device_type libxl__pcidev_devtype;
-extern const libxl__device_type libxl__vdispl_devtype;
-extern const libxl__device_type libxl__p9_devtype;
-extern const libxl__device_type libxl__pvcallsif_devtype;
-extern const libxl__device_type libxl__vsnd_devtype;
-
-extern const libxl__device_type *device_type_tbl[];
-
-/*----- Domain destruction -----*/
-
-/* Domain destruction has been split into two functions:
- *
- * libxl__domain_destroy is the main destroy function, which detects
- * stubdoms and calls libxl__destroy_domid on the domain and its
- * stubdom if present, creating a different libxl__destroy_domid_state
- * for each one of them.
- *
- * libxl__destroy_domid actually destroys the domain, but it
- * doesn't check for stubdomains, since that would involve
- * recursion, which we want to avoid.
- */
-
-typedef struct libxl__domain_destroy_state libxl__domain_destroy_state;
-typedef struct libxl__destroy_domid_state libxl__destroy_domid_state;
-typedef struct libxl__destroy_devicemodel_state libxl__destroy_devicemodel_state;
-typedef struct libxl__devices_remove_state libxl__devices_remove_state;
-
-typedef void libxl__domain_destroy_cb(libxl__egc *egc,
-                                      libxl__domain_destroy_state *dds,
-                                      int rc);
-
-typedef void libxl__domid_destroy_cb(libxl__egc *egc,
-                                     libxl__destroy_domid_state *dis,
-                                     int rc);
-
-typedef void libxl__devicemodel_destroy_cb(libxl__egc *egc,
-                                     libxl__destroy_devicemodel_state *ddms,
-                                     int rc);
-
-typedef void libxl__devices_remove_callback(libxl__egc *egc,
-                                            libxl__devices_remove_state *drs,
-                                            int rc);
-
-struct libxl__devices_remove_state {
-    /* filled in by user */
-    libxl__ao *ao;
-    uint32_t domid;
-    libxl__devices_remove_callback *callback;
-    libxl__force force; /* libxl_device_TYPE_destroy rather than _remove */
-    /* private */
-    libxl__multidev multidev;
-    int num_devices;
-};
-
-struct libxl__destroy_devicemodel_state {
-    /* filled in by user */
-    libxl__ao *ao;
-    uint32_t domid;
-    libxl__devicemodel_destroy_cb *callback; /* May be called re-entrantly */
-    /* private to implementation */
-    libxl__ev_child destroyer;
-    int rc; /* Accumulated return value for the destroy operation */
-};
-
-struct libxl__destroy_domid_state {
-    /* filled in by user */
-    libxl__ao *ao;
-    uint32_t domid;
-    libxl__domid_destroy_cb *callback;
-    /* private to implementation */
-    libxl__devices_remove_state drs;
-    libxl__destroy_devicemodel_state ddms;
-    libxl__ev_child destroyer;
-    bool soft_reset;
-    libxl__multidev multidev;
-};
-
-struct libxl__domain_destroy_state {
-    /* filled by the user */
-    libxl__ao *ao;
-    uint32_t domid;
-    libxl__domain_destroy_cb *callback;
-    /* Private */
-    int rc;
-    uint32_t stubdomid;
-    libxl__destroy_domid_state stubdom;
-    int stubdom_finished;
-    libxl__destroy_domid_state domain;
-    int domain_finished;
-    bool soft_reset;
-};
-
-/*
- * Entry point for domain destruction
- * This function checks for stubdom presence and then calls
- * libxl__destroy_domid on the passed domain and its stubdom if found.
- */
-_hidden void libxl__domain_destroy(libxl__egc *egc,
-                                   libxl__domain_destroy_state *dds);
-
-/* Used to destroy a domain with the passed id (it doesn't check for stubs) */
-_hidden void libxl__destroy_domid(libxl__egc *egc,
-                                  libxl__destroy_domid_state *dis);
-
-/* Used to detroy the device model */
-_hidden void libxl__destroy_device_model(libxl__egc *egc,
-                                         libxl__destroy_devicemodel_state *ddms);
-
-/* Entry point for devices destruction */
-_hidden void libxl__devices_destroy(libxl__egc *egc,
-                                    libxl__devices_remove_state *drs);
-
-/* Helper function to add a bunch of disks. This should be used when
- * the caller is inside an async op. "multidev" will NOT be prepared by
- * this function, so the caller must make sure to call
- * libxl__multidev_begin before calling this function.
- *
- * The "callback" will be called for each device, and the user is responsible
- * for calling libxl__ao_device_check_last on the callback.
- */
-_hidden void libxl__add_disks(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
-                              libxl_domain_config *d_config,
-                              libxl__multidev *multidev);
-
-_hidden void libxl__add_nics(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
-                             libxl_domain_config *d_config,
-                             libxl__multidev *multidev);
-
-/*----- device model creation -----*/
-
-/* First layer; wraps libxl__spawn_spawn. */
-
-typedef struct libxl__dm_spawn_state libxl__dm_spawn_state;
-
-typedef void libxl__dm_spawn_cb(libxl__egc *egc, libxl__dm_spawn_state*,
-                                int rc /* if !0, error was logged */);
-
-/* Call dmss_init and dmss_dispose to initialise and dispose of
- * libxl__dm_spawn_state */
-struct libxl__dm_spawn_state {
-    /* mixed - spawn.ao must be initialised by user; rest is private: */
-    libxl__spawn_state spawn;
-    libxl__ev_qmp qmp;
-    libxl__ev_time timeout;
-    libxl__dm_resume_state dmrs;
-    /* filled in by user, must remain valid: */
-    uint32_t guest_domid; /* domain being served */
-    libxl_domain_config *guest_config;
-    libxl__domain_build_state *build_state; /* relates to guest_domid */
-    libxl__dm_spawn_cb *callback;
-};
-
-_hidden void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state*);
-
-/*
- * Called after forking but before executing the local devicemodel.
- */
-_hidden int libxl__local_dm_preexec_restrict(libxl__gc *gc);
-
-/* Stubdom device models. */
-
-typedef struct {
-    /* Mixed - user must fill in public parts EXCEPT callback,
-     * which may be undefined on entry.  (See above for details) */
-    libxl__dm_spawn_state dm; /* the stub domain device model */
-    /* filled in by user, must remain valid: */
-    libxl__dm_spawn_cb *callback; /* called as callback(,&sdss->dm,) */
-    /* private to libxl__spawn_stub_dm: */
-    libxl_domain_config dm_config;
-    libxl__domain_build_state dm_state;
-    libxl__dm_spawn_state pvqemu;
-    libxl__destroy_domid_state dis;
-    libxl__multidev multidev;
-    libxl__xswait_state xswait;
-    libxl__spawn_state qmp_proxy_spawn;
-} libxl__stub_dm_spawn_state;
-
-_hidden void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state*);
-
-_hidden char *libxl__stub_dm_name(libxl__gc *gc, const char * guest_name);
-
-/* Qdisk backend launch helpers */
-
-_hidden void libxl__spawn_qdisk_backend(libxl__egc *egc,
-                                        libxl__dm_spawn_state *dmss);
-_hidden int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid);
-
-/*----- Domain creation -----*/
-
-
-struct libxl__domain_create_state {
-    /* filled in by user */
-    libxl__ao *ao;
-    libxl_domain_config *guest_config;
-    libxl_domain_config guest_config_saved; /* vanilla config */
-    int restore_fd, libxc_fd;
-    int restore_fdfl; /* original flags of restore_fd */
-    int send_back_fd;
-    libxl_domain_restore_params restore_params;
-    uint32_t domid;
-    bool soft_reset;
-    libxl__domain_create_cb *callback;
-    libxl_asyncprogress_how aop_console_how;
-    /* private to domain_create */
-    int guest_domid;
-    int device_type_idx;
-    const char *colo_proxy_script;
-    libxl__domain_build_state build_state;
-    libxl__colo_restore_state crs;
-    libxl__checkpoint_devices_state cds;
-    libxl__bootloader_state bl;
-    libxl__stub_dm_spawn_state sdss;
-        /* If we're not doing stubdom, we use only sdss.dm,
-         * for the non-stubdom device model. */
-    libxl__stream_read_state srs;
-    /* necessary if the domain creation failed and we have to destroy it */
-    libxl__domain_destroy_state dds;
-    libxl__multidev multidev;
-    libxl__xswait_state console_xswait;
-};
-
-_hidden int libxl__device_nic_set_devids(libxl__gc *gc,
-                                         libxl_domain_config *d_config,
-                                         uint32_t domid);
-
-/*----- Domain suspend (save) functions -----*/
-
-/* calls dss->callback when done */
-_hidden void libxl__domain_save(libxl__egc *egc,
-                                libxl__domain_save_state *dss);
-
-
-/* calls libxl__xc_domain_suspend_done when done */
-_hidden void libxl__xc_domain_save(libxl__egc *egc,
-                                   libxl__domain_save_state *dss,
-                                   libxl__save_helper_state *shs);
-/* If rc==0 then retval is the return value from xc_domain_save
- * and errnoval is the errno value it provided.
- * If rc!=0, retval and errnoval are undefined. */
-_hidden void libxl__xc_domain_save_done(libxl__egc*, void *dss_void,
-                                        int rc, int retval, int errnoval);
-
-/* Used by asynchronous callbacks: ie ones which xc regards as
- * returning a value, but which we want to handle asynchronously.
- * Such functions' actual callback function return void in libxl
- * When they are ready to indicate completion, they call this. */
-void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
-                           libxl__save_helper_state *shs, int return_value);
-
-
-_hidden void libxl__domain_suspend_common_switch_qemu_logdirty
-                               (uint32_t domid, unsigned int enable, void *data);
-_hidden void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc,
-                                               int domid, unsigned enable,
-                                               libxl__logdirty_switch *lds);
-_hidden int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss,
-                                               char **buf, uint32_t *len);
-_hidden int libxl__restore_emulator_xenstore_data
-    (libxl__domain_create_state *dcs, const char *ptr, uint32_t size);
-
-
-/* calls libxl__xc_domain_restore_done when done */
-_hidden void libxl__xc_domain_restore(libxl__egc *egc,
-                                      libxl__domain_create_state *dcs,
-                                      libxl__save_helper_state *shs);
-/* If rc==0 then retval is the return value from xc_domain_save
- * and errnoval is the errno value it provided.
- * If rc!=0, retval and errnoval are undefined. */
-_hidden void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
-                                           int rc, int retval, int errnoval);
-
-_hidden void libxl__save_helper_init(libxl__save_helper_state *shs);
-_hidden void libxl__save_helper_abort(libxl__egc *egc,
-                                      libxl__save_helper_state *shs);
-
-static inline bool libxl__save_helper_inuse(const libxl__save_helper_state *shs)
-{
-    return libxl__ev_child_inuse(&shs->child);
-}
-
-/* Each time the dm needs to be saved, we must call suspend and then save
- * calls dsps->callback_device_model_done when done */
-_hidden void libxl__domain_suspend_device_model(libxl__egc *egc,
-                                           libxl__domain_suspend_state *dsps);
-
-_hidden const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid);
-
-/* calls dsps->callback_common_done when done */
-_hidden void libxl__domain_suspend(libxl__egc *egc,
-                                   libxl__domain_suspend_state *dsps);
-/* used by libxc to suspend the guest during migration */
-_hidden void libxl__domain_suspend_callback(void *data);
-
-/* Remus setup and teardown */
-_hidden void libxl__remus_setup(libxl__egc *egc,
-                                libxl__remus_state *rs);
-_hidden void libxl__remus_teardown(libxl__egc *egc,
-                                   libxl__remus_state *rs,
-                                   int rc);
-_hidden void libxl__remus_restore_setup(libxl__egc *egc,
-                                        libxl__domain_create_state *dcs);
-
-_hidden char *libxl__domid_history_path(libxl__gc *gc,
-                                        const char *suffix);
-
-/*
- * Convenience macros.
- */
-
-#define FILLZERO LIBXL_FILLZERO
-
-
-/*
- * All of these assume (or define)
- *    libxl__gc *gc;
- * as a local variable.
- */
-
-#define GC_INIT(ctx)  libxl__gc gc[1]; LIBXL_INIT_GC(gc[0],ctx)
-#define GC_FREE       libxl__free_all(gc)
-#define CTX           libxl__gc_owner(gc)
-#define NOGC          (&CTX->nogc_gc) /* pass only to consenting functions */
-
-/* Allocation macros all of which use the gc. */
-
-#define ARRAY_SIZE_OK(ptr, nmemb) ((nmemb) < INT_MAX / (sizeof(*(ptr)) * 2))
-
-/*
- * Expression statement  <type> *GCNEW(<type> *var);
- * Uses                  libxl__gc *gc;
- *
- * Allocates a new object of type <type> from the gc and zeroes it
- * with memset.  Sets var to point to the new object or zero (setting
- * errno).  Returns the new value of var.
- */
-#define GCNEW(var)                                      \
-    (((var) = libxl__zalloc((gc),sizeof(*(var)))))
-
-/*
- * Expression statement  <type> *GCNEW_ARRAY(<type> *var, ssize_t nmemb);
- * Uses                  libxl__gc *gc;
- *
- * Like GCNEW but allocates an array of nmemb elements, as if from
- * calloc.  Does check for integer overflow due to large nmemb.  If
- * nmemb is 0 may succeed by returning 0.
- */
-#define GCNEW_ARRAY(var, nmemb)                                 \
-    ((var) = libxl__calloc((gc), (nmemb), sizeof(*(var))))
-
-/*
- * Expression statement  <type> *GCREALLOC_ARRAY(<type> *var, size_t nmemb);
- * Uses                  libxl__gc *gc;
- *
- * Reallocates the array var to be of size nmemb elements.  Updates
- * var and returns the new value of var.  Does check for integer
- * overflow due to large nmemb.
- *
- * Do not pass nmemb==0.  old may be 0 on entry.
- */
-#define GCREALLOC_ARRAY(var, nmemb)                                     \
-    (assert(nmemb > 0),                                                 \
-     assert(ARRAY_SIZE_OK((var), (nmemb))),                             \
-     (var) = libxl__realloc((gc), (var), (nmemb)*sizeof(*(var))))
-
-
-/*
- * Expression            char *GCSPRINTF(const char *fmt, ...);
- * Uses                  libxl__gc *gc;
- *
- * Trivial convenience wrapper for libxl__sprintf.
- */
-#define GCSPRINTF(fmt, ...) (libxl__sprintf((gc), (fmt), __VA_ARGS__))
-
-
-/*
- * Expression statements
- *    void LOG(<xtl_level_suffix>, const char *fmt, ...);
- *    void LOGE(<xtl_level_suffix>, const char *fmt, ...);
- *    void LOGEV(<xtl_level_suffix>, int errnoval, const char *fmt, ...);
- *
- *    void LOGD(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
- *    void LOGED(<xtl_level_suffix>, uint32_t domid, const char *fmt, ...);
- *    void LOGEVD(<xtl_level_suffix>, int errnoval, uint32_t domid, const char *fmt, ...);
- * Use
- *    libxl__gc *gc;
- *
- * Trivial convenience wrappers for LIBXL__LOG, LIBXL__LOG_ERRNO,
- * LIBXL__LOG_ERRNOVAL, LIBXL__LOGD, LIBXL__LOGD_ERRNO and
- * LIBXL__LOGD_ERRNOVAL respectively (and thus for libxl__log).
- *
- * XTL_<xtl_level_suffix> should exist and be an xentoollog.h log level
- * So <xtl_level_suffix> should be one of
- *   DEBUG VERBOSE DETAIL PROGRESS INFO NOTICE WARN ERROR ERROR CRITICAL
- * Of these, most of libxl uses
- *   DEBUG INFO WARN ERROR
- *
- * The LOG*D family will preprend the log message with a string formatted
- * as follows: 'Domain %PRIu32:'. This should help better automatic sorting
- * of log messages per domain.
- */
-#define LOG(l,f, ...)     LIBXL__LOG(CTX,XTL_##l,(f),##__VA_ARGS__)
-#define LOGE(l,f, ...)    LIBXL__LOG_ERRNO(CTX,XTL_##l,(f),##__VA_ARGS__)
-#define LOGEV(l,e,f, ...) LIBXL__LOG_ERRNOVAL(CTX,XTL_##l,(e),(f),##__VA_ARGS__)
-
-#define LOGD(l,d,f, ...)     LIBXL__LOGD(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
-#define LOGED(l,d,f, ...)    LIBXL__LOGD_ERRNO(CTX,XTL_##l,(d),(f),##__VA_ARGS__)
-#define LOGEVD(l,e,d,f, ...) LIBXL__LOGD_ERRNOVAL(CTX,XTL_##l,(e),(d),(f),##__VA_ARGS__)
-
-
-/* Locking functions.  See comment for "lock" member of libxl__ctx. */
-
-static inline void libxl__ctx_lock(libxl_ctx *ctx) {
-    int r = pthread_mutex_lock(&ctx->lock);
-    assert(!r);
-}
-
-static inline void libxl__ctx_unlock(libxl_ctx *ctx) {
-    int r = pthread_mutex_unlock(&ctx->lock);
-    assert(!r);
-}
-
-#define CTX_LOCK (libxl__ctx_lock(CTX))
-#define CTX_UNLOCK (libxl__ctx_unlock(CTX))
-
-/*
- * Automatic NUMA placement
- *
- * These functions and data structures deal with the initial placement of a
- * domain onto the host NUMA nodes.
- *
- * The key concept here is the one of "NUMA placement candidate", which is
- * basically a set of nodes whose characteristics have been successfully
- * checked against some specific requirements. More precisely, a candidate
- * is the nodemap associated with one of the possible subset of the host
- * NUMA nodes providing a certain amount of free memory, or a given number
- * of cpus, or even both (depending in what the caller wants). For
- * convenience of use, some of this information are stored within the
- * candidate itself, instead of always being dynamically computed. A single
- * node can be valid placement candidate, as well as it is possible for a
- * candidate to contain all the nodes of the host. The fewer nodes there
- * are in a candidate, the better performance a domain placed onto it
- * should get (at least from a NUMA point of view). For instance, looking
- * for a numa candidates with 2GB of free memory means we want the subsets
- * of the host NUMA nodes with, cumulatively, at least 2GB of free memory.
- * This condition can be satisfied by just one particular node, or it may
- * require more nodes, depending on the characteristics of the host, on how
- * many domains have been created already, on how big they are, etc.
- *
- * The intended usage is as follows:
- *  1. first of all, call libxl__get_numa_candidates(), and specify the
- *     proper constraints to it (e.g., the amount of memory a domain need
- *     as the minimum amount of free memory for the candidates). If a
- *     candidate comparison function is provided, the candidate with fewer
- *     nodes that is found to be best according to what such fucntion says
- *     is returned. If no comparison function is passed, the very first
- *     candidate is.
- *  2. The chosen candidate's nodemap should be utilized for computing the
- *     actual affinity of the domain which, given the current NUMA support
- *     in the hypervisor, is what determines the placement of the domain's
- *     vcpus and memory.
- */
-
-typedef struct {
-    int nr_cpus, nr_nodes;
-    int nr_vcpus;
-    uint64_t free_memkb;
-    libxl_bitmap nodemap;
-} libxl__numa_candidate;
-
-/* Signature for the comparison function between two candidates */
-typedef int (*libxl__numa_candidate_cmpf)(const libxl__numa_candidate *c1,
-                                          const libxl__numa_candidate *c2);
-
-/*
- * This looks for the best NUMA placement candidate satisfying some
- * specific conditions. If min_nodes and/or max_nodes are not 0, their
- * value is used to determine the minimum and maximum number of nodes the
- * candidate can have. If they are 0, it means the candidate can contain
- * from 1 node (min_nodes=0) to the total number of nodes of the host
- * (max_ndoes=0). If min_free_memkb and/or min_cpus are not 0, the caller
- * only wants candidates with at least the amount of free memory and the
- * number of cpus they specify, respectively. If they are 0, the
- * candidates' free memory and/or number of cpus won't be checked at all.
- *
- * Candidates are compared among each others by calling numa_cmpf(), which
- * is where the heuristics for determining which candidate is the best
- * one is actually implemented. The only bit of it that is hardcoded in
- * this function is the fact that candidates with fewer nodes are always
- * preferrable.
- *
- * If at least one suitable candidate is found, it is returned in cndt_out,
- * cndt_found is set to one, and the function returns successfully. On the
- * other hand, if not even one single candidate can be found, the function
- * still returns successfully but cndt_found will be zero.
- *
- * Finally, suitable_cpumap is useful for telling that only the cpus in that
- * mask should be considered when generating placement candidates (for
- * example because of cpupools).
- *
- * It is up to the function to properly allocate cndt_out (by calling
- * libxl__numa_candidate_alloc()), while it is the caller that should init
- * (libxl__numa_candidate_init()) and free (libxl__numa_candidate_dispose())
- * it.
- */
-_hidden int libxl__get_numa_candidate(libxl__gc *gc,
-                                      uint64_t min_free_memkb, int min_cpus,
-                                      int min_nodes, int max_nodes,
-                                      const libxl_bitmap *suitable_cpumap,
-                                      libxl__numa_candidate_cmpf numa_cmpf,
-                                      libxl__numa_candidate *cndt_out,
-                                      int *cndt_found);
-
-/* Initialization, allocation and deallocation for placement candidates */
-static inline void libxl__numa_candidate_init(libxl__numa_candidate *cndt)
-{
-    cndt->free_memkb = 0;
-    cndt->nr_cpus = cndt->nr_nodes = cndt->nr_vcpus = 0;
-    libxl_bitmap_init(&cndt->nodemap);
-}
-
-static inline int libxl__numa_candidate_alloc(libxl__gc *gc,
-                                              libxl__numa_candidate *cndt)
-{
-    return libxl_node_bitmap_alloc(CTX, &cndt->nodemap, 0);
-}
-static inline void libxl__numa_candidate_dispose(libxl__numa_candidate *cndt)
-{
-    libxl_bitmap_dispose(&cndt->nodemap);
-}
-
-/* Retrieve (in nodemap) the node map associated to placement candidate cndt */
-static inline
-void libxl__numa_candidate_get_nodemap(libxl__gc *gc,
-                                       const libxl__numa_candidate *cndt,
-                                       libxl_bitmap *nodemap)
-{
-    libxl_bitmap_copy(CTX, nodemap, &cndt->nodemap);
-}
-/* Set the node map of placement candidate cndt to match nodemap */
-static inline
-void libxl__numa_candidate_put_nodemap(libxl__gc *gc,
-                                       libxl__numa_candidate *cndt,
-                                       const libxl_bitmap *nodemap)
-{
-    libxl_bitmap_copy(CTX, &cndt->nodemap, nodemap);
-}
-
-/* Check if vNUMA config is valid. Returns 0 if valid,
- * ERROR_VNUMA_CONFIG_INVALID otherwise.
- */
-int libxl__vnuma_config_check(libxl__gc *gc,
-                              const libxl_domain_build_info *b_info,
-                              const libxl__domain_build_state *state);
-int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
-                                            uint32_t domid,
-                                            libxl_domain_build_info *b_info,
-                                            libxl__domain_build_state *state);
-int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
-                                    uint32_t domid,
-                                    libxl_domain_build_info *b_info,
-                                    libxl__domain_build_state *state);
-int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
-                                     uint32_t domid,
-                                     libxl_domain_build_info *b_info,
-                                     libxl__domain_build_state *state,
-                                     struct xc_dom_image *dom);
-bool libxl__vnuma_configured(const libxl_domain_build_info *b_info);
-
-_hidden int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid,
-                                   const libxl_ms_vm_genid *id);
-
-
-/* Som handy macros for defbool type. */
-#define LIBXL__DEFBOOL_DEFAULT     (0)
-#define LIBXL__DEFBOOL_FALSE       (-1)
-#define LIBXL__DEFBOOL_TRUE        (1)
-#define LIBXL__DEFBOOL_STR_DEFAULT "<default>"
-#define LIBXL__DEFBOOL_STR_FALSE   "False"
-#define LIBXL__DEFBOOL_STR_TRUE    "True"
-static inline int libxl__defbool_is_default(libxl_defbool *db)
-{
-    return !db->val;
-}
-
-/*
- * Inserts "elm_new" into the sorted list "head".
- *
- * "elm_search" must be a loop search variable of the same type as
- * "elm_new".  "new_after_search_p" must be an expression which is
- * true iff the element "elm_new" sorts after the element
- * "elm_search".
- *
- * "search_body" can be empty, or some declaration(s) and statement(s)
- * needed for "new_after_search_p".
- */
-#define LIBXL_TAILQ_INSERT_SORTED(head, entry, elm_new, elm_search,     \
-                                  search_body, new_after_search_p)      \
-    do {                                                                \
-        for ((elm_search) = LIBXL_TAILQ_FIRST((head));                  \
-             (elm_search);                                              \
-             (elm_search) = LIBXL_TAILQ_NEXT((elm_search), entry)) {    \
-            search_body;                                                \
-            if (!(new_after_search_p))                                  \
-                break;                                                  \
-        }                                                               \
-        /* now elm_search is either the element before which we want    \
-         * to place elm_new, or NULL meaning we want to put elm_new at  \
-         * the end */                                                   \
-        if ((elm_search))                                               \
-            LIBXL_TAILQ_INSERT_BEFORE((elm_search), (elm_new), entry);  \
-        else                                                            \
-            LIBXL_TAILQ_INSERT_TAIL((head), (elm_new), entry);          \
-    } while(0)
-
-
-/*
- * int CTYPE(ISFOO, char c);
- * int CTYPE(toupper, char c);
- * int CTYPE(tolower, char c);
- *
- * This is necessary because passing a simple char to a ctype.h
- * is forbidden.  ctype.h macros take ints derived from _unsigned_ chars.
- *
- * If you have a char which might be EOF then you should already have
- * it in an int representing an unsigned char, and you can use the
- * <ctype.h> macros directly.  This generally happens only with values
- * from fgetc et al.
- *
- * For any value known to be a character (eg, anything that came from
- * a char[]), use CTYPE.
- */
-#define CTYPE(isfoo,c) (isfoo((unsigned char)(c)))
-
-int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                              libxl_defbool *p);
-int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                           bool *p);
-int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                          libxl_mac *p);
-int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                             libxl_bitmap *p);
-int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                           libxl_uuid *p);
-int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
-                                        const libxl__json_object *o,
-                                        libxl_cpuid_policy_list *p);
-int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                                  libxl_string_list *p);
-int libxl__key_value_list_parse_json(libxl__gc *gc,
-                                     const libxl__json_object *o,
-                                     libxl_key_value_list *p);
-int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                            libxl_hwcap *p);
-int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                                  libxl_ms_vm_genid *p);
-int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                          void *p);
-int libxl__uint8_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                            void *p);
-int libxl__uint16_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                             void *p);
-int libxl__uint32_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                             void *p);
-int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                             void *p);
-int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                             char **p);
-
-int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len);
-
-#include "_libxl_types_private.h"
-#include "_libxl_types_internal_private.h"
-
-/* This always return false, there's no "default value" for hw cap */
-static inline int libxl__hwcap_is_default(libxl_hwcap *hwcap)
-{
-    return 0;
-}
-
-static inline int libxl__string_list_is_empty(libxl_string_list *psl)
-{
-    return !libxl_string_list_length(psl);
-}
-
-static inline int libxl__key_value_list_is_empty(libxl_key_value_list *pkvl)
-{
-    return !libxl_key_value_list_length(pkvl);
-}
-
-int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl);
-
-/* Portability note: a proper flock(2) implementation is required */
-typedef struct {
-    libxl__carefd *carefd;
-    char *path; /* path of the lock file itself */
-} libxl__flock;
-/* The CTX_LOCK must be held around uses of this lock */
-
-libxl__flock *libxl__lock_file(libxl__gc *gc, const char *filename);
-void libxl__unlock_file(libxl__flock *lock);
-
-libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid);
-libxl__flock *libxl__lock_domid_history(libxl__gc *gc);
-
-/*
- * Retrieve / store domain configuration from / to libxl private
- * data store. The registry entry in libxl private data store
- * is "libxl-json".
- * Caller must hold user data lock.
- *
- * Other names used for this lock throughout the libxl code are json_lock,
- * libxl__domain_userdata_lock, "libxl-json", data store lock.
- *
- * See the comment for libxl__ao_device, and "Algorithm for handling device
- * removal", for information about using the libxl-json lock / json_lock.
- */
-int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
-                                    libxl_domain_config *d_config);
-int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
-                                    libxl_domain_config *d_config);
-
-/* ------ Things related to updating domain configurations ----- */
-void libxl__update_domain_configuration(libxl__gc *gc,
-                                        libxl_domain_config *dst,
-                                        const libxl_domain_config *src);
-
-/* Target memory in xenstore is different from what user has
- * asked for. The difference is video_memkb + (possible) fudge.
- * See libxl_set_memory_target.
- */
-static inline
-uint64_t libxl__get_targetmem_fudge(libxl__gc *gc,
-                                    const libxl_domain_build_info *info)
-{
-    int64_t mem_target_fudge = (info->type == LIBXL_DOMAIN_TYPE_HVM &&
-                                info->max_memkb > info->target_memkb)
-                                ? LIBXL_MAXMEM_CONSTANT : 0;
-
-    return info->video_memkb + mem_target_fudge;
-}
-
-int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
-                             uint64_t *out_target_memkb,
-                             uint64_t *out_max_memkb);
-void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
-                          const xc_domaininfo_t *xcinfo,
-                          libxl_dominfo *xlinfo);
-
-/* Macros used to compare device identifier. Returns true if the two
- * devices have same identifier. */
-#define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid)
-#define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev))
-#define COMPARE_PCI(a, b) ((a)->func == (b)->func &&    \
-                           (a)->bus == (b)->bus &&      \
-                           (a)->dev == (b)->dev)
-#define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \
-                           (a)->port == (b)->port)
-#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid)
-
-/* This function copies X bytes from source to destination bitmap,
- * where X is the smaller of the two sizes.
- *
- * If destination's size is larger than source, the extra bytes are
- * untouched.
- *
- * XXX This is introduced to fix a regression for 4.5. It shall
- * be revisited in 4.6 time frame.
- */
-void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
-                                    const libxl_bitmap *sptr);
-
-int libxl__count_physical_sockets(libxl__gc *gc, int *sockets);
-
-_hidden int libxl__read_sysfs_file_contents(libxl__gc *gc,
-                                            const char *filename,
-                                            void **data_r,
-                                            int *datalen_r);
-
-#define LIBXL_QEMU_USER_PREFIX "xen-qemuuser"
-#define LIBXL_QEMU_USER_SHARED LIBXL_QEMU_USER_PREFIX"-shared"
-#define LIBXL_QEMU_USER_RANGE_BASE LIBXL_QEMU_USER_PREFIX"-range-base"
-#define LIBXL_QEMU_USER_REAPER LIBXL_QEMU_USER_PREFIX"-reaper"
-
-static inline bool libxl__acpi_defbool_val(const libxl_domain_build_info *b_info)
-{
-    return libxl_defbool_val(b_info->acpi) &&
-           libxl_defbool_val(b_info->u.hvm.acpi);
-}
-
-/*
- * Add a device in libxl_domain_config structure
- *
- * If there is already a device with the same identifier in d_config,
- * that entry is updated.
- *
- * parameters:
- *  d_config: pointer to template domain config
- *  dt:       type of `dev'
- *  dev:      the device that is to be added / removed / updated
- *            (a copy of `dev' will be made)
- */
-void device_add_domain_config(libxl__gc *gc, libxl_domain_config *d_config,
-                              const libxl__device_type *dt,
-                              const void *dev);
-
-void libxl__device_add_async(libxl__egc *egc, uint32_t domid,
-                             const libxl__device_type *dt, void *type,
-                             libxl__ao_device *aodev);
-int libxl__device_add(libxl__gc *gc, uint32_t domid,
-                      const libxl__device_type *dt, void *type);
-
-/* Caller is responsible for freeing the memory by calling
- * libxl__device_list_free
- */
-void* libxl__device_list(libxl__gc *gc, const libxl__device_type *dt,
-                         uint32_t domid, int *num);
-void libxl__device_list_free(const libxl__device_type *dt,
-                             void *list, int num);
-
-static inline bool libxl__timer_mode_is_default(libxl_timer_mode *tm)
-{
-    return *tm == LIBXL_TIMER_MODE_DEFAULT;
-}
-
-static inline bool libxl__string_is_default(char **s)
-{
-    return *s == NULL;
-}
-
-_hidden int libxl__prepare_sockaddr_un(libxl__gc *gc, struct sockaddr_un *un,
-                                       const char *path, const char *what);
-static inline const char *libxl__qemu_qmp_path(libxl__gc *gc, int domid)
-{
-    return GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
-}
-
-/* Send control commands over xenstore and wait for an Ack. */
-_hidden int libxl__domain_pvcontrol(libxl__egc *egc,
-                                    libxl__xswait_state *pvcontrol,
-                                    domid_t domid, const char *cmd);
-
-/*
- * Maximum number of seconds after desctruction then a domid remains
- * 'recent'. Recent domids are not allowed to be re-used. This can be
- * overidden, for debugging purposes, by the environment variable of the
- * same name.
- */
-#define LIBXL_DOMID_REUSE_TIMEOUT 60
-
-/* Check whether a domid is recent */
-int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent);
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_json.c b/tools/libxl/libxl_json.c
deleted file mode 100644 (file)
index 9b8ef2c..0000000
+++ /dev/null
@@ -1,1191 +0,0 @@
-/*
- * Copyright (C) 2011      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <math.h>
-
-#include <yajl/yajl_parse.h>
-#include <yajl/yajl_gen.h>
-
-#include "libxl_internal.h"
-
-/* #define DEBUG_ANSWER */
-
-typedef struct libxl__yajl_ctx {
-    libxl__gc *gc;
-    yajl_handle hand;
-    libxl__json_object *head;
-    libxl__json_object *current;
-#ifdef DEBUG_ANSWER
-    yajl_gen g;
-#endif
-} libxl__yajl_ctx;
-
-#ifdef DEBUG_ANSWER
-#if YAJL_VERSION < 20000
-#  define DEBUG_GEN_ALLOC(ctx)                  \
-    if ((ctx)->g == NULL) {                     \
-        yajl_gen_config conf = { 1, "  " };     \
-        (ctx)->g = yajl_gen_alloc(&conf, NULL); \
-    }
-#else  /* YAJL2 */
-#  define DEBUG_GEN_ALLOC(ctx)                                    \
-    if ((ctx)->g == NULL) {                                       \
-        (ctx)->g = yajl_gen_alloc(NULL);                          \
-        yajl_gen_config((ctx)->g, yajl_gen_beautify, 1);          \
-        yajl_gen_config((ctx)->g, yajl_gen_indent_string, "  ");  \
-    }
-#endif
-#  define DEBUG_GEN_FREE(ctx) \
-    if ((ctx)->g) yajl_gen_free((ctx)->g)
-#  define DEBUG_GEN(ctx, type)              yajl_gen_##type(ctx->g)
-#  define DEBUG_GEN_VALUE(ctx, type, value) yajl_gen_##type(ctx->g, value)
-#  define DEBUG_GEN_STRING(ctx, str, n)     yajl_gen_string(ctx->g, str, n)
-#  define DEBUG_GEN_NUMBER(ctx, str, n)     yajl_gen_number(ctx->g, str, n)
-#  define DEBUG_GEN_REPORT(yajl_ctx) \
-    do { \
-        const unsigned char *buf = NULL; \
-        size_t len = 0; \
-        yajl_gen_get_buf((yajl_ctx)->g, &buf, &len); \
-        LIBXL__LOG(libxl__gc_owner((yajl_ctx)->gc), XTL_DEBUG, \
-                  "response: %s\n", buf); \
-        yajl_gen_free((yajl_ctx)->g); \
-        (yajl_ctx)->g = NULL; \
-    } while (0)
-#else
-#  define DEBUG_GEN_ALLOC(ctx)                  ((void)0)
-#  define DEBUG_GEN_FREE(ctx)                   ((void)0)
-#  define DEBUG_GEN(ctx, type)                  ((void)0)
-#  define DEBUG_GEN_VALUE(ctx, type, value)     ((void)0)
-#  define DEBUG_GEN_STRING(ctx, value, length)  ((void)0)
-#  define DEBUG_GEN_NUMBER(ctx, value, length)  ((void)0)
-#  define DEBUG_GEN_REPORT(ctx)                 ((void)0)
-#endif
-
-/*
- * YAJL Helper
- */
-
-yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str)
-{
-    return yajl_gen_string(hand, (const unsigned char *)str, strlen(str));
-}
-
-yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str)
-{
-    if (str)
-        return libxl__yajl_gen_asciiz(hand, str);
-    else
-        return yajl_gen_null(hand);
-}
-
-/*
- * YAJL generators for builtin libxl types.
- */
-yajl_gen_status libxl_defbool_gen_json(yajl_gen hand,
-                                       libxl_defbool *db)
-{
-    return libxl__yajl_gen_asciiz(hand, libxl_defbool_to_string(*db));
-}
-
-int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                              libxl_defbool *p)
-{
-    const char *s;
-
-    if (!libxl__json_object_is_string(o))
-        return ERROR_FAIL;
-
-    s = libxl__json_object_get_string(o);
-
-    if (!strncmp(s, LIBXL__DEFBOOL_STR_DEFAULT,
-                 strlen(LIBXL__DEFBOOL_STR_DEFAULT)))
-        p->val = LIBXL__DEFBOOL_DEFAULT;
-    else if (!strncmp(s, LIBXL__DEFBOOL_STR_TRUE,
-                      strlen(LIBXL__DEFBOOL_STR_TRUE)))
-        p->val = LIBXL__DEFBOOL_TRUE;
-    else if (!strncmp(s, LIBXL__DEFBOOL_STR_FALSE,
-                      strlen(LIBXL__DEFBOOL_STR_FALSE)))
-        p->val = LIBXL__DEFBOOL_FALSE;
-    else
-        return ERROR_FAIL;
-
-    return 0;
-}
-
-int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                           bool *p)
-{
-    if (!libxl__json_object_is_bool(o))
-        return ERROR_FAIL;
-
-    *p = libxl__json_object_get_bool(o);
-
-    return 0;
-}
-
-yajl_gen_status libxl_uuid_gen_json(yajl_gen hand,
-                                    libxl_uuid *uuid)
-{
-    char buf[LIBXL_UUID_FMTLEN+1];
-    snprintf(buf, sizeof(buf), LIBXL_UUID_FMT, LIBXL_UUID_BYTES((*uuid)));
-    return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_UUID_FMTLEN);
-}
-
-int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                           libxl_uuid *p)
-{
-    if (!libxl__json_object_is_string(o))
-        return ERROR_FAIL;
-
-    return libxl_uuid_from_string(p, o->u.string);
-}
-
-yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand,
-                                      libxl_bitmap *bitmap)
-{
-    yajl_gen_status s;
-    int i;
-
-    s = yajl_gen_array_open(hand);
-    if (s != yajl_gen_status_ok) goto out;
-
-    libxl_for_each_bit(i, *bitmap) {
-        if (libxl_bitmap_test(bitmap, i)) {
-            s = yajl_gen_integer(hand, i);
-            if (s != yajl_gen_status_ok) goto out;
-        }
-    }
-    s = yajl_gen_array_close(hand);
-out:
-    return s;
-}
-
-int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                            libxl_bitmap *p)
-{
-    int i;
-    int size;
-    const libxl__json_object *t;
-    flexarray_t *array;
-
-    if (!libxl__json_object_is_array(o))
-        return ERROR_FAIL;
-
-    array = libxl__json_object_get_array(o);
-    if (!array->count) {
-        libxl_bitmap_init(p);
-        return 0;
-    }
-
-    t = libxl__json_array_get(o, array->count - 1);
-    if (!libxl__json_object_is_integer(t))
-        return ERROR_FAIL;
-    size = libxl__json_object_get_integer(t) + 1;
-
-    libxl_bitmap_alloc(CTX, p, size);
-
-    for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
-        if (!libxl__json_object_is_integer(t))
-            return ERROR_FAIL;
-
-        libxl_bitmap_set(p, libxl__json_object_get_integer(t));
-    }
-
-    return 0;
-}
-
-yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
-                                              libxl_key_value_list *pkvl)
-{
-    libxl_key_value_list kvl = *pkvl;
-    yajl_gen_status s;
-    int i;
-
-    s = yajl_gen_map_open(hand);
-    if (s != yajl_gen_status_ok) goto out;
-
-    if (!kvl) goto empty;
-
-    for (i = 0; kvl[i] != NULL; i += 2) {
-        s = libxl__yajl_gen_asciiz(hand, kvl[i]);
-        if (s != yajl_gen_status_ok) goto out;
-        if (kvl[i + 1])
-            s = libxl__yajl_gen_asciiz(hand, kvl[i+1]);
-        else
-            s = yajl_gen_null(hand);
-        if (s != yajl_gen_status_ok) goto out;
-    }
-empty:
-    s = yajl_gen_map_close(hand);
-out:
-    return s;
-}
-
-int libxl__key_value_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                                     libxl_key_value_list *p)
-{
-    libxl__json_map_node *node = NULL;
-    flexarray_t *maps = NULL;
-    int i, size;
-    libxl_key_value_list kvl;
-
-    if (!libxl__json_object_is_map(o))
-        return ERROR_FAIL;
-
-    maps = libxl__json_object_get_map(o);
-    size = maps->count * 2;
-    kvl = *p = libxl__calloc(NOGC, size+1, sizeof(char *));
-
-    for (i = 0; i < maps->count; i++) {
-        int idx = i * 2;
-        if (flexarray_get(maps, i, (void**)&node) != 0)
-            return ERROR_FAIL;
-
-        if (!libxl__json_object_is_string(node->obj) &&
-            !libxl__json_object_is_null(node->obj))
-            return ERROR_FAIL;
-
-        kvl[idx] = libxl__strdup(NOGC, node->map_key);
-        if (libxl__json_object_is_string(node->obj))
-            kvl[idx+1] =
-                libxl__strdup(NOGC, libxl__json_object_get_string(node->obj));
-        else
-            kvl[idx+1] = NULL;
-    }
-
-    return 0;
-}
-
-yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *pl)
-{
-    libxl_string_list l = *pl;
-    yajl_gen_status s;
-    int i;
-
-    s = yajl_gen_array_open(hand);
-    if (s != yajl_gen_status_ok) goto out;
-
-    if (!l) goto empty;
-
-    for (i = 0; l[i] != NULL; i++) {
-        s = libxl__yajl_gen_asciiz(hand, l[i]);
-        if (s != yajl_gen_status_ok) goto out;
-    }
-empty:
-    s = yajl_gen_array_close(hand);
-out:
-    return s;
-}
-
-int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                                  libxl_string_list *p)
-{
-    const libxl__json_object *t;
-    libxl_string_list l;
-    flexarray_t *array = NULL;
-    int i, size;
-
-    if (!libxl__json_object_is_array(o))
-        return ERROR_FAIL;
-
-    array = libxl__json_object_get_array(o);
-    size = array->count;
-
-    if (size == 0) {
-        *p = NULL;
-        return 0;
-    }
-
-    /* need one extra slot as sentinel */
-    l = *p = libxl__calloc(NOGC, size + 1, sizeof(char *));
-
-    for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
-        if (!libxl__json_object_is_string(t))
-            return ERROR_FAIL;
-
-        l[i] = libxl__strdup(NOGC, libxl__json_object_get_string(t));
-    }
-
-    return 0;
-}
-
-yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *mac)
-{
-    char buf[LIBXL_MAC_FMTLEN+1];
-    snprintf(buf, sizeof(buf), LIBXL_MAC_FMT, LIBXL_MAC_BYTES((*mac)));
-    return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_MAC_FMTLEN);
-}
-
-int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                          libxl_mac *p)
-{
-    if (!libxl__json_object_is_string(o))
-        return ERROR_FAIL;
-
-    return libxl__parse_mac(libxl__json_object_get_string(o), *p);
-}
-
-yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand,
-                                     libxl_hwcap *p)
-{
-    yajl_gen_status s;
-    int i;
-
-    s = yajl_gen_array_open(hand);
-    if (s != yajl_gen_status_ok) goto out;
-
-    for(i=0; i<4; i++) {
-        s = yajl_gen_integer(hand, (*p)[i]);
-        if (s != yajl_gen_status_ok) goto out;
-    }
-    s = yajl_gen_array_close(hand);
-out:
-    return s;
-}
-
-int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                            libxl_hwcap *p)
-{
-    int i;
-
-    if (!libxl__json_object_is_array(o))
-        return ERROR_FAIL;
-
-    for (i = 0; i<4; i++) {
-        const libxl__json_object *t;
-
-        t = libxl__json_array_get(o, i);
-        if (!t || !libxl__json_object_is_integer(t))
-            return ERROR_FAIL;
-
-        (*p)[i] = libxl__json_object_get_integer(t);
-    }
-
-    return 0;
-}
-
-yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p)
-{
-    yajl_gen_status s;
-    int i;
-
-    s = yajl_gen_array_open(hand);
-    if (s != yajl_gen_status_ok)
-        return s;
-
-    for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
-        s = yajl_gen_integer(hand, p->bytes[i]);
-        if (s != yajl_gen_status_ok)
-            return s;
-    }
-
-    return yajl_gen_array_close(hand);
-}
-
-int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                                  libxl_ms_vm_genid *p)
-{
-    unsigned int i;
-
-    if (!libxl__json_object_is_array(o))
-        return ERROR_FAIL;
-
-    for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
-        const libxl__json_object *t;
-
-        t = libxl__json_array_get(o, i);
-        if (!t || !libxl__json_object_is_integer(t))
-            return ERROR_FAIL;
-
-        p->bytes[i] = libxl__json_object_get_integer(t);
-    }
-
-    return 0;
-}
-
-yajl_gen_status libxl__string_gen_json(yajl_gen hand,
-                                       const char *p)
-{
-    if (p)
-        return libxl__yajl_gen_asciiz(hand, p);
-    else
-        return yajl_gen_null(hand);
-}
-
-int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                             char **p)
-{
-    if (!libxl__json_object_is_string(o) && !libxl__json_object_is_null(o))
-        return ERROR_FAIL;
-
-    if (libxl__json_object_is_null(o))
-        *p = NULL;
-    else
-        *p = libxl__strdup(NOGC, libxl__json_object_get_string(o));
-
-    return 0;
-}
-
-/*
- * libxl__json_object helper functions
- */
-
-libxl__json_object *libxl__json_object_alloc(libxl__gc *gc,
-                                             libxl__json_node_type type)
-{
-    libxl__json_object *obj;
-
-    obj = libxl__zalloc(gc, sizeof(*obj));
-
-    obj->type = type;
-
-    if (type == JSON_MAP || type == JSON_ARRAY) {
-        flexarray_t *array = flexarray_make(gc, 1, 1);
-        if (type == JSON_MAP)
-            obj->u.map = array;
-        else
-            obj->u.array = array;
-    }
-
-    return obj;
-}
-
-static int libxl__json_object_append_to(libxl__gc *gc,
-                                        libxl__json_object *obj,
-                                        libxl__yajl_ctx *ctx)
-{
-    libxl__json_object *dst = ctx->current;
-
-    if (dst) {
-        switch (dst->type) {
-        case JSON_MAP: {
-            libxl__json_map_node *last;
-
-            if (dst->u.map->count == 0) {
-                LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
-                           "Try to add a value to an empty map (with no key)");
-                return ERROR_FAIL;
-            }
-            flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last);
-            last->obj = obj;
-            break;
-        }
-        case JSON_ARRAY:
-            flexarray_append(dst->u.array, obj);
-            break;
-        default:
-            LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
-                       "Try append an object is not a map/array (%i)",
-                       dst->type);
-            return ERROR_FAIL;
-        }
-    }
-
-    obj->parent = dst;
-
-    if (libxl__json_object_is_map(obj) || libxl__json_object_is_array(obj))
-        ctx->current = obj;
-    if (ctx->head == NULL)
-        ctx->head = obj;
-
-    return 0;
-}
-
-void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj)
-{
-    int idx = 0;
-
-    if (obj == NULL)
-        return;
-    switch (obj->type) {
-    case JSON_STRING:
-    case JSON_NUMBER:
-        free(obj->u.string);
-        break;
-    case JSON_MAP: {
-        libxl__json_map_node *node = NULL;
-
-        for (idx = 0; idx < obj->u.map->count; idx++) {
-            if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
-                break;
-            libxl__json_object_free(gc, node->obj);
-            free(node->map_key);
-            free(node);
-            node = NULL;
-        }
-        flexarray_free(obj->u.map);
-        break;
-    }
-    case JSON_ARRAY: {
-        libxl__json_object *node = NULL;
-
-        for (idx = 0; idx < obj->u.array->count; idx++) {
-            if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
-                break;
-            libxl__json_object_free(gc, node);
-            node = NULL;
-        }
-        flexarray_free(obj->u.array);
-        break;
-    }
-    default:
-        break;
-    }
-    free(obj);
-}
-
-libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i)
-{
-    flexarray_t *array = NULL;
-    libxl__json_object *obj = NULL;
-
-    if ((array = libxl__json_object_get_array(o)) == NULL) {
-        return NULL;
-    }
-
-    if (i >= array->count)
-        return NULL;
-
-    if (flexarray_get(array, i, (void**)&obj) != 0)
-        return NULL;
-
-    return obj;
-}
-
-libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
-                                               int i)
-{
-    flexarray_t *array = NULL;
-    libxl__json_map_node *obj = NULL;
-
-    if ((array = libxl__json_object_get_map(o)) == NULL) {
-        return NULL;
-    }
-
-    if (i >= array->count)
-        return NULL;
-
-    if (flexarray_get(array, i, (void**)&obj) != 0)
-        return NULL;
-
-    return obj;
-}
-
-const libxl__json_object *libxl__json_map_get(const char *key,
-                                          const libxl__json_object *o,
-                                          libxl__json_node_type expected_type)
-{
-    flexarray_t *maps = NULL;
-    int idx = 0;
-
-    if (libxl__json_object_is_map(o)) {
-        libxl__json_map_node *node = NULL;
-
-        maps = o->u.map;
-        for (idx = 0; idx < maps->count; idx++) {
-            if (flexarray_get(maps, idx, (void**)&node) != 0)
-                return NULL;
-            if (strcmp(key, node->map_key) == 0) {
-                if (expected_type == JSON_ANY
-                    || (node->obj && (node->obj->type & expected_type))) {
-                    return node->obj;
-                } else {
-                    return NULL;
-                }
-            }
-        }
-    }
-    return NULL;
-}
-
-yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc,
-                                           yajl_gen hand,
-                                           const libxl__json_object *obj)
-{
-    int idx = 0;
-    yajl_status rc;
-
-#define CONVERT_YAJL_GEN_TO_STATUS(gen) \
-    ((gen) == yajl_gen_status_ok ? yajl_status_ok : yajl_status_error)
-
-    switch (obj->type) {
-    case JSON_NULL:
-        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_null(hand));
-    case JSON_BOOL:
-        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_bool(hand, obj->u.b));
-    case JSON_INTEGER:
-        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_integer(hand, obj->u.i));
-    case JSON_DOUBLE:
-        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_double(hand, obj->u.d));
-    case JSON_NUMBER:
-        return CONVERT_YAJL_GEN_TO_STATUS(
-                  yajl_gen_number(hand, obj->u.string, strlen(obj->u.string)));
-    case JSON_STRING:
-        return CONVERT_YAJL_GEN_TO_STATUS(
-                        libxl__yajl_gen_asciiz(hand, obj->u.string));
-    case JSON_MAP: {
-        libxl__json_map_node *node = NULL;
-
-        rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_open(hand));
-        if (rc != yajl_status_ok)
-            return rc;
-        for (idx = 0; idx < obj->u.map->count; idx++) {
-            if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
-                break;
-
-            rc = CONVERT_YAJL_GEN_TO_STATUS(
-                            libxl__yajl_gen_asciiz(hand, node->map_key));
-            if (rc != yajl_status_ok)
-                return rc;
-            rc = libxl__json_object_to_yajl_gen(gc, hand, node->obj);
-            if (rc != yajl_status_ok)
-                return rc;
-        }
-        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_close(hand));
-    }
-    case JSON_ARRAY: {
-        libxl__json_object *node = NULL;
-
-        rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_open(hand));
-        if (rc != yajl_status_ok)
-            return rc;
-        for (idx = 0; idx < obj->u.array->count; idx++) {
-            if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
-                break;
-            rc = libxl__json_object_to_yajl_gen(gc, hand, node);
-            if (rc != yajl_status_ok)
-                return rc;
-        }
-        return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_close(hand));
-    }
-    case JSON_ANY:
-        /* JSON_ANY is not a valid value for obj->type. */
-        ;
-    }
-    abort();
-#undef CONVERT_YAJL_GEN_TO_STATUS
-}
-
-
-/*
- * JSON callbacks
- */
-
-static int json_callback_null(void *opaque)
-{
-    libxl__yajl_ctx *ctx = opaque;
-    libxl__json_object *obj;
-
-    DEBUG_GEN(ctx, null);
-
-    obj = libxl__json_object_alloc(ctx->gc, JSON_NULL);
-
-    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
-        return 0;
-
-    return 1;
-}
-
-static int json_callback_boolean(void *opaque, int boolean)
-{
-    libxl__yajl_ctx *ctx = opaque;
-    libxl__json_object *obj;
-
-    DEBUG_GEN_VALUE(ctx, bool, boolean);
-
-    obj = libxl__json_object_alloc(ctx->gc, JSON_BOOL);
-    obj->u.b = boolean;
-
-    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
-        return 0;
-
-    return 1;
-}
-
-static bool is_decimal(const char *s, unsigned len)
-{
-    const char *end = s + len;
-    for (; s < end; s++) {
-        if (*s == '.')
-            return true;
-    }
-    return false;
-}
-
-static int json_callback_number(void *opaque, const char *s, libxl_yajl_length len)
-{
-    libxl__yajl_ctx *ctx = opaque;
-    libxl__json_object *obj = NULL;
-    char *t = NULL;
-
-    DEBUG_GEN_NUMBER(ctx, s, len);
-
-    if (is_decimal(s, len)) {
-        double d = strtod(s, NULL);
-
-        if ((d == HUGE_VALF || d == HUGE_VALL) && errno == ERANGE) {
-            goto error;
-        }
-
-        obj = libxl__json_object_alloc(ctx->gc, JSON_DOUBLE);
-        obj->u.d = d;
-    } else {
-        long long i = strtoll(s, NULL, 10);
-
-        if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) {
-            goto error;
-        }
-
-        obj = libxl__json_object_alloc(ctx->gc, JSON_INTEGER);
-        obj->u.i = i;
-    }
-    goto out;
-
-error:
-    /* If the conversion fail, we just store the original string. */
-    obj = libxl__json_object_alloc(ctx->gc, JSON_NUMBER);
-
-    t = libxl__zalloc(ctx->gc, len + 1);
-    strncpy(t, s, len);
-    t[len] = 0;
-
-    obj->u.string = t;
-
-out:
-    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
-        return 0;
-
-    return 1;
-}
-
-static int json_callback_string(void *opaque, const unsigned char *str,
-                                libxl_yajl_length len)
-{
-    libxl__yajl_ctx *ctx = opaque;
-    char *t = NULL;
-    libxl__json_object *obj = NULL;
-
-    t = libxl__zalloc(ctx->gc, len + 1);
-
-    DEBUG_GEN_STRING(ctx, str, len);
-
-    strncpy(t, (const char *) str, len);
-    t[len] = 0;
-
-    obj = libxl__json_object_alloc(ctx->gc, JSON_STRING);
-    obj->u.string = t;
-
-    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
-        return 0;
-
-    return 1;
-}
-
-static int json_callback_map_key(void *opaque, const unsigned char *str,
-                                 libxl_yajl_length len)
-{
-    libxl__yajl_ctx *ctx = opaque;
-    char *t = NULL;
-    libxl__json_object *obj = ctx->current;
-    libxl__gc *gc = ctx->gc;
-
-    t = libxl__zalloc(gc, len + 1);
-
-    DEBUG_GEN_STRING(ctx, str, len);
-
-    strncpy(t, (const char *) str, len);
-    t[len] = 0;
-
-    if (libxl__json_object_is_map(obj)) {
-        libxl__json_map_node *node;
-
-        GCNEW(node);
-        node->map_key = t;
-        node->obj = NULL;
-
-        flexarray_append(obj->u.map, node);
-    } else {
-        LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
-                   "Current json object is not a map");
-        return 0;
-    }
-
-    return 1;
-}
-
-static int json_callback_start_map(void *opaque)
-{
-    libxl__yajl_ctx *ctx = opaque;
-    libxl__json_object *obj = NULL;
-
-    DEBUG_GEN(ctx, map_open);
-
-    obj = libxl__json_object_alloc(ctx->gc, JSON_MAP);
-
-    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
-        return 0;
-
-    return 1;
-}
-
-static int json_callback_end_map(void *opaque)
-{
-    libxl__yajl_ctx *ctx = opaque;
-
-    DEBUG_GEN(ctx, map_close);
-
-    if (ctx->current) {
-        ctx->current = ctx->current->parent;
-    } else {
-        LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
-                   "No current libxl__json_object, cannot use his parent.");
-        return 0;
-    }
-
-    return 1;
-}
-
-static int json_callback_start_array(void *opaque)
-{
-    libxl__yajl_ctx *ctx = opaque;
-    libxl__json_object *obj = NULL;
-
-    DEBUG_GEN(ctx, array_open);
-
-    obj = libxl__json_object_alloc(ctx->gc, JSON_ARRAY);
-
-    if (libxl__json_object_append_to(ctx->gc, obj, ctx))
-        return 0;
-
-    return 1;
-}
-
-static int json_callback_end_array(void *opaque)
-{
-    libxl__yajl_ctx *ctx = opaque;
-
-    DEBUG_GEN(ctx, array_close);
-
-    if (ctx->current) {
-        ctx->current = ctx->current->parent;
-    } else {
-        LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
-                   "No current libxl__json_object, cannot use his parent.");
-        return 0;
-    }
-
-    return 1;
-}
-
-static yajl_callbacks callbacks = {
-    json_callback_null,
-    json_callback_boolean,
-    NULL,
-    NULL,
-    json_callback_number,
-    json_callback_string,
-    json_callback_start_map,
-    json_callback_map_key,
-    json_callback_end_map,
-    json_callback_start_array,
-    json_callback_end_array
-};
-
-static void yajl_ctx_free(libxl__yajl_ctx *yajl_ctx)
-{
-    if (yajl_ctx->hand) {
-        yajl_free(yajl_ctx->hand);
-        yajl_ctx->hand = NULL;
-    }
-    DEBUG_GEN_FREE(yajl_ctx);
-}
-
-libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s)
-{
-    yajl_status status;
-    libxl__yajl_ctx yajl_ctx;
-    libxl__json_object *o = NULL;
-    unsigned char *str = NULL;
-
-    memset(&yajl_ctx, 0, sizeof (yajl_ctx));
-    yajl_ctx.gc = gc;
-
-    DEBUG_GEN_ALLOC(&yajl_ctx);
-
-    if (yajl_ctx.hand == NULL) {
-        yajl_ctx.hand = libxl__yajl_alloc(&callbacks, NULL, &yajl_ctx);
-    }
-    status = yajl_parse(yajl_ctx.hand, (const unsigned char *)s, strlen(s));
-    if (status != yajl_status_ok)
-        goto out;
-
-    status = yajl_complete_parse(yajl_ctx.hand);
-    if (status != yajl_status_ok)
-        goto out;
-
-    o = yajl_ctx.head;
-
-    DEBUG_GEN_REPORT(&yajl_ctx);
-
-    yajl_ctx.head = NULL;
-
-    yajl_ctx_free(&yajl_ctx);
-    return o;
-
-out:
-    str = yajl_get_error(yajl_ctx.hand, 1, (const unsigned char*)s, strlen(s));
-
-    LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "yajl error: %s", str);
-    yajl_free_error(yajl_ctx.hand, str);
-    yajl_ctx_free(&yajl_ctx);
-    return NULL;
-}
-
-static const char *yajl_gen_status_to_string(yajl_gen_status s)
-{
-        switch (s) {
-        case yajl_gen_status_ok: abort();
-        case yajl_gen_keys_must_be_strings:
-            return "keys must be strings";
-        case yajl_max_depth_exceeded:
-            return "max depth exceeded";
-        case yajl_gen_in_error_state:
-            return "in error state";
-        case yajl_gen_generation_complete:
-            return "generation complete";
-        case yajl_gen_invalid_number:
-            return "invalid number";
-#if 0 /* This is in the docs but not implemented in the version I am running. */
-        case yajl_gen_no_buf:
-            return "no buffer";
-        case yajl_gen_invalid_string:
-            return "invalid string";
-#endif
-        default:
-            return "unknown error";
-        }
-}
-
-char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
-                            libxl__gen_json_callback gen, void *p)
-{
-    const unsigned char *buf;
-    char *ret = NULL;
-    libxl_yajl_length len = 0;
-    yajl_gen_status s;
-    yajl_gen hand;
-
-    hand = libxl_yajl_gen_alloc(NULL);
-    if (!hand)
-        return NULL;
-
-    s = gen(hand, p);
-    if (s != yajl_gen_status_ok)
-        goto out;
-
-    s = yajl_gen_get_buf(hand, &buf, &len);
-    if (s != yajl_gen_status_ok)
-        goto out;
-    ret = strdup((const char *)buf);
-
-out:
-    yajl_gen_free(hand);
-
-    if (s != yajl_gen_status_ok) {
-        LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
-                   "unable to convert %s to JSON representation. "
-                   "YAJL error code %d: %s", type,
-                   s, yajl_gen_status_to_string(s));
-    } else if (!ret) {
-        LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
-                   "unable to allocate space for to JSON representation of %s",
-                   type);
-    }
-
-    return ret;
-}
-
-char *libxl__json_object_to_json(libxl__gc *gc,
-                                 const libxl__json_object *args)
-{
-    const unsigned char *buf;
-    libxl_yajl_length len;
-    yajl_gen_status s;
-    yajl_gen hand;
-    char *ret = NULL;
-    int rc;
-
-    if (!args)
-        return NULL;
-
-    hand = libxl_yajl_gen_alloc(NULL);
-    if (!hand)
-        return NULL;
-
-    rc = libxl__json_object_to_yajl_gen(gc, hand, args);
-    if (rc)
-        goto out;
-
-    s = yajl_gen_get_buf(hand, &buf, &len);
-    if (s)
-        goto out;
-
-    ret = libxl__strndup(gc, (const char *)buf, len);
-
-out:
-    yajl_gen_free(hand);
-    return ret;
-}
-
-yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val)
-{
-    char *num;
-    int len;
-    yajl_gen_status s;
-
-
-    len = asprintf(&num, "%"PRIu64, val);
-    if (len == -1) {
-        s = yajl_gen_in_error_state;
-        goto out;
-    }
-
-    s = yajl_gen_number(hand, num, len);
-
-    free(num);
-
-out:
-    return s;
-}
-
-int libxl__object_from_json(libxl_ctx *ctx, const char *type,
-                            libxl__json_parse_callback parse,
-                            void *p, const char *s)
-{
-    GC_INIT(ctx);
-    libxl__json_object *o;
-    int rc;
-
-    o = libxl__json_parse(gc, s);
-    if (!o) {
-        LOG(ERROR,
-            "unable to generate libxl__json_object from JSON representation of %s.",
-            type);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = parse(gc, o, p);
-    if (rc) {
-        LOG(ERROR, "unable to convert libxl__json_object to %s. (rc=%d)", type, rc);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                          void *p)
-{
-    long long i;
-
-    if (!libxl__json_object_is_integer(o))
-        return ERROR_FAIL;
-
-    i = libxl__json_object_get_integer(o);
-
-    if (i > INT_MAX || i < INT_MIN)
-        return ERROR_FAIL;
-
-    *((int *)p) = i;
-
-    return 0;
-}
-
-/* Macro to generate:
- *  libxl__uint8_parse_json
- *  libxl__uint16_parse_json
- *  libxl__uint32_parse_json
- */
-#define PARSE_UINT(width)                                               \
-    int libxl__uint ## width ## _parse_json(libxl__gc *gc,              \
-                                            const libxl__json_object *o,\
-                                            void *p)                    \
-    {                                                                   \
-        long long i;                                                    \
-                                                                        \
-        if (!libxl__json_object_is_integer(o))                          \
-            return ERROR_FAIL;                                          \
-                                                                        \
-        i = libxl__json_object_get_integer(o);                          \
-                                                                        \
-        if (i < 0 || i > UINT ## width ## _MAX)                         \
-            return ERROR_FAIL;                                          \
-                                                                        \
-        *((uint ## width ## _t *)p) = i;                                \
-                                                                        \
-        return 0;                                                       \
-    }
-
-PARSE_UINT(8);
-PARSE_UINT(16);
-PARSE_UINT(32);
-
-int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
-                             void *p)
-{
-    if (!libxl__json_object_is_integer(o) &&
-        !libxl__json_object_is_number(o))
-        return ERROR_FAIL;
-
-    if (libxl__json_object_is_integer(o)) {
-        long long i = libxl__json_object_get_integer(o);
-
-        if (i < 0)
-            return ERROR_FAIL;
-
-        *((uint64_t *)p) = i;
-    } else {
-        const char *s;
-        unsigned long long i;
-        int saved_errno = errno;
-
-        s = libxl__json_object_get_number(o);
-
-        errno = 0;
-        i = strtoull(s, NULL, 10);
-
-        if (i == ULLONG_MAX && errno == ERANGE)
-            return ERROR_FAIL;
-
-        errno = saved_errno;
-        *((uint64_t *)p) = i;
-    }
-
-    return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_json.h b/tools/libxl/libxl_json.h
deleted file mode 100644 (file)
index 260783b..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef LIBXL_JSON_H
-#define LIBXL_JSON_H
-
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_parse.h>
-
-#ifdef HAVE_YAJL_YAJL_VERSION_H
-#  include <yajl/yajl_version.h>
-#endif
-
-yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val);
-yajl_gen_status libxl_defbool_gen_json(yajl_gen hand, libxl_defbool *p);
-yajl_gen_status libxl_uuid_gen_json(yajl_gen hand, libxl_uuid *p);
-yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *p);
-yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand, libxl_bitmap *p);
-yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
-                                                 libxl_cpuid_policy_list *p);
-yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *p);
-yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
-                                              libxl_key_value_list *p);
-yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand, libxl_hwcap *p);
-yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p);
-
-#include <_libxl_types_json.h>
-
-/* YAJL version check */
-#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
-#  define HAVE_YAJL_V2 1
-#endif
-
-#ifdef HAVE_YAJL_V2
-
-typedef size_t libxl_yajl_length;
-
-static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
-                                            yajl_alloc_funcs *allocFuncs,
-                                            void *ctx)
-{
-    yajl_handle hand = yajl_alloc(callbacks, allocFuncs, ctx);
-    if (hand)
-        yajl_config(hand, yajl_allow_trailing_garbage, 1);
-    return hand;
-}
-
-static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
-{
-    yajl_gen g;
-    g = yajl_gen_alloc(allocFuncs);
-    if (g)
-        yajl_gen_config(g, yajl_gen_beautify, 1);
-    return g;
-}
-
-#else /* !HAVE_YAJL_V2 */
-
-#define yajl_complete_parse yajl_parse_complete
-
-typedef unsigned int libxl_yajl_length;
-
-static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks,
-                                            const yajl_alloc_funcs *allocFuncs,
-                                            void *ctx)
-{
-    yajl_parser_config cfg = {
-        .allowComments = 1,
-        .checkUTF8 = 1,
-    };
-    return yajl_alloc(callbacks, &cfg, allocFuncs, ctx);
-}
-
-static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs)
-{
-    yajl_gen_config conf = { 1, "    " };
-    return yajl_gen_alloc(&conf, allocFuncs);
-}
-
-#endif /* !HAVE_YAJL_V2 */
-
-yajl_gen_status libxl_domain_config_gen_json(yajl_gen hand,
-                                             libxl_domain_config *p);
-
-#endif /* LIBXL_JSON_H */
diff --git a/tools/libxl/libxl_libfdt_compat.c b/tools/libxl/libxl_libfdt_compat.c
deleted file mode 100644 (file)
index 02b8f74..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- *
- * This file is part of libxl, and was originally taken from libfdt.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- *
- * Additionally, this particular file is dual licensed.  That is,
- * alternatively, at your option:
- *
- *      Redistribution and use in source and binary forms, with or
- *      without modification, are permitted provided that the following
- *      conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *      2. Redistributions in binary form must reproduce the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer in the documentation and/or other materials
- *         provided with the distribution.
- *
- *      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- *      CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- *      INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- *      MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- *      DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- *      CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *      SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *      NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- *      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- *      HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- *      OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- *      EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Note that this applies only to this file, and other files with a
- * similar notice.  Also, note that when the same code is distributed
- * along with the rest of libxl, you must comply with the terms of the
- * LGPLv2.1 for the whole of libxl including this file.
- *
- * The intent is to permit, in particular, upstream libfdt to
- * incorporate improvements to this file within upstream libfdt.  At
- * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
- * (as above) and GPLv2-or-later.  The 2-clause BSD licence is
- * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
- * copying in both directions, and the optional licence upgrade to a
- * copyleft licence by libdft upstream or the Xen Project,
- * respectively.
- */
-
-#include <libfdt.h>
-
-#include "libxl_libfdt_compat.h"
-
-#ifndef HAVE_FDT_FIRST_SUBNODE
-_hidden int fdt_first_subnode(const void *fdt, int offset)
-{
-       int depth = 0;
-
-       offset = fdt_next_node(fdt, offset, &depth);
-       if (offset < 0 || depth != 1)
-               return -FDT_ERR_NOTFOUND;
-
-       return offset;
-}
-#endif
-
-#ifndef HAVE_FDT_NEXT_SUBNODE
-_hidden int fdt_next_subnode(const void *fdt, int offset)
-{
-       int depth = 1;
-
-       /*
-        * With respect to the parent, the depth of the next subnode will be
-        * the same as the last.
-        */
-       do {
-               offset = fdt_next_node(fdt, offset, &depth);
-               if (offset < 0 || depth < 1)
-                       return -FDT_ERR_NOTFOUND;
-       } while (depth > 1);
-
-       return offset;
-}
-#endif
diff --git a/tools/libxl/libxl_libfdt_compat.h b/tools/libxl/libxl_libfdt_compat.h
deleted file mode 100644 (file)
index 23230b5..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- *
- * This file is part of libxl, and was originally taken from libfdt.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- *
- * Additionally, this particular file is dual licensed.  That is,
- * alternatively, at your option:
- *
- *      Redistribution and use in source and binary forms, with or
- *      without modification, are permitted provided that the following
- *      conditions are met:
- *
- *      1. Redistributions of source code must retain the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer.
- *      2. Redistributions in binary form must reproduce the above
- *         copyright notice, this list of conditions and the following
- *         disclaimer in the documentation and/or other materials
- *         provided with the distribution.
- *
- *      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- *      CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- *      INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- *      MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- *      DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- *      CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *      SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *      NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- *      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- *      HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- *      OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- *      EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Note that this applies only to this file, and other files with a
- * similar notice.  Also, note that when the same code is distributed
- * along with the rest of libxl, you must comply with the terms of the
- * LGPLv2.1 for the whole of libxl including this file.
- *
- * The intent is to permit, in particular, upstream libfdt to
- * incorporate improvements to this file within upstream libfdt.  At
- * the time of writing, upstream libfdt is dual licensed: 2-clause BSD
- * (as above) and GPLv2-or-later.  The 2-clause BSD licence is
- * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits
- * copying in both directions, and the optional licence upgrade to a
- * copyleft licence by libdft upstream or the Xen Project,
- * respectively.
- */
-
-#ifndef LIBXL_LIBFDT_COMPAT_H
-#define LIBXL_LIBFDT_COMPAT_H
-
-#include "libxl_internal.h"
-#include <libfdt.h>
-
-#if !HAVE_DECL_FDT_FIRST_SUBNODE
-_hidden int fdt_first_subnode(const void *fdt, int offset);
-#endif
-
-#if !HAVE_DECL_FDT_NEXT_SUBNODE
-_hidden int fdt_next_subnode(const void *fdt, int offset);
-#endif
-
-#if !HAVE_DECL_FDT_PROPERTY_U32
-static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
-{
-       uint32_t tmp = cpu_to_fdt32(val);
-       return fdt_property(fdt, name, &tmp, sizeof(tmp));
-}
-#endif
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_linux.c b/tools/libxl/libxl_linux.c
deleted file mode 100644 (file)
index 873b027..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (C) 2011
- * Author Roger Pau Monne <roger.pau@entel.upc.edu>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <sys/resource.h>
-#include "libxl_internal.h"
-
-
-/* Workarounds for Linux-specific lacks can go here: */
-
-#ifndef CLONE_NEWIPC /* Available as of Linux 2.6.19 / glibc 2.8 */
-# define CLONE_NEWIPC 0x08000000
-#endif
-
-
-int libxl__try_phy_backend(mode_t st_mode)
-{
-    if (S_ISBLK(st_mode) || S_ISREG(st_mode)) {
-        return 1;
-    }
-
-    return 0;
-}
-
-char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
-{
-    return libxl__devid_to_vdev(gc, devid);
-}
-
-/* Hotplug scripts helpers */
-
-static char **get_hotplug_env(libxl__gc *gc,
-                              char *script, libxl__device *dev)
-{
-    const char *type = libxl__device_kind_to_string(dev->backend_kind);
-    char *be_path = libxl__device_backend_path(gc, dev);
-    char **env;
-    int nr = 0;
-
-    const int arraysize = 15;
-    GCNEW_ARRAY(env, arraysize);
-    env[nr++] = "script";
-    env[nr++] = script;
-    env[nr++] = "XENBUS_TYPE";
-    env[nr++] = (char *) type;
-    env[nr++] = "XENBUS_PATH";
-    env[nr++] = GCSPRINTF("backend/%s/%u/%d", type, dev->domid, dev->devid);
-    env[nr++] = "XENBUS_BASE_PATH";
-    env[nr++] = "backend";
-    if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) {
-        libxl_nic_type nictype;
-        char *gatewaydev;
-
-        gatewaydev = libxl__xs_read(gc, XBT_NULL,
-                                    GCSPRINTF("%s/%s", be_path, "gatewaydev"));
-        env[nr++] = "netdev";
-        env[nr++] = gatewaydev ? : "";
-
-        if (libxl__nic_type(gc, dev, &nictype)) {
-            LOGD(ERROR, dev->domid, "unable to get nictype");
-            return NULL;
-        }
-        switch (nictype) {
-        case LIBXL_NIC_TYPE_VIF_IOEMU:
-            env[nr++] = "INTERFACE";
-            env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
-                                                    dev->devid,
-                                                    LIBXL_NIC_TYPE_VIF_IOEMU);
-            /*
-             * We need to fall through because for PV_IOEMU nic types we need
-             * to execute both the vif and the tap hotplug script, and we
-             * don't know which one we are executing in this call, so provide
-             * both env variables.
-             */
-        case LIBXL_NIC_TYPE_VIF:
-            env[nr++] = "vif";
-            env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
-                                                    dev->devid,
-                                                    LIBXL_NIC_TYPE_VIF);
-            break;
-        default:
-            return NULL;
-        }
-    }
-
-    env[nr++] = NULL;
-    assert(nr <= arraysize);
-
-    return env;
-}
-
-/* Hotplug scripts caller functions */
-
-static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
-                               char ***args, char ***env,
-                               libxl__device_action action, int num_exec)
-{
-    char *be_path = libxl__device_backend_path(gc, dev);
-    char *script;
-    int nr = 0, rc = 0;
-    libxl_nic_type nictype;
-
-    script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path,
-                                                             "script"));
-    if (!script) {
-        LOGED(ERROR, dev->domid,
-              "unable to read script from %s", be_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__nic_type(gc, dev, &nictype);
-    if (rc) {
-        LOGD(ERROR, dev->domid, "error when fetching nic type");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
-        rc = 0;
-        goto out;
-    }
-
-    *env = get_hotplug_env(gc, script, dev);
-    if (!*env) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    const int arraysize = 4;
-    GCNEW_ARRAY(*args, arraysize);
-    (*args)[nr++] = script;
-
-    if (nictype == LIBXL_NIC_TYPE_VIF_IOEMU && num_exec) {
-        (*args)[nr++] = (char *) libxl__device_action_to_string(action);
-        (*args)[nr++] = "type_if=tap";
-        (*args)[nr++] = NULL;
-    } else {
-        (*args)[nr++] = action == LIBXL__DEVICE_ACTION_ADD ? "online" :
-                                                             "offline";
-        (*args)[nr++] = "type_if=vif";
-        (*args)[nr++] = NULL;
-    }
-    assert(nr == arraysize);
-    rc = 1;
-
-out:
-    return rc;
-}
-
-static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
-                               char ***args, char ***env,
-                               libxl__device_action action)
-{
-    char *be_path = libxl__device_backend_path(gc, dev);
-    char *script;
-    int nr = 0, rc = 0;
-
-    script = libxl__xs_read(gc, XBT_NULL,
-                            GCSPRINTF("%s/%s", be_path, "script"));
-    if (!script) {
-        LOGEVD(ERROR, errno, dev->domid,
-               "unable to read script from %s", be_path);
-        rc = ERROR_FAIL;
-        goto error;
-    }
-
-    *env = get_hotplug_env(gc, script, dev);
-    if (!*env) {
-        LOGD(ERROR, dev->domid, "Failed to get hotplug environment");
-        rc = ERROR_FAIL;
-        goto error;
-    }
-
-    const int arraysize = 3;
-    GCNEW_ARRAY(*args, arraysize);
-    (*args)[nr++] = script;
-    (*args)[nr++] = (char *) libxl__device_action_to_string(action);
-    (*args)[nr++] = NULL;
-    assert(nr == arraysize);
-
-    LOGD(DEBUG, dev->domid, "Args and environment ready");
-    rc = 1;
-
-error:
-    return rc;
-}
-
-int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
-                                   char ***args, char ***env,
-                                   libxl__device_action action,
-                                   int num_exec)
-{
-    int rc;
-
-    switch (dev->backend_kind) {
-    case LIBXL__DEVICE_KIND_VBD:
-        if (num_exec != 0) {
-            LOGD(DEBUG, dev->domid,
-                 "num_exec %d, not running hotplug scripts", num_exec);
-            rc = 0;
-            goto out;
-        }
-        rc = libxl__hotplug_disk(gc, dev, args, env, action);
-        break;
-    case LIBXL__DEVICE_KIND_VIF:
-        /*
-         * If domain has a stubdom we don't have to execute hotplug scripts
-         * for emulated interfaces
-         */
-        if ((num_exec > 1) ||
-            (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
-            LOGD(DEBUG, dev->domid,
-                 "num_exec %d, not running hotplug scripts", num_exec);
-            rc = 0;
-            goto out;
-        }
-        rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
-        break;
-    default:
-        /* No need to execute any hotplug scripts */
-        LOGD(DEBUG, dev->domid,
-             "backend_kind %d, no need to execute scripts", dev->backend_kind);
-        rc = 0;
-        break;
-    }
-
-out:
-    return rc;
-}
-
-libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
-{
-    return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
-}
-
-int libxl__pci_numdevs(libxl__gc *gc)
-{
-    DIR *dir;
-    struct dirent *entry;
-    int num_devs = 0;
-
-    dir = opendir("/sys/bus/pci/devices");
-    if (!dir) {
-        LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
-        return ERROR_FAIL;
-    }
-
-    while ((entry = readdir(dir))) {
-        if (entry->d_name[0] == '.')
-            continue;
-        num_devs++;
-    }
-    closedir(dir);
-
-    return num_devs;
-}
-
-int libxl__pci_topology_init(libxl__gc *gc,
-                             physdev_pci_device_t *devs,
-                             int num_devs)
-{
-
-    DIR *dir;
-    struct dirent *entry;
-    int i, err = 0;
-
-    dir = opendir("/sys/bus/pci/devices");
-    if (!dir) {
-        LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
-        return ERROR_FAIL;
-    }
-
-    i = 0;
-    while ((entry = readdir(dir))) {
-        unsigned int dom, bus, dev, func;
-
-        if (entry->d_name[0] == '.')
-            continue;
-
-        if (i == num_devs) {
-            LOG(ERROR, "Too many devices");
-            err = ERROR_FAIL;
-            errno = -ENOSPC;
-            goto out;
-        }
-
-        if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4) {
-            LOGE(ERROR, "Error processing /sys/bus/pci/devices");
-            err = ERROR_FAIL;
-            goto out;
-        }
-
-        devs[i].seg = dom;
-        devs[i].bus = bus;
-        devs[i].devfn = ((dev & 0x1f) << 3) | (func & 7);
-
-        i++;
-    }
-
- out:
-    closedir(dir);
-
-    return err;
-}
-
-static struct {
-    int resource;
-    rlim_t limit;
-} rlimits[] = {
-#define RLIMIT_ENTRY(r, l) \
-    { .resource = r, .limit = l }
-    /* Big enough for log files, not big enough for a DoS */
-    RLIMIT_ENTRY(RLIMIT_FSIZE,    256*1024),
-
-    /* Shouldn't need any of these */
-    RLIMIT_ENTRY(RLIMIT_CORE,     0),
-    RLIMIT_ENTRY(RLIMIT_MSGQUEUE, 0),
-    RLIMIT_ENTRY(RLIMIT_LOCKS,    0),
-    RLIMIT_ENTRY(RLIMIT_MEMLOCK,  0),
-
-    /* End-of-list marker */
-    RLIMIT_ENTRY(RLIMIT_NLIMITS,  0),
-#undef RLIMIT_ENTRY
-};
-
-int libxl__local_dm_preexec_restrict(libxl__gc *gc)
-{
-    int r;
-    unsigned i;
-
-    /* Unshare mount and IPC namespaces.  These are unused by QEMU. */
-    r = unshare(CLONE_NEWNS | CLONE_NEWIPC);
-    if (r) {
-        LOGE(ERROR, "libxl: unshare Mount and IPC namespace failed");
-        return ERROR_FAIL;
-    }
-
-    /* Set various "easy" rlimits */
-    for (i = 0; rlimits[i].resource != RLIMIT_NLIMITS; i++) {
-        struct rlimit rlim;
-
-        rlim.rlim_cur = rlim.rlim_max = rlimits[i].limit;
-
-        r = setrlimit(rlimits[i].resource, &rlim);
-        if (r < 0) {
-            LOGE(ERROR, "Setting rlimit %d to %llu failed\n",
-                                  rlimits[i].resource,
-                                  (unsigned long long)rlimits[i].limit);
-            return ERROR_FAIL;
-        }
-    }
-
-    return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_mem.c b/tools/libxl/libxl_mem.c
deleted file mode 100644 (file)
index e52a962..0000000
+++ /dev/null
@@ -1,651 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-/*
- * Set the maximum memory size of the domain in the hypervisor. There is no
- * change of the current memory size involved. The specified memory size can
- * even be above the configured maxmem size of the domain, but the related
- * Xenstore entry memory/static-max isn't modified!
- */
-int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb)
-{
-    GC_INIT(ctx);
-    char *mem, *endptr;
-    uint64_t memorykb, size;
-    char *dompath = libxl__xs_get_dompath(gc, domid);
-    int rc = 1;
-    libxl__flock *lock = NULL;
-    libxl_domain_config d_config;
-
-    libxl_domain_config_init(&d_config);
-
-    CTX_LOCK;
-
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out;
-    }
-
-    mem = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath));
-    if (!mem) {
-        LOGED(ERROR, domid, "Cannot get memory info from %s/memory/target",
-              dompath);
-        goto out;
-    }
-    memorykb = strtoull(mem, &endptr, 10);
-    if (*endptr != '\0') {
-        LOGED(ERROR, domid, "Invalid memory %s from %s/memory/target\n",
-              mem, dompath);
-        goto out;
-    }
-
-    if (max_memkb < memorykb) {
-        LOGED(ERROR, domid,
-              "memory_static_max must be greater than or or equal to memory_dynamic_max");
-        goto out;
-    }
-
-    rc = libxl__get_domain_configuration(gc, domid, &d_config);
-    if (rc < 0) {
-        LOGE(ERROR, "unable to retrieve domain configuration");
-        goto out;
-    }
-
-    rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
-    if (rc < 0) {
-        LOGE(ERROR, "Couldn't get arch extra constant memory size");
-        goto out;
-    }
-
-    rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + size);
-    if (rc != 0) {
-        LOGED(ERROR, domid,
-              "xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n",
-              domid, max_memkb + size, rc);
-        goto out;
-    }
-
-    rc = 0;
-out:
-    libxl_domain_config_dispose(&d_config);
-    if (lock) libxl__unlock_file(lock);
-    CTX_UNLOCK;
-    GC_FREE;
-    return rc;
-}
-
-static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
-                                        uint64_t *max_memkb)
-{
-    int rc;
-    libxl_dominfo info;
-    libxl_physinfo physinfo;
-    char *target = NULL, *staticmax = NULL, *endptr = NULL;
-    char *target_path = "/local/domain/0/memory/target";
-    char *max_path = "/local/domain/0/memory/static-max";
-    xs_transaction_t t;
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-
-    libxl_dominfo_init(&info);
-
-retry_transaction:
-    t = xs_transaction_start(ctx->xsh);
-
-    target = libxl__xs_read(gc, t, target_path);
-    staticmax = libxl__xs_read(gc, t, max_path);
-    if (target && staticmax) {
-        rc = 0;
-        goto out;
-    }
-
-    if (target) {
-        *target_memkb = strtoull(target, &endptr, 10);
-        if (*endptr != '\0') {
-            LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target,
-                 target_path);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    if (staticmax) {
-        *max_memkb = strtoull(staticmax, &endptr, 10);
-        if (*endptr != '\0') {
-            LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n",
-                 staticmax,
-                 max_path);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    libxl_dominfo_dispose(&info);
-    libxl_dominfo_init(&info);
-    rc = libxl_domain_info(ctx, &info, 0);
-    if (rc < 0)
-        goto out;
-
-    rc = libxl_get_physinfo(ctx, &physinfo);
-    if (rc < 0)
-        goto out;
-
-    if (target == NULL) {
-        libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
-        *target_memkb = info.current_memkb;
-    }
-    if (staticmax == NULL) {
-        libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
-        *max_memkb = info.max_memkb;
-    }
-
-    rc = 0;
-
-out:
-    if (!xs_transaction_end(ctx->xsh, t, 0)) {
-        if (errno == EAGAIN)
-            goto retry_transaction;
-        else
-            rc = ERROR_FAIL;
-    }
-
-    libxl_dominfo_dispose(&info);
-    return rc;
-}
-
-int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid,
-        int64_t target_memkb, int relative, int enforce)
-{
-    GC_INIT(ctx);
-    int rc, r, lrc, abort_transaction = 0;
-    uint64_t memorykb, size;
-    uint64_t videoram = 0;
-    uint64_t current_target_memkb = 0, new_target_memkb = 0;
-    uint64_t current_max_memkb = 0;
-    char *memmax, *endptr, *videoram_s = NULL, *target = NULL;
-    char *dompath = libxl__xs_get_dompath(gc, domid);
-    xc_domaininfo_t info;
-    libxl_dominfo ptr;
-    char *uuid;
-    xs_transaction_t t;
-    libxl__flock *lock;
-    libxl_domain_config d_config;
-
-    libxl_domain_config_init(&d_config);
-
-    CTX_LOCK;
-
-    lock = libxl__lock_domain_userdata(gc, domid);
-    if (!lock) {
-        rc = ERROR_LOCK_FAIL;
-        goto out_no_transaction;
-    }
-
-    rc = libxl__get_domain_configuration(gc, domid, &d_config);
-    if (rc < 0) {
-        LOGE(ERROR, "unable to retrieve domain configuration");
-        goto out_no_transaction;
-    }
-
-    rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
-    if (rc < 0) {
-        LOGE(ERROR, "Couldn't get arch extra constant memory size");
-        goto out_no_transaction;
-    }
-
-retry_transaction:
-    t = xs_transaction_start(ctx->xsh);
-
-    target = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/target", dompath));
-    if (!target && !domid) {
-        if (!xs_transaction_end(ctx->xsh, t, 1)) {
-            rc = ERROR_FAIL;
-            goto out_no_transaction;
-        }
-        lrc = libxl__fill_dom0_memory_info(gc, &current_target_memkb,
-                                           &current_max_memkb);
-        if (lrc < 0) { rc = ERROR_FAIL; goto out_no_transaction; }
-        goto retry_transaction;
-    } else if (!target) {
-        LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
-              dompath);
-        abort_transaction = 1;
-        rc = ERROR_FAIL;
-        goto out;
-    } else {
-        current_target_memkb = strtoull(target, &endptr, 10);
-        if (*endptr != '\0') {
-            LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
-                  target, dompath);
-            abort_transaction = 1;
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-    memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath));
-    if (!memmax) {
-        LOGED(ERROR, domid, "Cannot get memory info from %s/memory/static-max",
-              dompath);
-        abort_transaction = 1;
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    memorykb = strtoull(memmax, &endptr, 10);
-    if (*endptr != '\0') {
-        LOGED(ERROR, domid, "Invalid max memory %s from %s/memory/static-max\n",
-             memmax, dompath);
-        abort_transaction = 1;
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    videoram_s = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/videoram",
-                                                 dompath));
-    videoram = videoram_s ? atoi(videoram_s) : 0;
-
-    if (relative) {
-        if (target_memkb < 0 && llabs(target_memkb) > current_target_memkb)
-            new_target_memkb = 0;
-        else
-            new_target_memkb = current_target_memkb + target_memkb;
-    } else
-        new_target_memkb = target_memkb - videoram;
-    if (new_target_memkb > memorykb) {
-        LOGD(ERROR, domid,
-             "memory_dynamic_max must be less than or equal to"
-             " memory_static_max\n");
-        abort_transaction = 1;
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
-        LOGD(ERROR, domid,
-             "New target %"PRIu64" for dom0 is below the minimum threshold",
-             new_target_memkb);
-        abort_transaction = 1;
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    if (enforce) {
-        memorykb = new_target_memkb + videoram;
-        r = xc_domain_setmaxmem(ctx->xch, domid, memorykb + size);
-        if (r != 0) {
-            LOGED(ERROR, domid,
-                  "xc_domain_setmaxmem memkb=%"PRIu64" failed ""rc=%d\n",
-                  memorykb + size,
-                  r);
-            abort_transaction = 1;
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    if (d_config.c_info.type != LIBXL_DOMAIN_TYPE_PV) {
-        r = xc_domain_set_pod_target(ctx->xch, domid,
-                (new_target_memkb + size) / 4, NULL, NULL, NULL);
-        if (r != 0) {
-            LOGED(ERROR, domid,
-                  "xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n",
-                  (new_target_memkb + size) / 4,
-                  r);
-            abort_transaction = 1;
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
-                     "%"PRIu64, new_target_memkb);
-
-    r = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
-    if (r != 1 || info.domain != domid) {
-        abort_transaction = 1;
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl_dominfo_init(&ptr);
-    libxl__xcinfo2xlinfo(ctx, &info, &ptr);
-    uuid = libxl__uuid2string(gc, ptr.uuid);
-    libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
-                     "%"PRIu64, new_target_memkb / 1024);
-    libxl_dominfo_dispose(&ptr);
-
-    rc = 0;
-out:
-    if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
-        && !abort_transaction)
-        if (errno == EAGAIN)
-            goto retry_transaction;
-
-out_no_transaction:
-    libxl_domain_config_dispose(&d_config);
-    if (lock) libxl__unlock_file(lock);
-    CTX_UNLOCK;
-    GC_FREE;
-    return rc;
-}
-
-/* out_target_memkb and out_max_memkb can be NULL */
-int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
-                             uint64_t *out_target_memkb,
-                             uint64_t *out_max_memkb)
-{
-    int rc;
-    char *target = NULL, *static_max = NULL, *endptr = NULL;
-    char *dompath = libxl__xs_get_dompath(gc, domid);
-    uint64_t target_memkb, max_memkb;
-
-    target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
-                                                    dompath));
-    static_max = libxl__xs_read(gc, XBT_NULL,
-                    GCSPRINTF("%s/memory/static-max", dompath));
-
-    rc = ERROR_FAIL;
-    if ((!target || !static_max) && !domid) {
-        rc = libxl__fill_dom0_memory_info(gc, &target_memkb,
-                                          &max_memkb);
-        if (rc < 0)
-            goto out;
-    } else if (!target) {
-        LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
-              dompath);
-        goto out;
-    } else if (!static_max) {
-        LOGED(ERROR, domid,
-              "Cannot get target memory info from %s/memory/static-max",
-               dompath);
-        goto out;
-    } else {
-        target_memkb = strtoull(target, &endptr, 10);
-        if (*endptr != '\0') {
-            LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
-                  target, dompath);
-            goto out;
-        }
-        max_memkb = strtoull(static_max, &endptr, 10);
-        if (*endptr != '\0') {
-            LOGED(ERROR, domid,
-                  "Invalid memory target %s from %s/memory/static-max\n",
-                  static_max,
-                  dompath);
-            goto out;
-        }
-
-    }
-
-    if (out_target_memkb)
-        *out_target_memkb = target_memkb;
-
-    if (out_max_memkb)
-        *out_max_memkb = max_memkb;
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
-                               uint64_t val64, uint32_t *ptr32)
-{
-    GC_INIT(ctx);
-
-    if (rc)
-        goto out;
-
-    *ptr32 = val64;
-    if (*ptr32 == val64)
-        goto out;
-
-    LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
-    rc = ERROR_FAIL;
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
-                            uint64_t *out_target)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    rc = libxl__get_memory_target(gc, domid, out_target, NULL);
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_get_memory_target_0x040700(
-    libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
-{
-    uint64_t my_out_target;
-    int rc;
-
-    rc = libxl_get_memory_target(ctx, domid, &my_out_target);
-    return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
-}
-
-int libxl__domain_need_memory_calculate(libxl__gc *gc,
-                              libxl_domain_build_info *b_info,
-                              uint64_t *need_memkb)
-{
-    int rc;
-
-    *need_memkb = b_info->target_memkb;
-    *need_memkb += b_info->shadow_memkb + b_info->iommu_memkb;
-
-    switch (b_info->type) {
-    case LIBXL_DOMAIN_TYPE_PVH:
-    case LIBXL_DOMAIN_TYPE_HVM:
-        *need_memkb += LIBXL_HVM_EXTRA_MEMORY;
-        if (libxl_defbool_val(b_info->device_model_stubdomain)) {
-            *need_memkb += b_info->stubdomain_memkb;
-            *need_memkb += b_info->video_memkb;
-        }
-        break;
-    case LIBXL_DOMAIN_TYPE_PV:
-        *need_memkb += LIBXL_PV_EXTRA_MEMORY;
-        break;
-    default:
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    if (*need_memkb % (2 * 1024))
-        *need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024));
-    rc = 0;
-out:
-    return rc;
-}
-
-int libxl_domain_need_memory(libxl_ctx *ctx,
-                             libxl_domain_config *d_config,
-                             uint32_t domid_for_logging,
-                             uint64_t *need_memkb)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    ctx->libxl_domain_need_memory_called = 1;
-
-    rc = libxl__domain_config_setdefault(gc,
-                                         d_config,
-                                         domid_for_logging);
-    if (rc) goto out;
-
-    rc = libxl__domain_need_memory_calculate(gc,
-                                   &d_config->b_info,
-                                   need_memkb);
-    if (rc) goto out;
-
-    rc = 0;
- out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
-                                      const libxl_domain_build_info *b_info_in,
-                                      uint64_t *need_memkb)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    ctx->libxl_domain_need_memory_0x041200_called = 1;
-
-    libxl_domain_build_info b_info[1];
-    libxl_domain_build_info_init(b_info);
-    libxl_domain_build_info_copy(ctx, b_info, b_info_in);
-
-    rc = libxl__domain_build_info_setdefault(gc, b_info);
-    if (rc) goto out;
-
-    rc = libxl__domain_need_memory_calculate(gc,
-                                   b_info,
-                                   need_memkb);
-    if (rc) goto out;
-
-    rc = 0;
- out:
-    libxl_domain_build_info_dispose(b_info);
-    GC_FREE;
-    return rc;
-}
-
-int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
-                                      const libxl_domain_build_info *b_info_in,
-                                      uint32_t *need_memkb)
-{
-    uint64_t my_need_memkb;
-    int rc;
-
-    rc = libxl_domain_need_memory_0x041200(ctx, b_info_in, &my_need_memkb);
-    return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
-}
-
-int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
-{
-    int rc = 0;
-    libxl_physinfo info;
-    GC_INIT(ctx);
-
-    rc = libxl_get_physinfo(ctx, &info);
-    if (rc < 0)
-        goto out;
-
-    *memkb = (info.free_pages + info.scrub_pages) * 4;
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
-{
-    uint64_t my_memkb;
-    int rc;
-
-    rc = libxl_get_free_memory(ctx, &my_memkb);
-    return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
-}
-
-int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
-                               uint64_t memory_kb, int wait_secs)
-{
-    int rc = 0;
-    libxl_physinfo info;
-    GC_INIT(ctx);
-
-    while (wait_secs > 0) {
-        rc = libxl_get_physinfo(ctx, &info);
-        if (rc < 0)
-            goto out;
-        if (info.free_pages * 4 >= memory_kb) {
-            rc = 0;
-            goto out;
-        }
-        wait_secs--;
-        sleep(1);
-    }
-    rc = ERROR_NOMEM;
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
-{
-    int rc = 0;
-    uint64_t target_memkb = 0;
-    uint64_t current_memkb, prev_memkb;
-    libxl_dominfo info;
-
-    rc = libxl_get_memory_target(ctx, domid, &target_memkb);
-    if (rc < 0)
-        return rc;
-
-    libxl_dominfo_init(&info);
-    prev_memkb = UINT64_MAX;
-
-    do {
-        sleep(2);
-
-        libxl_dominfo_dispose(&info);
-        libxl_dominfo_init(&info);
-        rc = libxl_domain_info(ctx, &info, domid);
-        if (rc < 0)
-            goto out;
-
-        current_memkb = info.current_memkb + info.outstanding_memkb;
-
-        if (current_memkb > prev_memkb)
-        {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        else if (current_memkb == prev_memkb)
-            wait_secs -= 2;
-        /* if current_memkb < prev_memkb loop for free as progress has
-         * been made */
-
-        prev_memkb = current_memkb;
-    } while (wait_secs > 0 && current_memkb > target_memkb);
-
-    if (current_memkb <= target_memkb)
-        rc = 0;
-    else
-        rc = ERROR_FAIL;
-
-out:
-    libxl_dominfo_dispose(&info);
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_netbsd.c b/tools/libxl/libxl_netbsd.c
deleted file mode 100644 (file)
index e66a393..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2011
- * Author Roger Pau Monne <roger.pau@entel.upc.edu>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__try_phy_backend(mode_t st_mode)
-{
-    if (S_ISREG(st_mode) || S_ISBLK(st_mode))
-        return 1;
-
-    return 0;
-}
-
-char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
-{
-    /* TODO */
-    return NULL;
-}
-
-/* Hotplug scripts caller functions */
-static int libxl__hotplug(libxl__gc *gc, libxl__device *dev, char ***args,
-                          libxl__device_action action)
-{
-    char *be_path = libxl__device_backend_path(gc, dev);
-    char *script;
-    int nr = 0, rc = 0, arraysize = 4;
-
-    script = libxl__xs_read(gc, XBT_NULL,
-                            GCSPRINTF("%s/%s", be_path, "script"));
-    if (!script) {
-        LOGEVD(ERROR, errno, dev->domid,
-               "unable to read script from %s", be_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    GCNEW_ARRAY(*args, arraysize);
-    (*args)[nr++] = script;
-    (*args)[nr++] = be_path;
-    (*args)[nr++] = GCSPRINTF("%d", action == LIBXL__DEVICE_ACTION_ADD ?
-                                    XenbusStateInitWait : XenbusStateClosed);
-    (*args)[nr++] = NULL;
-    assert(nr == arraysize);
-
-out:
-    return rc;
-}
-
-int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
-                                   char ***args, char ***env,
-                                   libxl__device_action action,
-                                   int num_exec)
-{
-    int rc;
-
-    switch (dev->backend_kind) {
-    case LIBXL__DEVICE_KIND_VBD:
-        if (num_exec != 0) {
-            LOGD(DEBUG, dev->domid,
-                 "num_exec %d, not running hotplug scripts", num_exec);
-            rc = 0;
-            goto out;
-        }
-        rc = libxl__hotplug(gc, dev, args, action);
-        if (!rc) rc = 1;
-        break;
-    case LIBXL__DEVICE_KIND_VIF:
-        /*
-         * If domain has a stubdom we don't have to execute hotplug scripts
-         * for emulated interfaces
-         *
-         * NetBSD let QEMU call a script to plug emulated nic, so
-         * only test if num_exec == 0 in that case.
-         */
-        if ((num_exec != 0) ||
-            (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
-            LOGD(DEBUG, dev->domid,
-                 "num_exec %d, not running hotplug scripts", num_exec);
-            rc = 0;
-            goto out;
-        }
-        rc = libxl__hotplug(gc, dev, args, action);
-        if (!rc) rc = 1;
-        break;
-    default:
-        /* If no need to execute any hotplug scripts,
-         * call the callback manually
-         */
-        rc = 0;
-        break;
-    }
-
-out:
-    return rc;
-}
-
-libxl_device_model_version libxl__default_device_model(libxl__gc *gc)
-{
-    return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
-}
-
-int libxl__pci_numdevs(libxl__gc *gc)
-{
-    return ERROR_NI;
-}
-
-int libxl__pci_topology_init(libxl__gc *gc,
-                             physdev_pci_device_t *devs,
-                             int num_devs)
-{
-    return ERROR_NI;
-}
-
-int libxl__local_dm_preexec_restrict(libxl__gc *gc)
-{
-    return 0;
-}
diff --git a/tools/libxl/libxl_netbuffer.c b/tools/libxl/libxl_netbuffer.c
deleted file mode 100644 (file)
index 4b21914..0000000
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright (C) 2014
- * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#include <netlink/cache.h>
-#include <netlink/socket.h>
-#include <netlink/attr.h>
-#include <netlink/route/link.h>
-#include <netlink/route/route.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc/plug.h>
-
-typedef struct libxl__remus_device_nic {
-    int devid;
-
-    const char *vif;
-    const char *ifb;
-    struct rtnl_qdisc *qdisc;
-} libxl__remus_device_nic;
-
-int libxl__netbuffer_enabled(libxl__gc *gc)
-{
-    return 1;
-}
-
-int init_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
-    int rc, ret;
-    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-    libxl__remus_state *rs = cds->concrete_data;
-
-    STATE_AO_GC(cds->ao);
-
-    rs->nlsock = nl_socket_alloc();
-    if (!rs->nlsock) {
-        LOGD(ERROR, dss->domid, "cannot allocate nl socket");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    ret = nl_connect(rs->nlsock, NETLINK_ROUTE);
-    if (ret) {
-        LOGD(ERROR, dss->domid, "failed to open netlink socket: %s",
-             nl_geterror(ret));
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /* get list of all qdiscs installed on network devs. */
-    ret = rtnl_qdisc_alloc_cache(rs->nlsock, &rs->qdisc_cache);
-    if (ret) {
-        LOGD(ERROR, dss->domid, "failed to allocate qdisc cache: %s",
-             nl_geterror(ret));
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (dss->remus->netbufscript) {
-        rs->netbufscript = libxl__strdup(gc, dss->remus->netbufscript);
-    } else {
-        rs->netbufscript = GCSPRINTF("%s/remus-netbuf-setup",
-                                     libxl__xen_script_dir_path());
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
-    libxl__remus_state *rs = cds->concrete_data;
-
-    STATE_AO_GC(cds->ao);
-
-    /* free qdisc cache */
-    if (rs->qdisc_cache) {
-        nl_cache_clear(rs->qdisc_cache);
-        nl_cache_free(rs->qdisc_cache);
-        rs->qdisc_cache = NULL;
-    }
-
-    /* close & free nlsock */
-    if (rs->nlsock) {
-        nl_close(rs->nlsock);
-        nl_socket_free(rs->nlsock);
-        rs->nlsock = NULL;
-    }
-}
-
-/*----- setup() and teardown() -----*/
-
-/* helper functions */
-
-/*
- * If the device has a vifname, then use that instead of
- * the vifX.Y format.
- * it must ONLY be used for remus because if driver domains
- * were in use it would constitute a security vulnerability.
- */
-static const char *get_vifname(libxl__checkpoint_device *dev,
-                               const libxl_device_nic *nic)
-{
-    const char *vifname = NULL;
-    const char *path;
-    int rc;
-
-    STATE_AO_GC(dev->cds->ao);
-
-    /* Convenience aliases */
-    const uint32_t domid = dev->cds->domid;
-
-    path = GCSPRINTF("%s/vifname",
-                     libxl__domain_device_backend_path(gc, 0, domid,
-                     nic->devid, LIBXL__DEVICE_KIND_VIF));
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
-    if (!rc && !vifname) {
-        vifname = libxl__device_nic_devname(gc, domid,
-                                            nic->devid,
-                                            nic->nictype);
-    }
-
-    return vifname;
-}
-
-static void free_qdisc(libxl__remus_device_nic *remus_nic)
-{
-    if (remus_nic->qdisc == NULL)
-        return;
-
-    nl_object_put((struct nl_object *)(remus_nic->qdisc));
-    remus_nic->qdisc = NULL;
-}
-
-static int init_qdisc(libxl__checkpoint_devices_state *cds,
-                      libxl__remus_device_nic *remus_nic)
-{
-    int rc, ret, ifindex;
-    struct rtnl_link *ifb = NULL;
-    struct rtnl_qdisc *qdisc = NULL;
-    libxl__remus_state *rs = cds->concrete_data;
-
-    STATE_AO_GC(cds->ao);
-
-    /* Now that we have brought up REMUS_IFB device with plug qdisc for
-     * this vif, so we need to refill the qdisc cache.
-     */
-    ret = nl_cache_refill(rs->nlsock, rs->qdisc_cache);
-    if (ret) {
-        LOGD(ERROR, cds->domid,
-             "cannot refill qdisc cache: %s", nl_geterror(ret));
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /* get a handle to the REMUS_IFB interface */
-    ret = rtnl_link_get_kernel(rs->nlsock, 0, remus_nic->ifb, &ifb);
-    if (ret) {
-        LOGD(ERROR, cds->domid,
-             "cannot obtain handle for %s: %s", remus_nic->ifb,
-            nl_geterror(ret));
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    ifindex = rtnl_link_get_ifindex(ifb);
-    if (!ifindex) {
-        LOGD(ERROR, cds->domid,
-             "interface %s has no index", remus_nic->ifb);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /* Get a reference to the root qdisc installed on the REMUS_IFB, by
-     * querying the qdisc list we obtained earlier. The netbufscript
-     * sets up the plug qdisc as the root qdisc, so we don't have to
-     * search the entire qdisc tree on the REMUS_IFB dev.
-
-     * There is no need to explicitly free this qdisc as its just a
-     * reference from the qdisc cache we allocated earlier.
-     */
-    qdisc = rtnl_qdisc_get_by_parent(rs->qdisc_cache, ifindex, TC_H_ROOT);
-    if (qdisc) {
-        const char *tc_kind = rtnl_tc_get_kind(TC_CAST(qdisc));
-        /* Sanity check: Ensure that the root qdisc is a plug qdisc. */
-        if (!tc_kind || strcmp(tc_kind, "plug")) {
-            LOGD(ERROR, cds->domid,
-                 "plug qdisc is not installed on %s", remus_nic->ifb);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        remus_nic->qdisc = qdisc;
-    } else {
-        LOGD(ERROR, cds->domid,
-             "Cannot get qdisc handle from ifb %s", remus_nic->ifb);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    if (ifb)
-        rtnl_link_put(ifb);
-
-    if (rc && qdisc)
-        nl_object_put((struct nl_object *)qdisc);
-
-    return rc;
-}
-
-/* callbacks */
-
-static void netbuf_setup_script_cb(libxl__egc *egc,
-                                   libxl__async_exec_state *aes,
-                                   int rc, int status);
-static void netbuf_teardown_script_cb(libxl__egc *egc,
-                                      libxl__async_exec_state *aes,
-                                      int rc, int status);
-
-/*
- * the script needs the following env & args
- * $vifname
- * $XENBUS_PATH (/libxl/<domid>/remus/netbuf/<devid>/)
- * $REMUS_IFB (for teardown)
- * setup/teardown as command line arg.
- */
-static void setup_async_exec(libxl__checkpoint_device *dev, char *op)
-{
-    int arraysize, nr = 0;
-    char **env = NULL, **args = NULL;
-    libxl__remus_device_nic *remus_nic = dev->concrete_data;
-    libxl__checkpoint_devices_state *cds = dev->cds;
-    libxl__async_exec_state *aes = &dev->aodev.aes;
-    libxl__remus_state *rs = cds->concrete_data;
-
-    STATE_AO_GC(cds->ao);
-
-    /* Convenience aliases */
-    char *const script = libxl__strdup(gc, rs->netbufscript);
-    const uint32_t domid = cds->domid;
-    const int dev_id = remus_nic->devid;
-    const char *const vif = remus_nic->vif;
-    const char *const ifb = remus_nic->ifb;
-
-    arraysize = 7;
-    GCNEW_ARRAY(env, arraysize);
-    env[nr++] = "vifname";
-    env[nr++] = libxl__strdup(gc, vif);
-    env[nr++] = "XENBUS_PATH";
-    env[nr++] = GCSPRINTF("%s/remus/netbuf/%d",
-                          libxl__xs_libxl_path(gc, domid), dev_id);
-    if (!strcmp(op, "teardown") && ifb) {
-        env[nr++] = "REMUS_IFB";
-        env[nr++] = libxl__strdup(gc, ifb);
-    }
-    env[nr++] = NULL;
-    assert(nr <= arraysize);
-
-    arraysize = 3; nr = 0;
-    GCNEW_ARRAY(args, arraysize);
-    args[nr++] = script;
-    args[nr++] = op;
-    args[nr++] = NULL;
-    assert(nr == arraysize);
-
-    aes->ao = dev->cds->ao;
-    aes->what = GCSPRINTF("%s %s", args[0], args[1]);
-    aes->env = env;
-    aes->args = args;
-    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
-    aes->stdfds[0] = -1;
-    aes->stdfds[1] = -1;
-    aes->stdfds[2] = -1;
-
-    if (!strcmp(op, "teardown"))
-        aes->callback = netbuf_teardown_script_cb;
-    else
-        aes->callback = netbuf_setup_script_cb;
-}
-
-/* setup() and teardown() */
-
-static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    int rc;
-    libxl__remus_device_nic *remus_nic;
-    const libxl_device_nic *nic = dev->backend_dev;
-
-    STATE_AO_GC(dev->cds->ao);
-
-    /*
-     * thers's no subkind of nic devices, so nic ops is always matched
-     * with nic devices
-     */
-    dev->matched = true;
-
-    GCNEW(remus_nic);
-    dev->concrete_data = remus_nic;
-    remus_nic->devid = nic->devid;
-    remus_nic->vif = get_vifname(dev, nic);
-    if (!remus_nic->vif) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    setup_async_exec(dev, "setup");
-    rc = libxl__async_exec_start(&dev->aodev.aes);
-    if (rc)
-        goto out;
-
-    return;
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-/*
- * In return, the script writes the name of REMUS_IFB device (during setup)
- * to be used for output buffering into XENBUS_PATH/ifb
- */
-static void netbuf_setup_script_cb(libxl__egc *egc,
-                                   libxl__async_exec_state *aes,
-                                   int rc, int status)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
-    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
-    libxl__remus_device_nic *remus_nic = dev->concrete_data;
-    libxl__checkpoint_devices_state *cds = dev->cds;
-    libxl__remus_state *rs = cds->concrete_data;
-    const char *out_path_base, *hotplug_error = NULL;
-
-    STATE_AO_GC(cds->ao);
-
-    /* Convenience aliases */
-    const uint32_t domid = cds->domid;
-    const int devid = remus_nic->devid;
-    const char *const vif = remus_nic->vif;
-    const char **const ifb = &remus_nic->ifb;
-
-    if (status && !rc)
-        rc = ERROR_FAIL;
-    if (rc)
-        goto out;
-
-    /*
-     * we need to get ifb first because it's needed for teardown
-     */
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/remus/netbuf/%d/ifb",
-                                          libxl__xs_libxl_path(gc, domid),
-                                          devid),
-                                ifb);
-    if (rc)
-        goto out;
-
-    if (!(*ifb)) {
-        LOGD(ERROR, domid, "Cannot get ifb dev name for domain %u dev %s",
-             domid, vif);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    out_path_base = GCSPRINTF("%s/remus/netbuf/%d",
-                              libxl__xs_libxl_path(gc, domid), devid);
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/hotplug-error", out_path_base),
-                                &hotplug_error);
-    if (rc)
-        goto out;
-
-    if (hotplug_error) {
-        LOGD(ERROR, domid, "netbuf script %s setup failed for vif %s: %s",
-             rs->netbufscript, vif, hotplug_error);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (status) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    LOGD(DEBUG, domid, "%s will buffer packets from vif %s", *ifb, vif);
-    rc = init_qdisc(cds, remus_nic);
-
-out:
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-static void nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    int rc;
-    STATE_AO_GC(dev->cds->ao);
-
-    setup_async_exec(dev, "teardown");
-
-    rc = libxl__async_exec_start(&dev->aodev.aes);
-    if (rc)
-        goto out;
-
-    return;
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void netbuf_teardown_script_cb(libxl__egc *egc,
-                                      libxl__async_exec_state *aes,
-                                      int rc, int status)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
-    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
-    libxl__remus_device_nic *remus_nic = dev->concrete_data;
-
-    if (status && !rc)
-        rc = ERROR_FAIL;
-
-    free_qdisc(remus_nic);
-
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-/*----- checkpointing APIs -----*/
-
-/* The value of buffer_op, not the value passed to kernel */
-enum {
-    tc_buffer_start,
-    tc_buffer_release
-};
-
-/* API implementations */
-
-static int remus_netbuf_op(libxl__remus_device_nic *remus_nic,
-                           libxl__checkpoint_devices_state *cds,
-                           int buffer_op)
-{
-    int rc, ret;
-    libxl__remus_state *rs = cds->concrete_data;
-
-    STATE_AO_GC(cds->ao);
-
-    if (buffer_op == tc_buffer_start)
-        ret = rtnl_qdisc_plug_buffer(remus_nic->qdisc);
-    else
-        ret = rtnl_qdisc_plug_release_one(remus_nic->qdisc);
-
-    if (ret) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    ret = rtnl_qdisc_add(rs->nlsock, remus_nic->qdisc, NLM_F_REQUEST);
-    if (ret) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    if (rc)
-        LOGD(ERROR, cds-> domid, "Remus: cannot do netbuf op %s on %s:%s",
-             ((buffer_op == tc_buffer_start) ?
-             "start_new_epoch" : "release_prev_epoch"),
-             remus_nic->ifb, nl_geterror(ret));
-    return rc;
-}
-
-static void nic_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    int rc;
-    libxl__remus_device_nic *remus_nic = dev->concrete_data;
-
-    STATE_AO_GC(dev->cds->ao);
-
-    rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_start);
-
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void nic_commit(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    int rc;
-    libxl__remus_device_nic *remus_nic = dev->concrete_data;
-
-    STATE_AO_GC(dev->cds->ao);
-
-    rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_release);
-
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-const libxl__checkpoint_device_instance_ops remus_device_nic = {
-    .kind = LIBXL__DEVICE_KIND_VIF,
-    .setup = nic_setup,
-    .teardown = nic_teardown,
-    .postsuspend = nic_postsuspend,
-    .commit = nic_commit,
-};
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_nic.c b/tools/libxl/libxl_nic.c
deleted file mode 100644 (file)
index 0e5d120..0000000
+++ /dev/null
@@ -1,544 +0,0 @@
-/*
- * Copyright (C) 2016      SUSE Linux GmbH
- * Author Juergen Gross <jgross@suse.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
-                            const char *mac, libxl_device_nic *nic)
-{
-    GC_INIT(ctx);
-    libxl_device_nic *nics;
-    int nb, rc, i;
-    libxl_mac mac_n;
-
-    rc = libxl__parse_mac(mac, mac_n);
-    if (rc)
-        return rc;
-
-    nics = libxl__device_list(gc, &libxl__nic_devtype, domid, &nb);
-    if (!nics)
-        return ERROR_FAIL;
-
-    memset(nic, 0, sizeof (libxl_device_nic));
-
-    rc = ERROR_INVAL;
-    for (i = 0; i < nb; ++i) {
-        if (!libxl__compare_macs(&mac_n, &nics[i].mac)) {
-            *nic = nics[i];
-            rc = 0;
-            i++; /* Do not dispose this NIC on exit path */
-            break;
-        }
-        libxl_device_nic_dispose(&nics[i]);
-    }
-
-    for (; i<nb; i++)
-        libxl_device_nic_dispose(&nics[i]);
-
-    free(nics);
-    return rc;
-}
-
-#define LIBXL_DEVICE_NIC_MTU_DEFAULT 1500
-
-static int libxl__device_nic_setdefault(libxl__gc *gc, uint32_t domid,
-                                        libxl_device_nic *nic, bool hotplug)
-{
-    int rc;
-
-    if (!nic->mtu)
-        nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
-    if (!nic->model) {
-        nic->model = strdup("rtl8139");
-        if (!nic->model) return ERROR_NOMEM;
-    }
-    if (libxl__mac_is_default(&nic->mac)) {
-        const uint8_t *r;
-        libxl_uuid uuid;
-
-        libxl_uuid_generate(&uuid);
-        r = libxl_uuid_bytearray(&uuid);
-
-        nic->mac[0] = 0x00;
-        nic->mac[1] = 0x16;
-        nic->mac[2] = 0x3e;
-        nic->mac[3] = r[0] & 0x7f;
-        nic->mac[4] = r[1];
-        nic->mac[5] = r[2];
-    }
-    if (!nic->bridge) {
-        nic->bridge = strdup("xenbr0");
-        if (!nic->bridge) return ERROR_NOMEM;
-    }
-    if ( !nic->script && asprintf(&nic->script, "%s/vif-bridge",
-                                  libxl__xen_script_dir_path()) < 0 )
-        return ERROR_FAIL;
-
-    rc = libxl__resolve_domid(gc, nic->backend_domname, &nic->backend_domid);
-    if (rc < 0) return rc;
-
-    switch (libxl__domain_type(gc, domid)) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        if (!nic->nictype) {
-            if (hotplug)
-                nic->nictype = LIBXL_NIC_TYPE_VIF;
-            else
-                nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
-        }
-        break;
-    case LIBXL_DOMAIN_TYPE_PVH:
-    case LIBXL_DOMAIN_TYPE_PV:
-        if (nic->nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
-            LOGD(ERROR, domid,
-            "trying to create PV or PVH guest with an emulated interface");
-            return ERROR_INVAL;
-        }
-        nic->nictype = LIBXL_NIC_TYPE_VIF;
-        break;
-    case LIBXL_DOMAIN_TYPE_INVALID:
-        return ERROR_FAIL;
-    default:
-        abort();
-    }
-
-    return rc;
-}
-
-static void libxl__update_config_nic(libxl__gc *gc, libxl_device_nic *dst,
-                                     const libxl_device_nic *src)
-{
-    dst->devid = src->devid;
-    dst->nictype = src->nictype;
-    libxl_mac_copy(CTX, &dst->mac, &src->mac);
-}
-
-static int libxl__set_xenstore_nic(libxl__gc *gc, uint32_t domid,
-                                   libxl_device_nic *nic,
-                                   flexarray_t *back, flexarray_t *front,
-                                   flexarray_t *ro_front)
-{
-    flexarray_grow(back, 2);
-
-    if (nic->script)
-        flexarray_append_pair(back, "script",
-                              libxl__abs_path(gc, nic->script,
-                                              libxl__xen_script_dir_path()));
-
-    if (nic->ifname) {
-        flexarray_append(back, "vifname");
-        flexarray_append(back, nic->ifname);
-    }
-
-    if (nic->coloft_forwarddev) {
-        flexarray_append(back, "forwarddev");
-        flexarray_append(back, nic->coloft_forwarddev);
-    }
-
-#define MAYBE_ADD_COLO_ARGS(arg) ({                                       \
-    if (nic->colo_##arg) {                                                \
-        flexarray_append(back, "colo_"#arg);                              \
-        flexarray_append(back, nic->colo_##arg);                          \
-    }                                                                     \
-})
-
-    MAYBE_ADD_COLO_ARGS(sock_mirror_id);
-    MAYBE_ADD_COLO_ARGS(sock_mirror_ip);
-    MAYBE_ADD_COLO_ARGS(sock_mirror_port);
-    MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_id);
-    MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_ip);
-    MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_port);
-    MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_id);
-    MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_ip);
-    MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_port);
-    MAYBE_ADD_COLO_ARGS(sock_compare_notify_id);
-    MAYBE_ADD_COLO_ARGS(sock_compare_notify_ip);
-    MAYBE_ADD_COLO_ARGS(sock_compare_notify_port);
-    MAYBE_ADD_COLO_ARGS(sock_redirector0_id);
-    MAYBE_ADD_COLO_ARGS(sock_redirector0_ip);
-    MAYBE_ADD_COLO_ARGS(sock_redirector0_port);
-    MAYBE_ADD_COLO_ARGS(sock_redirector1_id);
-    MAYBE_ADD_COLO_ARGS(sock_redirector1_ip);
-    MAYBE_ADD_COLO_ARGS(sock_redirector1_port);
-    MAYBE_ADD_COLO_ARGS(sock_redirector2_id);
-    MAYBE_ADD_COLO_ARGS(sock_redirector2_ip);
-    MAYBE_ADD_COLO_ARGS(sock_redirector2_port);
-    MAYBE_ADD_COLO_ARGS(filter_mirror_queue);
-    MAYBE_ADD_COLO_ARGS(filter_mirror_outdev);
-    MAYBE_ADD_COLO_ARGS(filter_redirector0_queue);
-    MAYBE_ADD_COLO_ARGS(filter_redirector0_indev);
-    MAYBE_ADD_COLO_ARGS(filter_redirector0_outdev);
-    MAYBE_ADD_COLO_ARGS(filter_redirector1_queue);
-    MAYBE_ADD_COLO_ARGS(filter_redirector1_indev);
-    MAYBE_ADD_COLO_ARGS(filter_redirector1_outdev);
-    MAYBE_ADD_COLO_ARGS(compare_pri_in);
-    MAYBE_ADD_COLO_ARGS(compare_sec_in);
-    MAYBE_ADD_COLO_ARGS(compare_out);
-    MAYBE_ADD_COLO_ARGS(compare_notify_dev);
-
-    MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_id);
-    MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_ip);
-    MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_port);
-    MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_id);
-    MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_ip);
-    MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_port);
-    MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_queue);
-    MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_indev);
-    MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_outdev);
-    MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_queue);
-    MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_indev);
-    MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_outdev);
-    MAYBE_ADD_COLO_ARGS(filter_sec_rewriter0_queue);
-    MAYBE_ADD_COLO_ARGS(checkpoint_host);
-    MAYBE_ADD_COLO_ARGS(checkpoint_port);
-
-#undef MAYBE_ADD_COLO_ARGS
-
-    flexarray_append(back, "mac");
-    flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
-    if (nic->ip) {
-        flexarray_append(back, "ip");
-        flexarray_append(back, libxl__strdup(gc, nic->ip));
-    }
-    if (nic->gatewaydev) {
-        flexarray_append(back, "gatewaydev");
-        flexarray_append(back, libxl__strdup(gc, nic->gatewaydev));
-    }
-
-    if (nic->rate_interval_usecs > 0) {
-        flexarray_append(back, "rate");
-        flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"",
-                            nic->rate_bytes_per_interval,
-                            nic->rate_interval_usecs));
-    }
-
-    if (nic->mtu != LIBXL_DEVICE_NIC_MTU_DEFAULT) {
-        flexarray_append(back, "mtu");
-        flexarray_append(back, GCSPRINTF("%u", nic->mtu));
-    }
-    
-    flexarray_append(back, "bridge");
-    flexarray_append(back, libxl__strdup(gc, nic->bridge));
-    flexarray_append(back, "handle");
-    flexarray_append(back, GCSPRINTF("%d", nic->devid));
-    flexarray_append(back, "type");
-    flexarray_append(back, libxl__strdup(gc,
-                                     libxl_nic_type_to_string(nic->nictype)));
-
-    flexarray_append(front, "handle");
-    flexarray_append(front, GCSPRINTF("%d", nic->devid));
-    flexarray_append(front, "mac");
-    flexarray_append(front, GCSPRINTF(
-                                    LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
-
-    flexarray_append(ro_front, "mtu");
-    flexarray_append(ro_front, GCSPRINTF("%u", nic->mtu));
-
-    return 0;
-}
-
-static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
-                                  libxl_device_nic *nic,
-                                  libxl__ao_device *aodev)
-{
-    libxl__device_add_async(egc, domid, &libxl__nic_devtype, nic, aodev);
-}
-
-static int libxl__nic_from_xenstore(libxl__gc *gc, const char *libxl_path,
-                                    libxl_devid devid, libxl_device_nic *nic)
-{
-    const char *tmp;
-    int rc;
-
-    libxl_device_nic_init(nic);
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/handle", libxl_path), &tmp);
-    if (rc) goto out;
-    if (tmp)
-        nic->devid = atoi(tmp);
-    else
-        nic->devid = 0;
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/backend", libxl_path), &tmp);
-    if (rc) goto out;
-
-    if (!tmp) {
-        LOG(ERROR, "nic %s does not exist (no backend path)", libxl_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = libxl__backendpath_parse_domid(gc, tmp, &nic->backend_domid);
-    if (rc) goto out;
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/mtu", libxl_path), &tmp);
-    if (rc) goto out;
-    if (tmp) {
-        char *endptr;
-
-        nic->mtu = strtol(tmp, &endptr, 10);
-        if (*endptr != '\0') {
-            rc = ERROR_INVAL;
-            goto out;
-        }
-    } else {
-        nic->mtu = LIBXL_DEVICE_NIC_MTU_DEFAULT;
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/mac", libxl_path), &tmp);
-    if (rc) goto out;
-    if (tmp) {
-        rc = libxl__parse_mac(tmp, nic->mac);
-        if (rc) goto out;
-    } else {
-        memset(nic->mac, 0, sizeof(nic->mac));
-    }
-
-    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
-                                GCSPRINTF("%s/ip", libxl_path),
-                                (const char **)(&nic->ip));
-    if (rc) goto out;
-    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
-                                GCSPRINTF("%s/bridge", libxl_path),
-                                (const char **)(&nic->bridge));
-    if (rc) goto out;
-    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
-                                GCSPRINTF("%s/script", libxl_path),
-                                (const char **)(&nic->script));
-    if (rc) goto out;
-    rc = libxl__xs_read_checked(NOGC, XBT_NULL,
-                                GCSPRINTF("%s/forwarddev", libxl_path),
-                                (const char **)(&nic->coloft_forwarddev));
-    if (rc) goto out;
-
-#define CHECK_COLO_ARGS(arg) ({                                           \
-    rc = libxl__xs_read_checked(NOGC, XBT_NULL,                           \
-                                GCSPRINTF("%s/colo_"#arg, libxl_path),    \
-                                (const char **)(&nic->colo_##arg));       \
-    if (rc) goto out;                                                     \
-})
-
-    CHECK_COLO_ARGS(sock_mirror_id);
-    CHECK_COLO_ARGS(sock_mirror_ip);
-    CHECK_COLO_ARGS(sock_mirror_port);
-    CHECK_COLO_ARGS(sock_compare_pri_in_id);
-    CHECK_COLO_ARGS(sock_compare_pri_in_ip);
-    CHECK_COLO_ARGS(sock_compare_pri_in_port);
-    CHECK_COLO_ARGS(sock_compare_sec_in_id);
-    CHECK_COLO_ARGS(sock_compare_sec_in_ip);
-    CHECK_COLO_ARGS(sock_compare_sec_in_port);
-    CHECK_COLO_ARGS(sock_compare_notify_id);
-    CHECK_COLO_ARGS(sock_compare_notify_ip);
-    CHECK_COLO_ARGS(sock_compare_notify_port);
-    CHECK_COLO_ARGS(sock_redirector0_id);
-    CHECK_COLO_ARGS(sock_redirector0_ip);
-    CHECK_COLO_ARGS(sock_redirector0_port);
-    CHECK_COLO_ARGS(sock_redirector1_id);
-    CHECK_COLO_ARGS(sock_redirector1_ip);
-    CHECK_COLO_ARGS(sock_redirector1_port);
-    CHECK_COLO_ARGS(sock_redirector2_id);
-    CHECK_COLO_ARGS(sock_redirector2_ip);
-    CHECK_COLO_ARGS(sock_redirector2_port);
-    CHECK_COLO_ARGS(filter_mirror_queue);
-    CHECK_COLO_ARGS(filter_mirror_outdev);
-    CHECK_COLO_ARGS(filter_redirector0_queue);
-    CHECK_COLO_ARGS(filter_redirector0_indev);
-    CHECK_COLO_ARGS(filter_redirector0_outdev);
-    CHECK_COLO_ARGS(filter_redirector1_queue);
-    CHECK_COLO_ARGS(filter_redirector1_indev);
-    CHECK_COLO_ARGS(filter_redirector1_outdev);
-    CHECK_COLO_ARGS(compare_pri_in);
-    CHECK_COLO_ARGS(compare_sec_in);
-    CHECK_COLO_ARGS(compare_out);
-    CHECK_COLO_ARGS(compare_notify_dev);
-    CHECK_COLO_ARGS(sock_sec_redirector0_id);
-    CHECK_COLO_ARGS(sock_sec_redirector0_ip);
-    CHECK_COLO_ARGS(sock_sec_redirector0_port);
-    CHECK_COLO_ARGS(sock_sec_redirector1_id);
-    CHECK_COLO_ARGS(sock_sec_redirector1_ip);
-    CHECK_COLO_ARGS(sock_sec_redirector1_port);
-    CHECK_COLO_ARGS(filter_sec_redirector0_queue);
-    CHECK_COLO_ARGS(filter_sec_redirector0_indev);
-    CHECK_COLO_ARGS(filter_sec_redirector0_outdev);
-    CHECK_COLO_ARGS(filter_sec_redirector1_queue);
-    CHECK_COLO_ARGS(filter_sec_redirector1_indev);
-    CHECK_COLO_ARGS(filter_sec_redirector1_outdev);
-    CHECK_COLO_ARGS(filter_sec_rewriter0_queue);
-    CHECK_COLO_ARGS(checkpoint_host);
-    CHECK_COLO_ARGS(checkpoint_port);
-
-#undef CHECK_COLO_ARGS
-
-    /* vif_ioemu nics use the same xenstore entries as vif interfaces */
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/type", libxl_path), &tmp);
-    if (rc) goto out;
-    if (tmp) {
-        rc = libxl_nic_type_from_string(tmp, &nic->nictype);
-        if (rc) goto out;
-    } else {
-        nic->nictype = LIBXL_NIC_TYPE_VIF;
-    }
-    nic->model = NULL; /* XXX Only for TYPE_IOEMU */
-    nic->ifname = NULL; /* XXX Only for TYPE_IOEMU */
-
-    rc = 0;
- out:
-    return rc;
-}
-
-libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num)
-{
-    libxl_device_nic *r;
-
-    GC_INIT(ctx);
-
-    r = libxl__device_list(gc, &libxl__nic_devtype, domid, num);
-
-    GC_FREE;
-
-    return r;
-}
-
-void libxl_device_nic_list_free(libxl_device_nic* list, int num)
-{
-    libxl__device_list_free(&libxl__nic_devtype, list, num);
-}
-
-int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
-                              const libxl_device_nic *nic,
-                              libxl_nicinfo *nicinfo)
-{
-    GC_INIT(ctx);
-    char *nicpath, *libxl_path;
-    char *val;
-    int rc;
-
-    nicinfo->devid = nic->devid;
-
-    nicpath = libxl__domain_device_frontend_path(gc, domid, nicinfo->devid,
-                                                 LIBXL__DEVICE_KIND_VIF);
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, nicinfo->devid,
-                                                 LIBXL__DEVICE_KIND_VIF);
-    nicinfo->backend = xs_read(ctx->xsh, XBT_NULL,
-                                GCSPRINTF("%s/backend", libxl_path), NULL);
-    if (!nicinfo->backend) {
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    rc = libxl__backendpath_parse_domid(gc, nicinfo->backend,
-                                        &nicinfo->backend_id);
-    if (rc) goto out;
-
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath));
-    nicinfo->state = val ? strtoul(val, NULL, 10) : -1;
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath));
-    nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath));
-    nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1;
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath));
-    nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1;
-    nicinfo->frontend = libxl__strdup(NOGC, nicpath);
-    nicinfo->frontend_id = domid;
-
-    rc = 0;
- out:
-    GC_FREE;
-    return rc;
-}
-
-const char *libxl__device_nic_devname(libxl__gc *gc,
-                                      uint32_t domid,
-                                      uint32_t devid,
-                                      libxl_nic_type type)
-{
-    switch (type) {
-    case LIBXL_NIC_TYPE_VIF:
-        return GCSPRINTF(NETBACK_NIC_NAME, domid, devid);
-    case LIBXL_NIC_TYPE_VIF_IOEMU:
-        return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid);
-    default:
-        abort();
-    }
-}
-
-static int libxl_device_nic_compare(const libxl_device_nic *d1,
-                                    const libxl_device_nic *d2)
-{
-    return COMPARE_DEVID(d1, d2);
-}
-
-static void libxl_device_nic_update_config(libxl__gc *gc, void *d, void *s)
-{
-    libxl__update_config_nic(gc, d, s);
-}
-
-int libxl__device_nic_set_devids(libxl__gc *gc, libxl_domain_config *d_config,
-                                 uint32_t domid)
-{
-    int ret = 0;
-    int i;
-    size_t last_devid = -1;
-
-    for (i = 0; i < d_config->num_nics; i++) {
-        /* We have to init the nic here, because we still haven't
-         * called libxl_device_nic_add when domcreate_launch_dm gets called,
-         * but qemu needs the nic information to be complete.
-         */
-        ret = libxl__device_nic_setdefault(gc, domid, &d_config->nics[i],
-                                           false);
-        if (ret) {
-            LOGD(ERROR, domid, "Unable to set nic defaults for nic %d", i);
-            goto out;
-        }
-
-        if (d_config->nics[i].devid > last_devid)
-            last_devid = d_config->nics[i].devid;
-    }
-    for (i = 0; i < d_config->num_nics; i++) {
-        if (d_config->nics[i].devid < 0)
-            d_config->nics[i].devid = ++last_devid;
-    }
-
-out:
-    return ret;
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(nic)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(nic)
-
-LIBXL_DEFINE_DEVID_TO_DEVICE(nic)
-LIBXL_DEFINE_DEVICE_ADD(nic)
-LIBXL_DEFINE_DEVICES_ADD(nic)
-LIBXL_DEFINE_DEVICE_REMOVE(nic)
-
-DEFINE_DEVICE_TYPE_STRUCT(nic, VIF,
-    .update_config = libxl_device_nic_update_config,
-    .from_xenstore = (device_from_xenstore_fn_t)libxl__nic_from_xenstore,
-    .set_xenstore_config = (device_set_xenstore_config_fn_t)
-                           libxl__set_xenstore_nic,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_no_colo.c b/tools/libxl/libxl_no_colo.c
deleted file mode 100644 (file)
index 2e1315c..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016
- * Author Wei Liu <wei.liu2@citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-void libxl__colo_restore_setup(libxl__egc *egc,
-                               libxl__colo_restore_state *crs)
-{
-    STATE_AO_GC(crs->ao);
-
-    LOGD(ERROR, crs->domid, "COLO is not supported");
-
-    crs->callback(egc, crs, ERROR_FAIL);
-}
-
-void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void,
-                                  int ret, int retval, int errnoval)
-{
-    /* Shouldn't be here because setup already failed */
-    abort();
-}
-
-void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css);
-    STATE_AO_GC(dss->ao);
-
-    LOGD(ERROR, dss->domid, "COLO is not supported");
-
-    dss->callback(egc, dss, ERROR_FAIL);
-}
-
-void libxl__colo_save_teardown(libxl__egc *egc,
-                               libxl__colo_save_state *css,
-                               int rc)
-{
-    /* Shouldn't be here because setup already failed */
-    abort();
-}
-
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_no_convert_callout.c b/tools/libxl/libxl_no_convert_callout.c
deleted file mode 100644 (file)
index 6ba4d92..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
-{
-    libxl__ev_child_init(&chs->child);
-}
-
-int libxl__convert_legacy_stream(libxl__egc *egc,
-                                 libxl__conversion_helper_state *chs)
-{
-    return ERROR_FAIL;
-}
-
-void libxl__conversion_helper_abort(libxl__egc *egc,
-                                    libxl__conversion_helper_state *chs,
-                                    int rc)
-{
-    /* no op */
-}
diff --git a/tools/libxl/libxl_nocpuid.c b/tools/libxl/libxl_nocpuid.c
deleted file mode 100644 (file)
index f473365..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
-{
-    return 1;
-}
-
-void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
-{
-}
-
-int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str)
-{
-    return 0;
-}
-
-int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid,
-                                  const char* str)
-{
-    return 0;
-}
-
-void libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
-                         libxl_domain_build_info *info)
-{
-}
-
-yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
-                                libxl_cpuid_policy_list *pcpuid)
-{
-    return 0;
-}
-
-int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
-                                        const libxl__json_object *o,
-                                        libxl_cpuid_policy_list *p)
-{
-    return 0;
-}
-
-void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
-                                  libxl_cpuid_policy_list *dst,
-                                  const libxl_cpuid_policy_list *src)
-{
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_nonetbuffer.c b/tools/libxl/libxl_nonetbuffer.c
deleted file mode 100644 (file)
index 4b68152..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014
- * Author Shriram Rajagopalan <rshriram@cs.ubc.ca>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-int libxl__netbuffer_enabled(libxl__gc *gc)
-{
-    return 0;
-}
-
-int init_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
-    return 0;
-}
-
-void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds)
-{
-    return;
-}
-
-static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    STATE_AO_GC(dev->cds->ao);
-
-    dev->aodev.rc = ERROR_FAIL;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-const libxl__checkpoint_device_instance_ops remus_device_nic = {
-    .kind = LIBXL__DEVICE_KIND_VIF,
-    .setup = nic_setup,
-};
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_numa.c b/tools/libxl/libxl_numa.c
deleted file mode 100644 (file)
index a8a75f8..0000000
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * Copyright (C) 2012      Citrix Ltd.
- * Author Dario Faggioli <dario.faggioli@citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <glob.h>
-
-#include "libxl_internal.h"
-
-/*
- * What follows are helpers for generating all the k-combinations
- * without repetitions of a set S with n elements in it. Formally
- * speaking, they are subsets of k distinct elements of S and, if
- * S is n elements big, the number of k-combinations is equal to the
- * binomial coefficient C(n k)=n!/(k! * (n - k)!).
- *
- * The various subset are generated one after the other by calling
- * comb_init() first, and, after that, comb_next()
- * C(n k)-1 times. An iterator is used to store the current status
- * of the whole generation operation (i.e., basically, the last
- * combination that has been generated). As soon as all combinations
- * have been generated, comb_next() will start returning 0 instead of
- * 1. The same instance of the iterator and the same values for
- * n and k _must_ be used for each call (if that doesn't happen, the
- * result is unspecified).
- *
- * The algorithm is a well known one (see, for example, D. Knuth's "The
- * Art of Computer Programming - Volume 4, Fascicle 3" and it produces
- * the combinations in such a way that they (well, more precisely, their
- * indexes it the array/map representing the set) come with lexicographic
- * ordering.
- *
- * For example, with n = 5 and k = 3, calling comb_init()
- * will generate { 0, 1, 2 }, while subsequent valid calls to
- * comb_next() will produce the following:
- * { { 0, 1, 3 }, { 0, 1, 4 },
- *   { 0, 2, 3 }, { 0, 2, 4 }, { 0, 3, 4 },
- *   { 1, 2, 3 }, { 1, 2, 4 }, { 1, 3, 4 },
- *   { 2, 3, 4 } }
- *
- * This is used by the automatic NUMA placement logic below.
- */
-typedef int* comb_iter_t;
-
-static int comb_init(libxl__gc *gc, comb_iter_t *it, int n, int k)
-{
-    comb_iter_t new_iter;
-    int i;
-
-    if (n < k)
-        return 0;
-
-    /* First set is always { 0, 1, 2, ..., k-1 } */
-    GCNEW_ARRAY(new_iter, k);
-    for (i = 0; i < k; i++)
-        new_iter[i] = i;
-
-    *it = new_iter;
-    return 1;
-}
-
-static int comb_next(comb_iter_t it, int n, int k)
-{
-    int i;
-
-    /*
-     * The idea here is to find the leftmost element from where
-     * we should start incrementing the indexes of the iterator.
-     * This means looking for the highest index that can be increased
-     * while still producing value smaller than n-1. In the example
-     * above, when dealing with { 0, 1, 4 }, such an element is the
-     * second one, as the third is already equal to 4 (which actually
-     * is n-1).
-     * Once we found from where to start, we increment that element
-     * and override the right-hand rest of the iterator with its
-     * successors, thus achieving lexicographic ordering.
-     *
-     * Regarding the termination of the generation process, when we
-     * manage in bringing n-k at the very first position of the iterator,
-     * we know that is the last valid combination ( { 2, 3, 4 }, with
-     * n - k = 5 - 2 = 2, in the example above), and thus we start
-     * returning 0 as soon as we cross that border.
-     */
-    for (i = k - 1; it[i] == n - k + i; i--) {
-        if (i <= 0)
-            return 0;
-    }
-    for (it[i]++, i++; i < k; i++)
-        it[i] = it[i - 1] + 1;
-    return 1;
-}
-
-/* NUMA automatic placement (see libxl_internal.h for details) */
-
-/*
- * This function turns a k-combination iterator into a node map,
- * given another map, telling us which nodes should be considered.
- *
- * This means the bits that are set in suitable_nodemap and that
- * corresponds to the indexes of the given combination are the ones
- * that will be set in nodemap.
- *
- * For example, given a fully set suitable_nodemap, if the iterator
- * represents the combination { 0, 2, 4}, nodmeap will have bits #0,
- * #2 and #4 set.
- * On the other hand, if, say,  suitable_nodemap=01011011, the same
- * iterator will cause bits #1, #4 and #7 of nodemap to be set.
- */
-static void comb_get_nodemap(comb_iter_t it, libxl_bitmap *suitable_nodemap,
-                             libxl_bitmap *nodemap, int k)
-{
-    int i, m = 0, n = 0;
-
-    libxl_bitmap_set_none(nodemap);
-    libxl_for_each_set_bit(i, *suitable_nodemap) {
-        /* Check wether the n-th set bit of suitable_nodemap
-         * matches with the m-th element of the iterator (and,
-         * only if it does, advance to the next one) */
-        if (m < k && n == it[m]) {
-            libxl_bitmap_set(nodemap, i);
-            m++;
-        }
-        n++;
-    }
-}
-
-/* Retrieve the number of cpus that the nodes that are part of the nodemap
- * span and are also set in suitable_cpumap. */
-static int nodemap_to_nr_cpus(libxl_cputopology *tinfo, int nr_cpus,
-                              const libxl_bitmap *suitable_cpumap,
-                              const libxl_bitmap *nodemap)
-{
-    int i, nodes_cpus = 0;
-
-    for (i = 0; i < nr_cpus; i++) {
-        if (libxl_bitmap_test(suitable_cpumap, i) &&
-            libxl_bitmap_test(nodemap, tinfo[i].node))
-            nodes_cpus++;
-    }
-    return nodes_cpus;
-}
-
-/* Retrieve the amount of free memory within the nodemap */
-static uint32_t nodemap_to_free_memkb(libxl_numainfo *ninfo,
-                                      libxl_bitmap *nodemap)
-{
-    uint32_t free_memkb = 0;
-    int i;
-
-    libxl_for_each_set_bit(i, *nodemap)
-        free_memkb += ninfo[i].free / 1024;
-
-    return free_memkb;
-}
-
-/* Retrieve the number of vcpus able to run on the nodes in nodemap */
-static int nodemap_to_nr_vcpus(libxl__gc *gc, int vcpus_on_node[],
-                               const libxl_bitmap *nodemap)
-{
-    int i, nr_vcpus = 0;
-
-    libxl_for_each_set_bit(i, *nodemap)
-        nr_vcpus += vcpus_on_node[i];
-
-    return nr_vcpus;
-}
-
-/* Number of vcpus able to run on the cpus of the various nodes
- * (reported by filling the array vcpus_on_node[]). */
-static int nr_vcpus_on_nodes(libxl__gc *gc, libxl_cputopology *tinfo,
-                             size_t tinfo_elements,
-                             const libxl_bitmap *suitable_cpumap,
-                             int vcpus_on_node[])
-{
-    libxl_dominfo *dinfo = NULL;
-    libxl_bitmap dom_nodemap, nodes_counted;
-    int nr_doms, nr_cpus;
-    int i, j, k;
-
-    dinfo = libxl_list_domain(CTX, &nr_doms);
-    if (dinfo == NULL)
-        return ERROR_FAIL;
-
-    if (libxl_node_bitmap_alloc(CTX, &nodes_counted, 0) < 0) {
-        libxl_dominfo_list_free(dinfo, nr_doms);
-        return ERROR_FAIL;
-    }
-
-    if (libxl_node_bitmap_alloc(CTX, &dom_nodemap, 0) < 0) {
-        libxl_bitmap_dispose(&nodes_counted);
-        libxl_dominfo_list_free(dinfo, nr_doms);
-        return ERROR_FAIL;
-    }
-
-    for (i = 0; i < nr_doms; i++) {
-        libxl_vcpuinfo *vinfo = NULL;
-        int nr_dom_vcpus = 0;
-        libxl_cpupoolinfo cpupool_info;
-        int cpupool;
-
-        libxl_cpupoolinfo_init(&cpupool_info);
-
-        cpupool = libxl__domain_cpupool(gc, dinfo[i].domid);
-        if (cpupool < 0)
-            goto next;
-        if (libxl_cpupool_info(CTX, &cpupool_info, cpupool))
-            goto next;
-
-        vinfo = libxl_list_vcpu(CTX, dinfo[i].domid, &nr_dom_vcpus, &nr_cpus);
-        if (vinfo == NULL)
-            goto next;
-
-        /* Retrieve the domain's node-affinity map */
-        libxl_domain_get_nodeaffinity(CTX, dinfo[i].domid, &dom_nodemap);
-
-        for (j = 0; j < nr_dom_vcpus; j++) {
-            /*
-             * For each vcpu of each domain, it must have both vcpu-affinity
-             * and node-affinity to (a pcpu belonging to) a certain node to
-             * cause an increment in the corresponding element of the array.
-             *
-             * Note that we also need to check whether the cpu actually
-             * belongs to the domain's cpupool (the cpupool of the domain
-             * being checked). In fact, it could be that the vcpu has affinity
-             * with cpus in suitable_cpumask, but that are not in its own
-             * cpupool, and we don't want to consider those!
-             */
-            libxl_bitmap_set_none(&nodes_counted);
-            libxl_for_each_set_bit(k, vinfo[j].cpumap) {
-                if (k >= tinfo_elements)
-                    break;
-                int node = tinfo[k].node;
-
-                if (libxl_bitmap_test(suitable_cpumap, k) &&
-                    libxl_bitmap_test(&cpupool_info.cpumap, k) &&
-                    libxl_bitmap_test(&dom_nodemap, node) &&
-                    !libxl_bitmap_test(&nodes_counted, node)) {
-                    libxl_bitmap_set(&nodes_counted, node);
-                    vcpus_on_node[node]++;
-                }
-            }
-        }
-
- next:
-        libxl_cpupoolinfo_dispose(&cpupool_info);
-        libxl_vcpuinfo_list_free(vinfo, nr_dom_vcpus);
-    }
-
-    libxl_bitmap_dispose(&dom_nodemap);
-    libxl_bitmap_dispose(&nodes_counted);
-    libxl_dominfo_list_free(dinfo, nr_doms);
-    return 0;
-}
-
-/*
- * This function tries to figure out if the host has a consistent number
- * of cpus along all its NUMA nodes. In fact, if that is the case, we can
- * calculate the minimum number of nodes needed for a domain by just
- * dividing its total number of vcpus by this value computed here.
- * However, we are not allowed to assume that all the nodes have the
- * same number of cpus. Therefore, in case discrepancies among different
- * nodes are found, this function just returns 0, for the caller to know
- * it shouldn't rely on this 'optimization', and sort out things in some
- * other way (by doing something basic, like starting trying with
- * candidates with just one node).
- */
-static int count_cpus_per_node(libxl_cputopology *tinfo, int nr_cpus,
-                               int nr_nodes)
-{
-    int cpus_per_node = 0;
-    int j, i;
-
-    /* This makes sense iff # of PCPUs is the same for all nodes */
-    for (j = 0; j < nr_nodes; j++) {
-        int curr_cpus = 0;
-
-        for (i = 0; i < nr_cpus; i++) {
-            if (tinfo[i].node == j)
-                curr_cpus++;
-        }
-        /* So, if the above does not hold, turn the whole thing off! */
-        cpus_per_node = cpus_per_node == 0 ? curr_cpus : cpus_per_node;
-        if (cpus_per_node != curr_cpus)
-            return 0;
-    }
-    return cpus_per_node;
-}
-
-/*
- * Looks for the placement candidates that satisfyies some specific
- * conditions and return the best one according to the provided
- * comparison function.
- */
-int libxl__get_numa_candidate(libxl__gc *gc,
-                              uint64_t min_free_memkb, int min_cpus,
-                              int min_nodes, int max_nodes,
-                              const libxl_bitmap *suitable_cpumap,
-                              libxl__numa_candidate_cmpf numa_cmpf,
-                              libxl__numa_candidate *cndt_out,
-                              int *cndt_found)
-{
-    libxl__numa_candidate new_cndt;
-    libxl_cputopology *tinfo = NULL;
-    libxl_numainfo *ninfo = NULL;
-    int nr_nodes = 0, nr_suit_nodes, nr_cpus = 0;
-    libxl_bitmap suitable_nodemap, nodemap;
-    int *vcpus_on_node, rc = 0;
-
-    libxl_bitmap_init(&nodemap);
-    libxl_bitmap_init(&suitable_nodemap);
-    libxl__numa_candidate_init(&new_cndt);
-
-    /* Get platform info and prepare the map for testing the combinations */
-    ninfo = libxl_get_numainfo(CTX, &nr_nodes);
-    if (ninfo == NULL)
-        return ERROR_FAIL;
-
-    if (nr_nodes <= 1) {
-        *cndt_found = 0;
-        goto out;
-    }
-
-    GCNEW_ARRAY(vcpus_on_node, nr_nodes);
-
-    /*
-     * The good thing about this solution is that it is based on heuristics
-     * (implemented in numa_cmpf() ), but we at least can evaluate it on
-     * all the possible placement candidates. That can happen because the
-     * number of nodes present in current NUMA systems is quite small.
-     * In fact, even if a sum of binomials is involved, if the system has
-     * up to 16 nodes it "only" takes 65535 steps. This is fine, as the
-     * number of nodes the biggest NUMA systems provide at the time of this
-     * writing is 8 (and it will probably continue to be so for a while).
-     * However, computanional complexity would explode on systems bigger
-     * than that, and it's really important we avoid trying to run this
-     * on monsters with 32, 64 or more nodes (if they ever pop into being).
-     * Therefore, here it comes a safety catch that disables the algorithm
-     * for the cases when it wouldn't work well.
-     */
-    if (nr_nodes > 16) {
-        /* Log we did nothing and return 0, as no real error occurred */
-        LOG(WARN, "System has %d NUMA nodes, which is too big for the "
-                  "placement algorithm to work effectively: skipping it. "
-                  "Consider manually pinning the vCPUs and/or looking at "
-                  "cpupools for manually partitioning the system.",
-                  nr_nodes);
-        *cndt_found = 0;
-        goto out;
-    }
-
-    tinfo = libxl_get_cpu_topology(CTX, &nr_cpus);
-    if (tinfo == NULL) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
-    if (rc)
-        goto out;
-    rc = libxl__numa_candidate_alloc(gc, &new_cndt);
-    if (rc)
-        goto out;
-
-    /* Allocate and prepare the map of the node that can be utilized for
-     * placement, basing on the map of suitable cpus. */
-    rc = libxl_node_bitmap_alloc(CTX, &suitable_nodemap, 0);
-    if (rc)
-        goto out;
-    rc = libxl_cpumap_to_nodemap(CTX, suitable_cpumap, &suitable_nodemap);
-    if (rc)
-        goto out;
-
-    /*
-     * Later on, we will try to figure out how many vcpus are runnable on
-     * each candidate (as a part of choosing the best one of them). That
-     * requires going through all the vcpus of all the domains and check
-     * their affinities. So, instead of doing that for each candidate,
-     * let's count here the number of vcpus runnable on each node, so that
-     * all we have to do later is summing up the right elements of the
-     * vcpus_on_node array.
-     */
-    rc = nr_vcpus_on_nodes(gc, tinfo, nr_cpus, suitable_cpumap, vcpus_on_node);
-    if (rc)
-        goto out;
-
-    /*
-     * If the minimum number of NUMA nodes is not explicitly specified
-     * (i.e., min_nodes == 0), we try to figure out a sensible number of nodes
-     * from where to start generating candidates, if possible (or just start
-     * from 1 otherwise). The maximum number of nodes should not exceed the
-     * number of existent NUMA nodes on the host, or the candidate generation
-     * won't work properly.
-     */
-    if (!min_nodes) {
-        int cpus_per_node;
-
-        cpus_per_node = count_cpus_per_node(tinfo, nr_cpus, nr_nodes);
-        if (cpus_per_node == 0)
-            min_nodes = 1;
-        else
-            min_nodes = (min_cpus + cpus_per_node - 1) / cpus_per_node;
-    }
-    /* We also need to be sure we do not exceed the number of
-     * nodes we are allowed to use. */
-    nr_suit_nodes = libxl_bitmap_count_set(&suitable_nodemap);
-
-    if (min_nodes > nr_suit_nodes)
-        min_nodes = nr_suit_nodes;
-    if (!max_nodes || max_nodes > nr_suit_nodes)
-        max_nodes = nr_suit_nodes;
-    if (min_nodes > max_nodes) {
-        LOG(ERROR, "Inconsistent minimum or maximum number of guest nodes");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    /* This is up to the caller to be disposed */
-    rc = libxl__numa_candidate_alloc(gc, cndt_out);
-    if (rc)
-        goto out;
-
-    /*
-     * Consider all the combinations with sizes in [min_nodes, max_nodes]
-     * (see comb_init() and comb_next()). Note that, since the fewer the
-     * number of nodes the better, it is guaranteed that any candidate
-     * found during the i-eth step will be better than any other one we
-     * could find during the (i+1)-eth and all the subsequent steps (they
-     * all will have more nodes). It's thus pointless to keep going if
-     * we already found something.
-     */
-    *cndt_found = 0;
-    while (min_nodes <= max_nodes && *cndt_found == 0) {
-        comb_iter_t comb_iter;
-        int comb_ok;
-
-        /*
-         * And here it is. Each step of this cycle generates a combination of
-         * nodes as big as min_nodes mandates.  Each of these combinations is
-         * checked against the constraints provided by the caller (namely,
-         * amount of free memory and number of cpus) and it can concur to
-         * become our best placement iff it passes the check.
-         */
-        for (comb_ok = comb_init(gc, &comb_iter, nr_suit_nodes, min_nodes);
-             comb_ok;
-             comb_ok = comb_next(comb_iter, nr_suit_nodes, min_nodes)) {
-            uint64_t nodes_free_memkb;
-            int nodes_cpus;
-
-            /* Get the nodemap for the combination, only considering
-             * suitable nodes. */
-            comb_get_nodemap(comb_iter, &suitable_nodemap,
-                             &nodemap, min_nodes);
-
-            /* If there is not enough memory in this combination, skip it
-             * and go generating the next one... */
-            nodes_free_memkb = nodemap_to_free_memkb(ninfo, &nodemap);
-            if (min_free_memkb && nodes_free_memkb < min_free_memkb)
-                continue;
-
-            /* And the same applies if this combination is short in cpus */
-            nodes_cpus = nodemap_to_nr_cpus(tinfo, nr_cpus, suitable_cpumap,
-                                            &nodemap);
-            if (min_cpus && nodes_cpus < min_cpus)
-                continue;
-
-            /*
-             * Conditions are met, we can compare this candidate with the
-             * current best one (if any).
-             */
-            libxl__numa_candidate_put_nodemap(gc, &new_cndt, &nodemap);
-            new_cndt.nr_vcpus = nodemap_to_nr_vcpus(gc, vcpus_on_node,
-                                                    &nodemap);
-            new_cndt.free_memkb = nodes_free_memkb;
-            new_cndt.nr_nodes = libxl_bitmap_count_set(&nodemap);
-            new_cndt.nr_cpus = nodes_cpus;
-
-            /*
-             * Check if the new candidate we is better the what we found up
-             * to now by means of the comparison function. If no comparison
-             * function is provided, just return as soon as we find our first
-             * candidate.
-             */
-            if (*cndt_found == 0 || numa_cmpf(&new_cndt, cndt_out) < 0) {
-                *cndt_found = 1;
-
-                LOG(DEBUG, "New best NUMA placement candidate found: "
-                           "nr_nodes=%d, nr_cpus=%d, nr_vcpus=%d, "
-                           "free_memkb=%"PRIu64"", new_cndt.nr_nodes,
-                           new_cndt.nr_cpus, new_cndt.nr_vcpus,
-                           new_cndt.free_memkb / 1024);
-
-                libxl__numa_candidate_put_nodemap(gc, cndt_out, &nodemap);
-                cndt_out->nr_vcpus = new_cndt.nr_vcpus;
-                cndt_out->free_memkb = new_cndt.free_memkb;
-                cndt_out->nr_nodes = new_cndt.nr_nodes;
-                cndt_out->nr_cpus = new_cndt.nr_cpus;
-
-                if (numa_cmpf == NULL)
-                    break;
-            }
-        }
-        min_nodes++;
-    }
-
-    if (*cndt_found == 0)
-        LOG(NOTICE, "NUMA placement failed, performance might be affected");
-
- out:
-    libxl_bitmap_dispose(&nodemap);
-    libxl_bitmap_dispose(&suitable_nodemap);
-    libxl__numa_candidate_dispose(&new_cndt);
-    libxl_numainfo_list_free(ninfo, nr_nodes);
-    libxl_cputopology_list_free(tinfo, nr_cpus);
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_osdeps.h b/tools/libxl/libxl_osdeps.h
deleted file mode 100644 (file)
index de1d24e..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-/*
- * This header must be included first, before any system headers,
- * so that _GNU_SOURCE takes effect properly.
- */
-
-#ifndef LIBXL_OSDEP
-#define LIBXL_OSDEP
-
-#define _GNU_SOURCE
-
-#if defined(__NetBSD__)
-#define SYSFS_USB_DEV          "/sys/bus/usb/devices"
-#define SYSFS_USBBACK_DRIVER   "/kern/xen/usb"
-#define SYSFS_PCI_DEV          "/sys/bus/pci/devices"
-#define SYSFS_PCIBACK_DRIVER   "/kern/xen/pci"
-#define NETBACK_NIC_NAME       "xvif%ui%d"
-#include <util.h>
-#include <uuid.h>
-#elif defined(__OpenBSD__)
-#include <util.h>
-#elif defined(__linux__)
-#define SYSFS_USB_DEV          "/sys/bus/usb/devices"
-#define SYSFS_USBBACK_DRIVER   "/sys/bus/usb/drivers/usbback"
-#define SYSFS_PCI_DEV          "/sys/bus/pci/devices"
-#define SYSFS_PCIBACK_DRIVER   "/sys/bus/pci/drivers/pciback"
-#define NETBACK_NIC_NAME       "vif%u.%d"
-#include <sys/sysmacros.h>
-#include <pty.h>
-#include <uuid/uuid.h>
-#elif defined(__sun__)
-#include <stropts.h>
-#elif defined(__FreeBSD__)
-#define SYSFS_USB_DEV          "/dev/null"
-#define SYSFS_USBBACK_DRIVER   "/dev/null"
-#define SYSFS_PCI_DEV          "/dev/null"
-#define SYSFS_PCIBACK_DRIVER   "/dev/null"
-#define NETBACK_NIC_NAME       "xnb%u.%d"
-#include <libutil.h>
-#include <sys/endian.h>
-#include <uuid.h>
-/*
- * FreeBSD doesn't have ENODATA errno ATM, so privcmd always translates
- * ENODATA into ENOENT.
- */
-#ifndef ENODATA
-#define ENODATA ENOENT
-#endif
-#endif
-
-#ifndef SYSFS_USBBACK_DRIVER
-#error define SYSFS_USBBACK_DRIVER for your platform
-#endif
-#ifndef SYSFS_USB_DEV
-#error define SYSFS_USB_DEV for your platform
-#endif
-
-#ifndef SYSFS_PCIBACK_DRIVER
-#error define SYSFS_PCIBACK_DRIVER for your platform
-#endif
-#ifndef SYSFS_PCI_DEV
-#error define SYSFS_PCI_DEV for your platform
-#endif
-
-#ifdef NEED_OWN_ASPRINTF
-#include <stdarg.h>
-
-int asprintf(char **buffer, char *fmt, ...);
-int vasprintf(char **buffer, const char *fmt, va_list ap);
-#endif /*NEED_OWN_ASPRINTF*/
-
-#ifndef htobe32 /* glibc < 2.9 */
-# include <byteswap.h>
-
-# if __BYTE_ORDER == __LITTLE_ENDIAN
-#  define htobe16(x) __bswap_16(x)
-#  define htole16(x) (x)
-#  define be16toh(x) __bswap_16(x)
-#  define le16toh(x) (x)
-
-#  define htobe32(x) __bswap_32(x)
-#  define htole32(x) (x)
-#  define be32toh(x) __bswap_32(x)
-#  define le32toh(x) (x)
-
-#  define htobe64(x) __bswap_64(x)
-#  define htole64(x) (x)
-#  define be64toh(x) __bswap_64(x)
-#  define le64toh(x) (x)
-# else
-#  define htobe16(x) (x)
-#  define htole16(x) __bswap_16(x)
-#  define be16toh(x) (x)
-#  define le16toh(x) __bswap_16(x)
-
-#  define htobe32(x) (x)
-#  define htole32(x) __bswap_32(x)
-#  define be32toh(x) (x)
-#  define le32toh(x) __bswap_32(x)
-
-#  define htobe64(x) (x)
-#  define htole64(x) __bswap_64(x)
-#  define be64toh(x) (x)
-#  define le64toh(x) __bswap_64(x)
-# endif
-#endif
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_paths.c b/tools/libxl/libxl_paths.c
deleted file mode 100644 (file)
index 3f6a336..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-#include "libxl_internal.h"
-
-const char *libxl__private_bindir_path(void)
-{
-    return LIBEXEC_BIN;
-}
-
-const char *libxl__xenfirmwaredir_path(void)
-{
-    return XENFIRMWAREDIR;
-}
-
-const char *libxl__xen_script_dir_path(void)
-{
-    return XEN_SCRIPT_DIR;
-}
-
-const char *libxl__run_dir_path(void)
-{
-    return XEN_RUN_DIR;
-}
-
-const char *libxl__seabios_path(void)
-{
-#ifdef SEABIOS_PATH
-    return SEABIOS_PATH;
-#else
-    return NULL;
-#endif
-}
-
-const char *libxl__ovmf_path(void)
-{
-#ifdef OVMF_PATH
-    return OVMF_PATH;
-#else
-    return NULL;
-#endif
-}
-
-const char *libxl__ipxe_path(void)
-{
-#ifdef IPXE_PATH
-    return IPXE_PATH;
-#else
-    return NULL;
-#endif
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c
deleted file mode 100644 (file)
index bc5843b..0000000
+++ /dev/null
@@ -1,2508 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#define PCI_BDF                "%04x:%02x:%02x.%01x"
-#define PCI_BDF_SHORT          "%02x:%02x.%01x"
-#define PCI_BDF_VDEVFN         "%04x:%02x:%02x.%01x@%02x"
-#define PCI_OPTIONS            "msitranslate=%d,power_mgmt=%d"
-#define PCI_BDF_XSPATH         "%04x-%02x-%02x-%01x"
-#define PCI_PT_QDEV_ID         "pci-pt-%02x_%02x.%01x"
-
-static unsigned int pcidev_encode_bdf(libxl_device_pci *pcidev)
-{
-    unsigned int value;
-
-    value = pcidev->domain << 16;
-    value |= (pcidev->bus & 0xff) << 8;
-    value |= (pcidev->dev & 0x1f) << 3;
-    value |= (pcidev->func & 0x7);
-
-    return value;
-}
-
-static void pcidev_struct_fill(libxl_device_pci *pcidev, unsigned int domain,
-                               unsigned int bus, unsigned int dev,
-                               unsigned int func, unsigned int vdevfn)
-{
-    pcidev->domain = domain;
-    pcidev->bus = bus;
-    pcidev->dev = dev;
-    pcidev->func = func;
-    pcidev->vdevfn = vdevfn;
-}
-
-static void libxl_create_pci_backend_device(libxl__gc *gc,
-                                            flexarray_t *back,
-                                            int num,
-                                            const libxl_device_pci *pcidev)
-{
-    flexarray_append(back, GCSPRINTF("key-%d", num));
-    flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
-    flexarray_append(back, GCSPRINTF("dev-%d", num));
-    flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
-    if (pcidev->vdevfn)
-        flexarray_append_pair(back, GCSPRINTF("vdevfn-%d", num), GCSPRINTF("%x", pcidev->vdevfn));
-    flexarray_append(back, GCSPRINTF("opts-%d", num));
-    flexarray_append(back,
-              GCSPRINTF("msitranslate=%d,power_mgmt=%d,permissive=%d",
-                             pcidev->msitranslate, pcidev->power_mgmt,
-                             pcidev->permissive));
-    flexarray_append_pair(back, GCSPRINTF("state-%d", num), GCSPRINTF("%d", XenbusStateInitialising));
-}
-
-static void libxl__device_from_pcidev(libxl__gc *gc, uint32_t domid,
-                                      const libxl_device_pci *pcidev,
-                                      libxl__device *device)
-{
-    device->backend_devid = 0;
-    device->backend_domid = 0;
-    device->backend_kind = LIBXL__DEVICE_KIND_PCI;
-    device->devid = 0;
-    device->domid = domid;
-    device->kind = LIBXL__DEVICE_KIND_PCI;
-}
-
-static int libxl__create_pci_backend(libxl__gc *gc, uint32_t domid,
-                                     const libxl_device_pci *pcidev,
-                                     int num)
-{
-    flexarray_t *front = NULL;
-    flexarray_t *back = NULL;
-    libxl__device device;
-    int i;
-
-    front = flexarray_make(gc, 16, 1);
-    back = flexarray_make(gc, 16, 1);
-
-    LOGD(DEBUG, domid, "Creating pci backend");
-
-    /* add pci device */
-    libxl__device_from_pcidev(gc, domid, pcidev, &device);
-
-    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
-    flexarray_append_pair(back, "online", "1");
-    flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising));
-    flexarray_append_pair(back, "domain", libxl__domid_to_name(gc, domid));
-
-    for (i = 0; i < num; i++, pcidev++)
-        libxl_create_pci_backend_device(gc, back, i, pcidev);
-
-    flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num));
-    flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", 0));
-    flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising));
-
-    return libxl__device_generic_add(gc, XBT_NULL, &device,
-                                     libxl__xs_kvs_of_flexarray(gc, back),
-                                     libxl__xs_kvs_of_flexarray(gc, front),
-                                     NULL);
-}
-
-static int libxl__device_pci_add_xenstore(libxl__gc *gc,
-                                          uint32_t domid,
-                                          const libxl_device_pci *pcidev,
-                                          bool starting)
-{
-    flexarray_t *back;
-    char *num_devs, *be_path;
-    int num = 0;
-    xs_transaction_t t = XBT_NULL;
-    int rc;
-    libxl_domain_config d_config;
-    libxl__flock *lock = NULL;
-    bool is_stubdomain = libxl_is_stubdom(CTX, domid, NULL);
-
-    /* Stubdomain doesn't have own config. */
-    if (!is_stubdomain)
-        libxl_domain_config_init(&d_config);
-
-    be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
-                                                LIBXL__DEVICE_KIND_PCI);
-    num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
-    if (!num_devs)
-        return libxl__create_pci_backend(gc, domid, pcidev, 1);
-
-    libxl_domain_type domtype = libxl__domain_type(gc, domid);
-    if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
-        return ERROR_FAIL;
-
-    if (!starting && domtype == LIBXL_DOMAIN_TYPE_PV) {
-        if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0)
-            return ERROR_FAIL;
-    }
-
-    back = flexarray_make(gc, 16, 1);
-
-    LOGD(DEBUG, domid, "Adding new pci device to xenstore");
-    num = atoi(num_devs);
-    libxl_create_pci_backend_device(gc, back, num, pcidev);
-    flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num + 1));
-    if (!starting)
-        flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring));
-
-    /*
-     * Stubdomin config is derived from its target domain, it doesn't have
-     * its own file.
-     */
-    if (!is_stubdomain) {
-        lock = libxl__lock_domain_userdata(gc, domid);
-        if (!lock) {
-            rc = ERROR_LOCK_FAIL;
-            goto out;
-        }
-
-        rc = libxl__get_domain_configuration(gc, domid, &d_config);
-        if (rc) goto out;
-
-        device_add_domain_config(gc, &d_config, &libxl__pcidev_devtype,
-                                 pcidev);
-
-        rc = libxl__dm_check_start(gc, &d_config, domid);
-        if (rc) goto out;
-    }
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        if (lock) {
-            rc = libxl__set_domain_configuration(gc, domid, &d_config);
-            if (rc) goto out;
-        }
-
-        libxl__xs_writev(gc, t, be_path, libxl__xs_kvs_of_flexarray(gc, back));
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    if (lock) libxl__unlock_file(lock);
-    if (!is_stubdomain)
-        libxl_domain_config_dispose(&d_config);
-    return rc;
-}
-
-static int libxl__device_pci_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath;
-    int num, i, j;
-    xs_transaction_t t;
-
-    be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
-                                                LIBXL__DEVICE_KIND_PCI);
-    num_devs_path = GCSPRINTF("%s/num_devs", be_path);
-    num_devs = libxl__xs_read(gc, XBT_NULL, num_devs_path);
-    if (!num_devs)
-        return ERROR_INVAL;
-    num = atoi(num_devs);
-
-    libxl_domain_type domtype = libxl__domain_type(gc, domid);
-    if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
-        return ERROR_FAIL;
-
-    if (domtype == LIBXL_DOMAIN_TYPE_PV) {
-        if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
-            LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
-            return ERROR_FAIL;
-        }
-    }
-
-    for (i = 0; i < num; i++) {
-        unsigned int domain = 0, bus = 0, dev = 0, func = 0;
-        xsdev = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, i));
-        sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
-        if (domain == pcidev->domain && bus == pcidev->bus &&
-            pcidev->dev == dev && pcidev->func == func) {
-            break;
-        }
-    }
-    if (i == num) {
-        LOGD(ERROR, domid, "Couldn't find the device on xenstore");
-        return ERROR_INVAL;
-    }
-
-retry_transaction:
-    t = xs_transaction_start(ctx->xsh);
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i), GCSPRINTF("%d", XenbusStateClosing), 1);
-    xs_write(ctx->xsh, t, GCSPRINTF("%s/state", be_path), GCSPRINTF("%d", XenbusStateReconfiguring), 1);
-    if (!xs_transaction_end(ctx->xsh, t, 0))
-        if (errno == EAGAIN)
-            goto retry_transaction;
-
-    if (domtype == LIBXL_DOMAIN_TYPE_PV) {
-        if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) {
-            LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path);
-            return ERROR_FAIL;
-        }
-    }
-
-retry_transaction2:
-    t = xs_transaction_start(ctx->xsh);
-    xs_rm(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i));
-    xs_rm(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, i));
-    xs_rm(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, i));
-    xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, i));
-    xs_rm(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, i));
-    xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, i));
-    libxl__xs_printf(gc, t, num_devs_path, "%d", num - 1);
-    for (j = i + 1; j < num; j++) {
-        tmppath = GCSPRINTF("%s/state-%d", be_path, j);
-        tmp = libxl__xs_read(gc, t, tmppath);
-        xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, j - 1), tmp, strlen(tmp));
-        xs_rm(ctx->xsh, t, tmppath);
-        tmppath = GCSPRINTF("%s/dev-%d", be_path, j);
-        tmp = libxl__xs_read(gc, t, tmppath);
-        xs_write(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, j - 1), tmp, strlen(tmp));
-        xs_rm(ctx->xsh, t, tmppath);
-        tmppath = GCSPRINTF("%s/key-%d", be_path, j);
-        tmp = libxl__xs_read(gc, t, tmppath);
-        xs_write(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, j - 1), tmp, strlen(tmp));
-        xs_rm(ctx->xsh, t, tmppath);
-        tmppath = GCSPRINTF("%s/vdev-%d", be_path, j);
-        tmp = libxl__xs_read(gc, t, tmppath);
-        if (tmp) {
-            xs_write(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp));
-            xs_rm(ctx->xsh, t, tmppath);
-        }
-        tmppath = GCSPRINTF("%s/opts-%d", be_path, j);
-        tmp = libxl__xs_read(gc, t, tmppath);
-        if (tmp) {
-            xs_write(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, j - 1), tmp, strlen(tmp));
-            xs_rm(ctx->xsh, t, tmppath);
-        }
-        tmppath = GCSPRINTF("%s/vdevfn-%d", be_path, j);
-        tmp = libxl__xs_read(gc, t, tmppath);
-        if (tmp) {
-            xs_write(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp));
-            xs_rm(ctx->xsh, t, tmppath);
-        }
-    }
-    if (!xs_transaction_end(ctx->xsh, t, 0))
-        if (errno == EAGAIN)
-            goto retry_transaction2;
-
-    if (num == 1) {
-        libxl__device dev;
-        if (libxl__parse_backend_path(gc, be_path, &dev) != 0)
-            return ERROR_FAIL;
-
-        dev.domid = domid;
-        dev.kind = LIBXL__DEVICE_KIND_PCI;
-        dev.devid = 0;
-
-        libxl__device_destroy(gc, &dev);
-        return 0;
-    }
-
-    return 0;
-}
-
-static int get_all_assigned_devices(libxl__gc *gc, libxl_device_pci **list, int *num)
-{
-    char **domlist;
-    unsigned int nd = 0, i;
-
-    *list = NULL;
-    *num = 0;
-
-    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &nd);
-    for(i = 0; i < nd; i++) {
-        char *path, *num_devs;
-
-        path = GCSPRINTF("/local/domain/0/backend/%s/%s/0/num_devs",
-                         libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
-                         domlist[i]);
-        num_devs = libxl__xs_read(gc, XBT_NULL, path);
-        if ( num_devs ) {
-            int ndev = atoi(num_devs), j;
-            char *devpath, *bdf;
-
-            for(j = 0; j < ndev; j++) {
-                devpath = GCSPRINTF("/local/domain/0/backend/%s/%s/0/dev-%u",
-                                    libxl__device_kind_to_string(LIBXL__DEVICE_KIND_PCI),
-                                    domlist[i], j);
-                bdf = libxl__xs_read(gc, XBT_NULL, devpath);
-                if ( bdf ) {
-                    unsigned dom, bus, dev, func;
-                    if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
-                        continue;
-
-                    *list = realloc(*list, sizeof(libxl_device_pci) * ((*num) + 1));
-                    if (*list == NULL)
-                        return ERROR_NOMEM;
-                    pcidev_struct_fill(*list + *num, dom, bus, dev, func, 0);
-                    (*num)++;
-                }
-            }
-        }
-    }
-    libxl__ptr_add(gc, *list);
-
-    return 0;
-}
-
-static int is_pcidev_in_array(libxl_device_pci *assigned, int num_assigned,
-                       int dom, int bus, int dev, int func)
-{
-    int i;
-
-    for(i = 0; i < num_assigned; i++) {
-        if ( assigned[i].domain != dom )
-            continue;
-        if ( assigned[i].bus != bus )
-            continue;
-        if ( assigned[i].dev != dev )
-            continue;
-        if ( assigned[i].func != func )
-            continue;
-        return 1;
-    }
-
-    return 0;
-}
-
-/* Write the standard BDF into the sysfs path given by sysfs_path. */
-static int sysfs_write_bdf(libxl__gc *gc, const char * sysfs_path,
-                           libxl_device_pci *pcidev)
-{
-    int rc, fd;
-    char *buf;
-
-    fd = open(sysfs_path, O_WRONLY);
-    if (fd < 0) {
-        LOGE(ERROR, "Couldn't open %s", sysfs_path);
-        return ERROR_FAIL;
-    }
-
-    buf = GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus,
-                    pcidev->dev, pcidev->func);
-    rc = write(fd, buf, strlen(buf));
-    /* Annoying to have two if's, but we need the errno */
-    if (rc < 0)
-        LOGE(ERROR, "write to %s returned %d", sysfs_path, rc);
-    close(fd);
-
-    if (rc < 0)
-        return ERROR_FAIL;
-
-    return 0;
-}
-
-libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num)
-{
-    GC_INIT(ctx);
-    libxl_device_pci *pcidevs = NULL, *new, *assigned;
-    struct dirent *de;
-    DIR *dir;
-    int r, num_assigned;
-
-    *num = 0;
-
-    r = get_all_assigned_devices(gc, &assigned, &num_assigned);
-    if (r) goto out;
-
-    dir = opendir(SYSFS_PCIBACK_DRIVER);
-    if (NULL == dir) {
-        if (errno == ENOENT) {
-            LOG(ERROR, "Looks like pciback driver not loaded");
-        } else {
-            LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER);
-        }
-        goto out;
-    }
-
-    while((de = readdir(dir))) {
-        unsigned dom, bus, dev, func;
-        if (sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4)
-            continue;
-
-        if (is_pcidev_in_array(assigned, num_assigned, dom, bus, dev, func))
-            continue;
-
-        new = realloc(pcidevs, ((*num) + 1) * sizeof(*new));
-        if (NULL == new)
-            continue;
-
-        pcidevs = new;
-        new = pcidevs + *num;
-
-        memset(new, 0, sizeof(*new));
-        pcidev_struct_fill(new, dom, bus, dev, func, 0);
-        (*num)++;
-    }
-
-    closedir(dir);
-out:
-    GC_FREE;
-    return pcidevs;
-}
-
-/* Unbind device from its current driver, if any.  If driver_path is non-NULL,
- * store the path to the original driver in it. */
-static int sysfs_dev_unbind(libxl__gc *gc, libxl_device_pci *pcidev,
-                            char **driver_path)
-{
-    char * spath, *dp = NULL;
-    struct stat st;
-
-    spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/driver",
-                           pcidev->domain,
-                           pcidev->bus,
-                           pcidev->dev,
-                           pcidev->func);
-    if ( !lstat(spath, &st) ) {
-        /* Find the canonical path to the driver. */
-        dp = libxl__zalloc(gc, PATH_MAX);
-        dp = realpath(spath, dp);
-        if ( !dp ) {
-            LOGE(ERROR, "realpath() failed");
-            return -1;
-        }
-
-        LOG(DEBUG, "Driver re-plug path: %s", dp);
-
-        /* Unbind from the old driver */
-        spath = GCSPRINTF("%s/unbind", dp);
-        if ( sysfs_write_bdf(gc, spath, pcidev) < 0 ) {
-            LOGE(ERROR, "Couldn't unbind device");
-            return -1;
-        }
-    }
-
-    if ( driver_path )
-        *driver_path = dp;
-
-    return 0;
-}
-
-static uint16_t sysfs_dev_get_vendor(libxl__gc *gc, libxl_device_pci *pcidev)
-{
-    char *pci_device_vendor_path =
-            GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/vendor",
-                      pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-    uint16_t read_items;
-    uint16_t pci_device_vendor;
-
-    FILE *f = fopen(pci_device_vendor_path, "r");
-    if (!f) {
-        LOGE(ERROR,
-             "pci device "PCI_BDF" does not have vendor attribute",
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-        return 0xffff;
-    }
-    read_items = fscanf(f, "0x%hx\n", &pci_device_vendor);
-    fclose(f);
-    if (read_items != 1) {
-        LOGE(ERROR,
-             "cannot read vendor of pci device "PCI_BDF,
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-        return 0xffff;
-    }
-
-    return pci_device_vendor;
-}
-
-static uint16_t sysfs_dev_get_device(libxl__gc *gc, libxl_device_pci *pcidev)
-{
-    char *pci_device_device_path =
-            GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/device",
-                      pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-    uint16_t read_items;
-    uint16_t pci_device_device;
-
-    FILE *f = fopen(pci_device_device_path, "r");
-    if (!f) {
-        LOGE(ERROR,
-             "pci device "PCI_BDF" does not have device attribute",
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-        return 0xffff;
-    }
-    read_items = fscanf(f, "0x%hx\n", &pci_device_device);
-    fclose(f);
-    if (read_items != 1) {
-        LOGE(ERROR,
-             "cannot read device of pci device "PCI_BDF,
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-        return 0xffff;
-    }
-
-    return pci_device_device;
-}
-
-static int sysfs_dev_get_class(libxl__gc *gc, libxl_device_pci *pcidev,
-                               unsigned long *class)
-{
-    char *pci_device_class_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/class",
-                     pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-    int read_items, ret = 0;
-
-    FILE *f = fopen(pci_device_class_path, "r");
-    if (!f) {
-        LOGE(ERROR,
-             "pci device "PCI_BDF" does not have class attribute",
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-        ret = ERROR_FAIL;
-        goto out;
-    }
-    read_items = fscanf(f, "0x%lx\n", class);
-    fclose(f);
-    if (read_items != 1) {
-        LOGE(ERROR,
-             "cannot read class of pci device "PCI_BDF,
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-        ret = ERROR_FAIL;
-    }
-
-out:
-    return ret;
-}
-
-/*
- * Some devices may need some ways to work well. Here like IGD,
- * we have to pass a specific option to qemu.
- */
-bool libxl__is_igd_vga_passthru(libxl__gc *gc,
-                                const libxl_domain_config *d_config)
-{
-    unsigned int i;
-    uint16_t pt_vendor, pt_device;
-    unsigned long class;
-
-    for (i = 0 ; i < d_config->num_pcidevs ; i++) {
-        libxl_device_pci *pcidev = &d_config->pcidevs[i];
-        pt_vendor = sysfs_dev_get_vendor(gc, pcidev);
-        pt_device = sysfs_dev_get_device(gc, pcidev);
-
-        if (pt_vendor == 0xffff || pt_device == 0xffff ||
-            pt_vendor != 0x8086)
-            continue;
-
-        if (sysfs_dev_get_class(gc, pcidev, &class))
-            continue;
-        if (class == 0x030000)
-            return true;
-    }
-
-    return false;
-}
-
-/*
- * A brief comment about slots.  I don't know what slots are for; however,
- * I have by experimentation determined:
- * - Before a device can be bound to pciback, its BDF must first be listed
- *   in pciback/slots
- * - The way to get the BDF listed there is to write BDF to
- *   pciback/new_slot
- * - Writing the same BDF to pciback/new_slot is not idempotent; it results
- *   in two entries of the BDF in pciback/slots
- * It's not clear whether having two entries in pciback/slots is a problem
- * or not.  Just to be safe, this code does the conservative thing, and
- * first checks to see if there is a slot, adding one only if one does not
- * already exist.
- */
-
-/* Scan through /sys/.../pciback/slots looking for pcidev's BDF */
-static int pciback_dev_has_slot(libxl__gc *gc, libxl_device_pci *pcidev)
-{
-    FILE *f;
-    int rc = 0;
-    unsigned dom, bus, dev, func;
-
-    f = fopen(SYSFS_PCIBACK_DRIVER"/slots", "r");
-
-    if (f == NULL) {
-        LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER"/slots");
-        return ERROR_FAIL;
-    }
-
-    while(fscanf(f, "%x:%x:%x.%d\n", &dom, &bus, &dev, &func)==4) {
-        if(dom == pcidev->domain
-           && bus == pcidev->bus
-           && dev == pcidev->dev
-           && func == pcidev->func) {
-            rc = 1;
-            goto out;
-        }
-    }
-out:
-    fclose(f);
-    return rc;
-}
-
-static int pciback_dev_is_assigned(libxl__gc *gc, libxl_device_pci *pcidev)
-{
-    char * spath;
-    int rc;
-    struct stat st;
-
-    if ( access(SYSFS_PCIBACK_DRIVER, F_OK) < 0 ) {
-        if ( errno == ENOENT ) {
-            LOG(ERROR, "Looks like pciback driver is not loaded");
-        } else {
-            LOGE(ERROR, "Can't access "SYSFS_PCIBACK_DRIVER);
-        }
-        return -1;
-    }
-
-    spath = GCSPRINTF(SYSFS_PCIBACK_DRIVER"/"PCI_BDF,
-                      pcidev->domain, pcidev->bus,
-                      pcidev->dev, pcidev->func);
-    rc = lstat(spath, &st);
-
-    if( rc == 0 )
-        return 1;
-    if ( rc < 0 && errno == ENOENT )
-        return 0;
-    LOGE(ERROR, "Accessing %s", spath);
-    return -1;
-}
-
-static int pciback_dev_assign(libxl__gc *gc, libxl_device_pci *pcidev)
-{
-    int rc;
-
-    if ( (rc=pciback_dev_has_slot(gc, pcidev)) < 0 ) {
-        LOGE(ERROR, "Error checking for pciback slot");
-        return ERROR_FAIL;
-    } else if (rc == 0) {
-        if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/new_slot",
-                             pcidev) < 0 ) {
-            LOGE(ERROR, "Couldn't bind device to pciback!");
-            return ERROR_FAIL;
-        }
-    }
-
-    if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/bind", pcidev) < 0 ) {
-        LOGE(ERROR, "Couldn't bind device to pciback!");
-        return ERROR_FAIL;
-    }
-    return 0;
-}
-
-static int pciback_dev_unassign(libxl__gc *gc, libxl_device_pci *pcidev)
-{
-    /* Remove from pciback */
-    if ( sysfs_dev_unbind(gc, pcidev, NULL) < 0 ) {
-        LOG(ERROR, "Couldn't unbind device!");
-        return ERROR_FAIL;
-    }
-
-    /* Remove slot if necessary */
-    if ( pciback_dev_has_slot(gc, pcidev) > 0 ) {
-        if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/remove_slot",
-                             pcidev) < 0 ) {
-            LOGE(ERROR, "Couldn't remove pciback slot");
-            return ERROR_FAIL;
-        }
-    }
-    return 0;
-}
-
-#define PCIBACK_INFO_PATH "/libxl/pciback"
-
-static void pci_assignable_driver_path_write(libxl__gc *gc,
-                                            libxl_device_pci *pcidev,
-                                            char *driver_path)
-{
-    char *path;
-
-    path = GCSPRINTF(PCIBACK_INFO_PATH"/"PCI_BDF_XSPATH"/driver_path",
-                     pcidev->domain,
-                     pcidev->bus,
-                     pcidev->dev,
-                     pcidev->func);
-    if ( libxl__xs_printf(gc, XBT_NULL, path, "%s", driver_path) < 0 ) {
-        LOGE(WARN, "Write of %s to node %s failed.", driver_path, path);
-    }
-}
-
-static char * pci_assignable_driver_path_read(libxl__gc *gc,
-                                              libxl_device_pci *pcidev)
-{
-    return libxl__xs_read(gc, XBT_NULL,
-                          GCSPRINTF(
-                           PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH "/driver_path",
-                           pcidev->domain,
-                           pcidev->bus,
-                           pcidev->dev,
-                           pcidev->func));
-}
-
-static void pci_assignable_driver_path_remove(libxl__gc *gc,
-                                              libxl_device_pci *pcidev)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-
-    /* Remove the xenstore entry */
-    xs_rm(ctx->xsh, XBT_NULL,
-          GCSPRINTF(PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH,
-                    pcidev->domain,
-                    pcidev->bus,
-                    pcidev->dev,
-                    pcidev->func) );
-}
-
-static int libxl__device_pci_assignable_add(libxl__gc *gc,
-                                            libxl_device_pci *pcidev,
-                                            int rebind)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    unsigned dom, bus, dev, func;
-    char *spath, *driver_path = NULL;
-    int rc;
-    struct stat st;
-
-    /* Local copy for convenience */
-    dom = pcidev->domain;
-    bus = pcidev->bus;
-    dev = pcidev->dev;
-    func = pcidev->func;
-
-    /* See if the device exists */
-    spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF, dom, bus, dev, func);
-    if ( lstat(spath, &st) ) {
-        LOGE(ERROR, "Couldn't lstat %s", spath);
-        return ERROR_FAIL;
-    }
-
-    /* Check to see if it's already assigned to pciback */
-    rc = pciback_dev_is_assigned(gc, pcidev);
-    if ( rc < 0 ) {
-        return ERROR_FAIL;
-    }
-    if ( rc ) {
-        LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func);
-        goto quarantine;
-    }
-
-    /* Check to see if there's already a driver that we need to unbind from */
-    if ( sysfs_dev_unbind(gc, pcidev, &driver_path ) ) {
-        LOG(ERROR, "Couldn't unbind "PCI_BDF" from driver",
-            dom, bus, dev, func);
-        return ERROR_FAIL;
-    }
-
-    /* Store driver_path for rebinding to dom0 */
-    if ( rebind ) {
-        if ( driver_path ) {
-            pci_assignable_driver_path_write(gc, pcidev, driver_path);
-        } else if ( (driver_path =
-                     pci_assignable_driver_path_read(gc, pcidev)) != NULL ) {
-            LOG(INFO, PCI_BDF" not bound to a driver, will be rebound to %s",
-                dom, bus, dev, func, driver_path);
-        } else {
-            LOG(WARN, PCI_BDF" not bound to a driver, will not be rebound.",
-                dom, bus, dev, func);
-        }
-    } else {
-        pci_assignable_driver_path_remove(gc, pcidev);
-    }
-
-    if ( pciback_dev_assign(gc, pcidev) ) {
-        LOG(ERROR, "Couldn't bind device to pciback!");
-        return ERROR_FAIL;
-    }
-
-quarantine:
-    /*
-     * DOMID_IO is just a sentinel domain, without any actual mappings,
-     * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being
-     * unnecessarily denied.
-     */
-    rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev),
-                          XEN_DOMCTL_DEV_RDM_RELAXED);
-    if ( rc < 0 ) {
-        LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func);
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-static int libxl__device_pci_assignable_remove(libxl__gc *gc,
-                                               libxl_device_pci *pcidev,
-                                               int rebind)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int rc;
-    char *driver_path;
-
-    /* De-quarantine */
-    rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev));
-    if ( rc < 0 ) {
-        LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus,
-            pcidev->dev, pcidev->func);
-        return ERROR_FAIL;
-    }
-
-    /* Unbind from pciback */
-    if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) {
-        return ERROR_FAIL;
-    } else if ( rc ) {
-        pciback_dev_unassign(gc, pcidev);
-    } else {
-        LOG(WARN, "Not bound to pciback");
-    }
-
-    /* Rebind if necessary */
-    driver_path = pci_assignable_driver_path_read(gc, pcidev);
-
-    if ( driver_path ) {
-        if ( rebind ) {
-            LOG(INFO, "Rebinding to driver at %s", driver_path);
-
-            if ( sysfs_write_bdf(gc,
-                                 GCSPRINTF("%s/bind", driver_path),
-                                 pcidev) < 0 ) {
-                LOGE(ERROR, "Couldn't bind device to %s", driver_path);
-                return -1;
-            }
-
-            pci_assignable_driver_path_remove(gc, pcidev);
-        }
-    } else {
-        if ( rebind ) {
-            LOG(WARN,
-                "Couldn't find path for original driver; not rebinding");
-        }
-    }
-
-    return 0;
-}
-
-int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev,
-                                    int rebind)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    rc = libxl__device_pci_assignable_add(gc, pcidev, rebind);
-
-    GC_FREE;
-    return rc;
-}
-
-
-int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev,
-                                       int rebind)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    rc = libxl__device_pci_assignable_remove(gc, pcidev, rebind);
-
-    GC_FREE;
-    return rc;
-}
-
-/*
- * This function checks that all functions of a device are bound to pciback
- * driver. It also initialises a bit-mask of which function numbers are present
- * on that device.
-*/
-static int pci_multifunction_check(libxl__gc *gc, libxl_device_pci *pcidev, unsigned int *func_mask)
-{
-    struct dirent *de;
-    DIR *dir;
-
-    *func_mask = 0;
-
-    dir = opendir(SYSFS_PCI_DEV);
-    if ( NULL == dir ) {
-        LOGE(ERROR, "Couldn't open %s", SYSFS_PCI_DEV);
-        return -1;
-    }
-
-    while( (de = readdir(dir)) ) {
-        unsigned dom, bus, dev, func;
-        struct stat st;
-        char *path;
-
-        if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
-            continue;
-        if ( pcidev->domain != dom )
-            continue;
-        if ( pcidev->bus != bus )
-            continue;
-        if ( pcidev->dev != dev )
-            continue;
-
-        path = GCSPRINTF("%s/" PCI_BDF, SYSFS_PCIBACK_DRIVER, dom, bus, dev, func);
-        if ( lstat(path, &st) ) {
-            if ( errno == ENOENT )
-                LOG(ERROR, PCI_BDF " is not assigned to pciback driver",
-                    dom, bus, dev, func);
-            else
-                LOGE(ERROR, "Couldn't lstat %s", path);
-            closedir(dir);
-            return -1;
-        }
-        (*func_mask) |= (1 << func);
-    }
-
-    closedir(dir);
-    return 0;
-}
-
-static int pci_ins_check(libxl__gc *gc, uint32_t domid, const char *state, void *priv)
-{
-    char *orig_state = priv;
-
-    if ( !strcmp(state, "pci-insert-failed") )
-        return -1;
-    if ( !strcmp(state, "pci-inserted") )
-        return 0;
-    if ( !strcmp(state, orig_state) )
-        return 1;
-
-    return 1;
-}
-
-static int qemu_pci_add_xenstore(libxl__gc *gc, uint32_t domid,
-                                 libxl_device_pci *pcidev)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int rc = 0;
-    char *path;
-    char *state, *vdevfn;
-    uint32_t dm_domid;
-
-    dm_domid = libxl_get_stubdom_id(CTX, domid);
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
-    state = libxl__xs_read(gc, XBT_NULL, path);
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
-    if (pcidev->vdevfn) {
-        libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF_VDEVFN","PCI_OPTIONS,
-                         pcidev->domain, pcidev->bus, pcidev->dev,
-                         pcidev->func, pcidev->vdevfn, pcidev->msitranslate,
-                         pcidev->power_mgmt);
-    } else {
-        libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF","PCI_OPTIONS,
-                         pcidev->domain,  pcidev->bus, pcidev->dev,
-                         pcidev->func, pcidev->msitranslate, pcidev->power_mgmt);
-    }
-
-    libxl__qemu_traditional_cmd(gc, domid, "pci-ins");
-    rc = libxl__wait_for_device_model_deprecated(gc, domid, NULL, NULL,
-                                      pci_ins_check, state);
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
-    vdevfn = libxl__xs_read(gc, XBT_NULL, path);
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
-    if ( rc < 0 )
-        LOGD(ERROR, domid, "qemu refused to add device: %s", vdevfn);
-    else if ( sscanf(vdevfn, "0x%x", &pcidev->vdevfn) != 1 ) {
-        LOGD(ERROR, domid, "wrong format for the vdevfn: '%s'", vdevfn);
-        rc = -1;
-    }
-    xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
-
-    return rc;
-}
-
-static int check_qemu_running(libxl__gc *gc,
-                              libxl_domid domid,
-                              libxl__xswait_state *xswa,
-                              int rc,
-                              const char *state)
-{
-    if (rc) {
-        if (rc == ERROR_TIMEDOUT) {
-            LOGD(ERROR, domid, "%s not ready", xswa->what);
-        }
-        goto out;
-    }
-
-    if (!state || strcmp(state, "running"))
-        return ERROR_NOT_READY;
-
-out:
-    libxl__xswait_stop(gc, xswa);
-    return rc;
-}
-
-typedef struct pci_add_state {
-    /* filled by user of do_pci_add */
-    libxl__ao_device *aodev;
-    libxl_domid domid;
-    bool starting;
-    void (*callback)(libxl__egc *, struct pci_add_state *, int rc);
-
-    /* private to device_pci_add_stubdom_wait */
-    libxl__ev_devstate pciback_ds;
-
-    /* private to do_pci_add */
-    libxl__xswait_state xswait;
-    libxl__ev_qmp qmp;
-    libxl__ev_time timeout;
-    libxl_device_pci *pcidev;
-    int pci_domid;
-} pci_add_state;
-
-static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
-    libxl__xswait_state *xswa, int rc, const char *state);
-static void pci_add_qmp_device_add(libxl__egc *, pci_add_state *);
-static void pci_add_qmp_device_add_cb(libxl__egc *,
-    libxl__ev_qmp *, const libxl__json_object *, int rc);
-static void pci_add_qmp_query_pci_cb(libxl__egc *,
-    libxl__ev_qmp *, const libxl__json_object *, int rc);
-static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
-    const struct timeval *requested_abs, int rc);
-static void pci_add_dm_done(libxl__egc *,
-    pci_add_state *, int rc);
-
-static void do_pci_add(libxl__egc *egc,
-                       libxl_domid domid,
-                       libxl_device_pci *pcidev,
-                       pci_add_state *pas)
-{
-    STATE_AO_GC(pas->aodev->ao);
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    int rc;
-
-    /* init pci_add_state */
-    libxl__xswait_init(&pas->xswait);
-    libxl__ev_qmp_init(&pas->qmp);
-    pas->pcidev = pcidev;
-    pas->pci_domid = domid;
-    libxl__ev_time_init(&pas->timeout);
-
-    if (type == LIBXL_DOMAIN_TYPE_INVALID) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (type == LIBXL_DOMAIN_TYPE_HVM) {
-        switch (libxl__device_model_version_running(gc, domid)) {
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-                pas->xswait.ao = ao;
-                pas->xswait.what = "Device Model";
-                pas->xswait.path = DEVICE_MODEL_XS_PATH(gc,
-                    libxl_get_stubdom_id(CTX, domid), domid, "/state");
-                pas->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
-                pas->xswait.callback = pci_add_qemu_trad_watch_state_cb;
-                rc = libxl__xswait_start(gc, &pas->xswait);
-                if (rc) goto out;
-                return;
-            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-                pci_add_qmp_device_add(egc, pas); /* must be last */
-                return;
-            default:
-                rc = ERROR_INVAL;
-                break;
-        }
-    }
-
-    rc = 0;
-
-out:
-    pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qemu_trad_watch_state_cb(libxl__egc *egc,
-                                             libxl__xswait_state *xswa,
-                                             int rc,
-                                             const char *state)
-{
-    pci_add_state *pas = CONTAINER_OF(xswa, *pas, xswait);
-    STATE_AO_GC(pas->aodev->ao);
-
-    /* Convenience aliases */
-    libxl_domid domid = pas->domid;
-    libxl_device_pci *pcidev = pas->pcidev;
-
-    rc = check_qemu_running(gc, domid, xswa, rc, state);
-    if (rc == ERROR_NOT_READY)
-        return;
-    if (rc)
-        goto out;
-
-    rc = qemu_pci_add_xenstore(gc, domid, pcidev);
-out:
-    pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qmp_device_add(libxl__egc *egc, pci_add_state *pas)
-{
-    STATE_AO_GC(pas->aodev->ao);
-    libxl__json_object *args = NULL;
-    int rc;
-
-    /* Convenience aliases */
-    libxl_domid domid = pas->domid;
-    libxl_device_pci *pcidev = pas->pcidev;
-    libxl__ev_qmp *const qmp = &pas->qmp;
-
-    rc = libxl__ev_time_register_rel(ao, &pas->timeout,
-                                     pci_add_timeout,
-                                     LIBXL_QMP_CMD_TIMEOUT * 1000);
-    if (rc) goto out;
-
-    libxl__qmp_param_add_string(gc, &args, "driver",
-                                "xen-pci-passthrough");
-    QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
-                           pcidev->bus, pcidev->dev, pcidev->func);
-    QMP_PARAMETERS_SPRINTF(&args, "hostaddr",
-                           "%04x:%02x:%02x.%01x", pcidev->domain,
-                           pcidev->bus, pcidev->dev, pcidev->func);
-    if (pcidev->vdevfn) {
-        QMP_PARAMETERS_SPRINTF(&args, "addr", "%x.%x",
-                               PCI_SLOT(pcidev->vdevfn),
-                               PCI_FUNC(pcidev->vdevfn));
-    }
-    /*
-     * Version of QEMU prior to the XSA-131 fix did not support
-     * this property and were effectively always in permissive
-     * mode. The fix for XSA-131 switched the default to be
-     * restricted by default and added the permissive property.
-     *
-     * Therefore in order to support both old and new QEMU we only
-     * set the permissive flag if it is true. Users of older QEMU
-     * have no reason to set the flag so this is ok.
-     */
-    if (pcidev->permissive)
-        libxl__qmp_param_add_bool(gc, &args, "permissive", true);
-
-    qmp->ao = pas->aodev->ao;
-    qmp->domid = domid;
-    qmp->payload_fd = -1;
-    qmp->callback = pci_add_qmp_device_add_cb;
-    rc = libxl__ev_qmp_send(egc, qmp, "device_add", args);
-    if (rc) goto out;
-    return;
-
-out:
-    pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qmp_device_add_cb(libxl__egc *egc,
-                                      libxl__ev_qmp *qmp,
-                                      const libxl__json_object *response,
-                                      int rc)
-{
-    EGC_GC;
-    pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
-
-    if (rc) goto out;
-
-    qmp->callback = pci_add_qmp_query_pci_cb;
-    rc = libxl__ev_qmp_send(egc, qmp, "query-pci", NULL);
-    if (rc) goto out;
-    return;
-
-out:
-    pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_qmp_query_pci_cb(libxl__egc *egc,
-                                     libxl__ev_qmp *qmp,
-                                     const libxl__json_object *response,
-                                     int rc)
-{
-    EGC_GC;
-    pci_add_state *pas = CONTAINER_OF(qmp, *pas, qmp);
-    const libxl__json_object *bus = NULL;
-    char *asked_id;
-    int i, j;
-    const libxl__json_object *devices = NULL;
-    const libxl__json_object *device = NULL;
-    const libxl__json_object *o = NULL;
-    const char *id = NULL;
-    int dev_slot, dev_func;
-
-    /* Convenience aliases */
-    libxl_device_pci *pcidev = pas->pcidev;
-
-    if (rc) goto out;
-
-    /* `query-pci' returns:
-     * [
-     *   {'bus': 'int',
-     *    'devices': [
-     *       {'bus': 'int', 'slot': 'int', 'function': 'int',
-     *        'class_info': 'PciDeviceClass', 'id': 'PciDeviceId',
-     *        '*irq': 'int', 'qdev_id': 'str',
-     *        '*pci_bridge': 'PciBridgeInfo',
-     *        'regions': ['PciMemoryRegion']
-     *       }
-     *    ]
-     *   }
-     * ]
-     * (See qemu.git/qapi/ for the struct that aren't detailed here)
-     */
-
-    asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
-                         pcidev->bus, pcidev->dev, pcidev->func);
-
-    for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
-        devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
-        if (!devices) {
-            rc = ERROR_QEMU_API;
-            goto out;
-        }
-
-        for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
-             o = libxl__json_map_get("qdev_id", device, JSON_STRING);
-             if (!o) {
-                 rc = ERROR_QEMU_API;
-                 goto out;
-             }
-             id = libxl__json_object_get_string(o);
-             if (!id || strcmp(asked_id, id))
-                 continue;
-
-             o = libxl__json_map_get("slot", device, JSON_INTEGER);
-             if (!o) {
-                 rc = ERROR_QEMU_API;
-                 goto out;
-             }
-             dev_slot = libxl__json_object_get_integer(o);
-             o = libxl__json_map_get("function", device, JSON_INTEGER);
-             if (!o) {
-                 rc = ERROR_QEMU_API;
-                 goto out;
-             }
-             dev_func = libxl__json_object_get_integer(o);
-
-             pcidev->vdevfn = PCI_DEVFN(dev_slot, dev_func);
-
-             rc = 0;
-             goto out;
-        }
-    }
-
-    rc = ERROR_FAIL;
-    LOGD(ERROR, qmp->domid,
-         "PCI device id '%s' wasn't found in QEMU's 'query-pci' response.",
-         asked_id);
-
-out:
-    if (rc == ERROR_QEMU_API) {
-        LOGD(ERROR, qmp->domid,
-             "Unexpected response to QMP cmd 'query-pci', received:\n%s",
-             JSON(response));
-    }
-    pci_add_dm_done(egc, pas, rc); /* must be last */
-}
-
-static void pci_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                            const struct timeval *requested_abs,
-                            int rc)
-{
-    pci_add_state *pas = CONTAINER_OF(ev, *pas, timeout);
-
-    pci_add_dm_done(egc, pas, rc);
-}
-
-static void pci_add_dm_done(libxl__egc *egc,
-                            pci_add_state *pas,
-                            int rc)
-{
-    STATE_AO_GC(pas->aodev->ao);
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    libxl_domid domid = pas->pci_domid;
-    char *sysfs_path;
-    FILE *f;
-    unsigned long long start, end, flags, size;
-    int irq, i;
-    int r;
-    uint32_t flag = XEN_DOMCTL_DEV_RDM_RELAXED;
-    uint32_t domainid = domid;
-    bool isstubdom = libxl_is_stubdom(ctx, domid, &domainid);
-
-    /* Convenience aliases */
-    bool starting = pas->starting;
-    libxl_device_pci *pcidev = pas->pcidev;
-    bool hvm = libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM;
-
-    libxl__ev_qmp_dispose(gc, &pas->qmp);
-
-    if (rc) goto out;
-
-    /* stubdomain is always running by now, even at create time */
-    if (isstubdom)
-        starting = false;
-
-    sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
-                           pcidev->bus, pcidev->dev, pcidev->func);
-    f = fopen(sysfs_path, "r");
-    start = end = flags = size = 0;
-    irq = 0;
-
-    if (f == NULL) {
-        LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
-        if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3)
-            continue;
-        size = end - start + 1;
-        if (start) {
-            if (flags & PCI_BAR_IO) {
-                r = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
-                if (r < 0) {
-                    LOGED(ERROR, domainid,
-                          "xc_domain_ioport_permission 0x%llx/0x%llx (error %d)",
-                          start, size, r);
-                    fclose(f);
-                    rc = ERROR_FAIL;
-                    goto out;
-                }
-            } else {
-                r = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
-                                                (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
-                if (r < 0) {
-                    LOGED(ERROR, domainid,
-                          "xc_domain_iomem_permission 0x%llx/0x%llx (error %d)",
-                          start, size, r);
-                    fclose(f);
-                    rc = ERROR_FAIL;
-                    goto out;
-                }
-            }
-        }
-    }
-    fclose(f);
-    sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
-                                pcidev->bus, pcidev->dev, pcidev->func);
-    f = fopen(sysfs_path, "r");
-    if (f == NULL) {
-        LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
-        goto out_no_irq;
-    }
-    if ((fscanf(f, "%u", &irq) == 1) && irq) {
-        r = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
-        if (r < 0) {
-            LOGED(ERROR, domainid, "xc_physdev_map_pirq irq=%d (error=%d)",
-                  irq, r);
-            fclose(f);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        r = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
-        if (r < 0) {
-            LOGED(ERROR, domainid,
-                  "xc_domain_irq_permission irq=%d (error=%d)", irq, r);
-            fclose(f);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-    fclose(f);
-
-    /* Don't restrict writes to the PCI config space from this VM */
-    if (pcidev->permissive) {
-        if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/permissive",
-                             pcidev) < 0 ) {
-            LOGD(ERROR, domainid, "Setting permissive for device");
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-out_no_irq:
-    if (!isstubdom) {
-        if (pcidev->rdm_policy == LIBXL_RDM_RESERVE_POLICY_STRICT) {
-            flag &= ~XEN_DOMCTL_DEV_RDM_RELAXED;
-        } else if (pcidev->rdm_policy != LIBXL_RDM_RESERVE_POLICY_RELAXED) {
-            LOGED(ERROR, domainid, "unknown rdm check flag.");
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        r = xc_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev), flag);
-        if (r < 0 && (hvm || errno != ENOSYS)) {
-            LOGED(ERROR, domainid, "xc_assign_device failed");
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-
-    if (!starting && !libxl_get_stubdom_id(CTX, domid))
-        rc = libxl__device_pci_add_xenstore(gc, domid, pcidev, starting);
-    else
-        rc = 0;
-out:
-    libxl__ev_time_deregister(gc, &pas->timeout);
-    pas->callback(egc, pas, rc);
-}
-
-static int libxl__device_pci_reset(libxl__gc *gc, unsigned int domain, unsigned int bus,
-                                   unsigned int dev, unsigned int func)
-{
-    char *reset;
-    int fd, rc;
-
-    reset = GCSPRINTF("%s/do_flr", SYSFS_PCIBACK_DRIVER);
-    fd = open(reset, O_WRONLY);
-    if (fd >= 0) {
-        char *buf = GCSPRINTF(PCI_BDF, domain, bus, dev, func);
-        rc = write(fd, buf, strlen(buf));
-        if (rc < 0)
-            LOGD(ERROR, domain, "write to %s returned %d", reset, rc);
-        close(fd);
-        return rc < 0 ? rc : 0;
-    }
-    if (errno != ENOENT)
-        LOGED(ERROR, domain, "Failed to access pciback path %s", reset);
-    reset = GCSPRINTF("%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func);
-    fd = open(reset, O_WRONLY);
-    if (fd >= 0) {
-        rc = write(fd, "1", 1);
-        if (rc < 0)
-            LOGED(ERROR, domain, "write to %s returned %d", reset, rc);
-        close(fd);
-        return rc < 0 ? rc : 0;
-    }
-    if (errno == ENOENT) {
-        LOGD(ERROR, domain,
-             "The kernel doesn't support reset from sysfs for PCI device "PCI_BDF,
-             domain, bus, dev, func);
-    } else {
-        LOGED(ERROR, domain, "Failed to access reset path %s", reset);
-    }
-    return -1;
-}
-
-int libxl__device_pci_setdefault(libxl__gc *gc, uint32_t domid,
-                                 libxl_device_pci *pci, bool hotplug)
-{
-    /* We'd like to force reserve rdm specific to a device by default.*/
-    if (pci->rdm_policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
-        pci->rdm_policy = LIBXL_RDM_RESERVE_POLICY_STRICT;
-    return 0;
-}
-
-int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid,
-                         libxl_device_pci *pcidev,
-                         const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__ao_device *aodev;
-
-    GCNEW(aodev);
-    libxl__prepare_ao_device(ao, aodev);
-    aodev->action = LIBXL__DEVICE_ACTION_ADD;
-    aodev->callback = device_addrm_aocomplete;
-    aodev->update_json = true;
-    libxl__device_pci_add(egc, domid, pcidev, false, aodev);
-    return AO_INPROGRESS;
-}
-
-static int libxl_pcidev_assignable(libxl_ctx *ctx, libxl_device_pci *pcidev)
-{
-    libxl_device_pci *pcidevs;
-    int num, i;
-
-    pcidevs = libxl_device_pci_assignable_list(ctx, &num);
-    for (i = 0; i < num; i++) {
-        if (pcidevs[i].domain == pcidev->domain &&
-            pcidevs[i].bus == pcidev->bus &&
-            pcidevs[i].dev == pcidev->dev &&
-            pcidevs[i].func == pcidev->func)
-            break;
-    }
-    free(pcidevs);
-    return i != num;
-}
-
-static void device_pci_add_stubdom_wait(libxl__egc *egc,
-    pci_add_state *pas, int rc);
-static void device_pci_add_stubdom_ready(libxl__egc *egc,
-    libxl__ev_devstate *ds, int rc);
-static void device_pci_add_stubdom_done(libxl__egc *egc,
-    pci_add_state *, int rc);
-static void device_pci_add_done(libxl__egc *egc,
-    pci_add_state *, int rc);
-
-void libxl__device_pci_add(libxl__egc *egc, uint32_t domid,
-                           libxl_device_pci *pcidev, bool starting,
-                           libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    libxl_device_pci *assigned;
-    int num_assigned, rc;
-    int stubdomid = 0;
-    pci_add_state *pas;
-
-    /* Store *pcidev to be used by callbacks */
-    aodev->device_config = pcidev;
-    aodev->device_type = &libxl__pcidev_devtype;
-
-    GCNEW(pas);
-    pas->aodev = aodev;
-    pas->domid = domid;
-    pas->starting = starting;
-    pas->callback = device_pci_add_stubdom_done;
-
-    if (libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
-        rc = xc_test_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev));
-        if (rc) {
-            LOGD(ERROR, domid,
-                 "PCI device %04x:%02x:%02x.%u %s?",
-                 pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
-                 errno == EOPNOTSUPP ? "cannot be assigned - no IOMMU"
-                 : "already assigned to a different guest");
-            goto out;
-        }
-    }
-
-    rc = libxl__device_pci_setdefault(gc, domid, pcidev, !starting);
-    if (rc) goto out;
-
-    if (pcidev->seize && !pciback_dev_is_assigned(gc, pcidev)) {
-        rc = libxl__device_pci_assignable_add(gc, pcidev, 1);
-        if ( rc )
-            goto out;
-    }
-
-    if (!libxl_pcidev_assignable(ctx, pcidev)) {
-        LOGD(ERROR, domid, "PCI device %x:%x:%x.%x is not assignable",
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = get_all_assigned_devices(gc, &assigned, &num_assigned);
-    if ( rc ) {
-        LOGD(ERROR, domid,
-             "cannot determine if device is assigned, refusing to continue");
-        goto out;
-    }
-    if ( is_pcidev_in_array(assigned, num_assigned, pcidev->domain,
-                     pcidev->bus, pcidev->dev, pcidev->func) ) {
-        LOGD(ERROR, domid, "PCI device already attached to a domain");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-
-    stubdomid = libxl_get_stubdom_id(ctx, domid);
-    if (stubdomid != 0) {
-        libxl_device_pci *pcidev_s;
-
-        GCNEW(pcidev_s);
-        libxl_device_pci_init(pcidev_s);
-        libxl_device_pci_copy(CTX, pcidev_s, pcidev);
-        pas->callback = device_pci_add_stubdom_wait;
-
-        do_pci_add(egc, stubdomid, pcidev_s, pas); /* must be last */
-        return;
-    }
-
-    device_pci_add_stubdom_done(egc, pas, 0); /* must be last */
-    return;
-
-out:
-    device_pci_add_done(egc, pas, rc); /* must be last */
-}
-
-static void device_pci_add_stubdom_wait(libxl__egc *egc,
-                                        pci_add_state *pas,
-                                        int rc)
-{
-    libxl__ao_device *aodev = pas->aodev;
-    STATE_AO_GC(aodev->ao);
-    int stubdomid = libxl_get_stubdom_id(CTX, pas->domid);
-    char *state_path;
-
-    if (rc) goto out;
-
-    /* Wait for the device actually being connected, otherwise device model
-     * running there will fail to find the device. */
-    state_path = GCSPRINTF("%s/state",
-            libxl__domain_device_backend_path(gc, 0, stubdomid, 0,
-                                              LIBXL__DEVICE_KIND_PCI));
-    rc = libxl__ev_devstate_wait(ao, &pas->pciback_ds,
-            device_pci_add_stubdom_ready,
-            state_path, XenbusStateConnected,
-            LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
-    if (rc) goto out;
-    return;
-out:
-    device_pci_add_done(egc, pas, rc); /* must be last */
-}
-
-static void device_pci_add_stubdom_ready(libxl__egc *egc,
-                                         libxl__ev_devstate *ds,
-                                         int rc)
-{
-    pci_add_state *pas = CONTAINER_OF(ds, *pas, pciback_ds);
-
-    device_pci_add_stubdom_done(egc, pas, rc); /* must be last */
-}
-
-static void device_pci_add_stubdom_done(libxl__egc *egc,
-                                        pci_add_state *pas,
-                                        int rc)
-{
-    STATE_AO_GC(pas->aodev->ao);
-    unsigned int orig_vdev, pfunc_mask;
-    int i;
-
-    /* Convenience aliases */
-    libxl__ao_device *aodev = pas->aodev;
-    libxl_domid domid = pas->domid;
-    libxl_device_pci *pcidev = aodev->device_config;
-
-    if (rc) goto out;
-
-    orig_vdev = pcidev->vdevfn & ~7U;
-
-    if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
-        if ( !(pcidev->vdevfn >> 3) ) {
-            LOGD(ERROR, domid, "Must specify a v-slot for multi-function devices");
-            rc = ERROR_INVAL;
-            goto out;
-        }
-        if ( pci_multifunction_check(gc, pcidev, &pfunc_mask) ) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        pcidev->vfunc_mask &= pfunc_mask;
-        /* so now vfunc_mask == pfunc_mask */
-    }else{
-        pfunc_mask = (1 << pcidev->func);
-    }
-
-    for(rc = 0, i = 7; i >= 0; --i) {
-        if ( (1 << i) & pfunc_mask ) {
-            if ( pcidev->vfunc_mask == pfunc_mask ) {
-                pcidev->func = i;
-                pcidev->vdevfn = orig_vdev | i;
-            }else{
-                /* if not passing through multiple devices in a block make
-                 * sure that virtual function number 0 is always used otherwise
-                 * guest won't see the device
-                 */
-                pcidev->vdevfn = orig_vdev;
-            }
-            pas->callback = device_pci_add_done;
-            do_pci_add(egc, domid, pcidev, pas); /* must be last */
-            return;
-        }
-    }
-
-out:
-    device_pci_add_done(egc, pas, rc);
-}
-
-static void device_pci_add_done(libxl__egc *egc,
-                                pci_add_state *pas,
-                                int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = pas->aodev;
-    libxl_domid domid = pas->domid;
-    libxl_device_pci *pcidev = aodev->device_config;
-
-    if (rc) {
-        LOGD(ERROR, domid,
-             "libxl__device_pci_add  failed for "
-             "PCI device %x:%x:%x.%x (rc %d)",
-             pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func,
-             rc);
-    }
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-typedef struct {
-    libxl__multidev multidev;
-    libxl__ao_device *outer_aodev;
-    libxl_domain_config *d_config;
-    libxl_domid domid;
-} add_pcidevs_state;
-
-static void add_pcidevs_done(libxl__egc *, libxl__multidev *, int rc);
-
-static void libxl__add_pcidevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
-                               libxl_domain_config *d_config,
-                               libxl__multidev *multidev)
-{
-    AO_GC;
-    add_pcidevs_state *apds;
-    int i;
-
-    /* We need to start a new multidev in order to be able to execute
-     * libxl__create_pci_backend only once. */
-
-    GCNEW(apds);
-    apds->outer_aodev = libxl__multidev_prepare(multidev);
-    apds->d_config = d_config;
-    apds->domid = domid;
-    apds->multidev.callback = add_pcidevs_done;
-    libxl__multidev_begin(ao, &apds->multidev);
-
-    for (i = 0; i < d_config->num_pcidevs; i++) {
-        libxl__ao_device *aodev = libxl__multidev_prepare(&apds->multidev);
-        libxl__device_pci_add(egc, domid, &d_config->pcidevs[i],
-                              true, aodev);
-    }
-
-    libxl__multidev_prepared(egc, &apds->multidev, 0);
-}
-
-static void add_pcidevs_done(libxl__egc *egc, libxl__multidev *multidev,
-                             int rc)
-{
-    EGC_GC;
-    add_pcidevs_state *apds = CONTAINER_OF(multidev, *apds, multidev);
-
-    /* Convenience aliases */
-    libxl_domain_config *d_config = apds->d_config;
-    libxl_domid domid = apds->domid;
-    libxl__ao_device *aodev = apds->outer_aodev;
-
-    if (rc) goto out;
-
-    if (d_config->num_pcidevs > 0 && !libxl_get_stubdom_id(CTX, domid)) {
-        rc = libxl__create_pci_backend(gc, domid, d_config->pcidevs,
-            d_config->num_pcidevs);
-        if (rc < 0) {
-            LOGD(ERROR, domid, "libxl_create_pci_backend failed: %d", rc);
-            goto out;
-        }
-    }
-
-out:
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-static int qemu_pci_remove_xenstore(libxl__gc *gc, uint32_t domid,
-                                    libxl_device_pci *pcidev, int force)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *state;
-    char *path;
-    uint32_t dm_domid;
-
-    dm_domid = libxl_get_stubdom_id(CTX, domid);
-
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
-    state = libxl__xs_read(gc, XBT_NULL, path);
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter");
-    libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF, pcidev->domain,
-                     pcidev->bus, pcidev->dev, pcidev->func);
-
-    /* Remove all functions at once atomically by only signalling
-     * device-model for function 0 */
-    if ( !force && (pcidev->vdevfn & 0x7) == 0 ) {
-        libxl__qemu_traditional_cmd(gc, domid, "pci-rem");
-        if (libxl__wait_for_device_model_deprecated(gc, domid, "pci-removed",
-                                         NULL, NULL, NULL) < 0) {
-            LOGD(ERROR, domid, "Device Model didn't respond in time");
-            /* This depends on guest operating system acknowledging the
-             * SCI, if it doesn't respond in time then we may wish to
-             * force the removal.
-             */
-            return ERROR_FAIL;
-        }
-    }
-    path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
-    xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
-
-    return 0;
-}
-
-typedef struct pci_remove_state {
-    libxl__ao_device *aodev;
-    libxl_domid domid;
-    libxl_device_pci *pcidev;
-    bool force;
-    bool hvm;
-    unsigned int orig_vdev;
-    unsigned int pfunc_mask;
-    int next_func;
-    libxl__ao_device stubdom_aodev;
-    libxl__xswait_state xswait;
-    libxl__ev_qmp qmp;
-    libxl__ev_time timeout;
-    libxl__ev_time retry_timer;
-} pci_remove_state;
-
-static void libxl__device_pci_remove_common(libxl__egc *egc,
-    uint32_t domid, libxl_device_pci *pcidev, bool force,
-    libxl__ao_device *aodev);
-static void device_pci_remove_common_next(libxl__egc *egc,
-    pci_remove_state *prs, int rc);
-
-static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
-    libxl__xswait_state *xswa, int rc, const char *state);
-static void pci_remove_qmp_device_del(libxl__egc *egc,
-    pci_remove_state *prs);
-static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void pci_remove_qmp_query_cb(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
-static void pci_remove_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void pci_remove_detatched(libxl__egc *egc,
-    pci_remove_state *prs, int rc);
-static void pci_remove_stubdom_done(libxl__egc *egc,
-    libxl__ao_device *aodev);
-static void pci_remove_done(libxl__egc *egc,
-    pci_remove_state *prs, int rc);
-
-static void do_pci_remove(libxl__egc *egc, uint32_t domid,
-                          libxl_device_pci *pcidev, int force,
-                          pci_remove_state *prs)
-{
-    STATE_AO_GC(prs->aodev->ao);
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    libxl_device_pci *assigned;
-    libxl_domain_type type = libxl__domain_type(gc, domid);
-    int rc, num;
-    uint32_t domainid = domid;
-
-    assigned = libxl_device_pci_list(ctx, domid, &num);
-    if (assigned == NULL) {
-        rc = ERROR_FAIL;
-        goto out_fail;
-    }
-    libxl__ptr_add(gc, assigned);
-
-    rc = ERROR_INVAL;
-    if ( !is_pcidev_in_array(assigned, num, pcidev->domain,
-                      pcidev->bus, pcidev->dev, pcidev->func) ) {
-        LOGD(ERROR, domainid, "PCI device not attached to this domain");
-        goto out_fail;
-    }
-
-    rc = ERROR_FAIL;
-    if (type == LIBXL_DOMAIN_TYPE_HVM) {
-        prs->hvm = true;
-        switch (libxl__device_model_version_running(gc, domid)) {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            prs->xswait.ao = ao;
-            prs->xswait.what = "Device Model";
-            prs->xswait.path = DEVICE_MODEL_XS_PATH(gc,
-                libxl_get_stubdom_id(CTX, domid), domid, "/state");
-            prs->xswait.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000;
-            prs->xswait.callback = pci_remove_qemu_trad_watch_state_cb;
-            rc = libxl__xswait_start(gc, &prs->xswait);
-            if (rc) goto out_fail;
-            return;
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            pci_remove_qmp_device_del(egc, prs); /* must be last */
-            return;
-        default:
-            rc = ERROR_INVAL;
-            goto out_fail;
-        }
-    } else {
-        assert(type == LIBXL_DOMAIN_TYPE_PV);
-
-        char *sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
-                                     pcidev->bus, pcidev->dev, pcidev->func);
-        FILE *f = fopen(sysfs_path, "r");
-        unsigned int start = 0, end = 0, flags = 0, size = 0;
-        int irq = 0;
-        int i;
-
-        if (f == NULL) {
-            LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
-            goto skip1;
-        }
-        for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
-            if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3)
-                continue;
-            size = end - start + 1;
-            if (start) {
-                if (flags & PCI_BAR_IO) {
-                    rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
-                    if (rc < 0)
-                        LOGED(ERROR, domainid,
-                              "xc_domain_ioport_permission error 0x%x/0x%x",
-                              start,
-                              size);
-                } else {
-                    rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
-                                                    (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
-                    if (rc < 0)
-                        LOGED(ERROR, domainid,
-                              "xc_domain_iomem_permission error 0x%x/0x%x",
-                              start,
-                              size);
-                }
-            }
-        }
-        fclose(f);
-skip1:
-        sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
-                               pcidev->bus, pcidev->dev, pcidev->func);
-        f = fopen(sysfs_path, "r");
-        if (f == NULL) {
-            LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path);
-            goto skip_irq;
-        }
-        if ((fscanf(f, "%u", &irq) == 1) && irq) {
-            rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
-            if (rc < 0) {
-                LOGED(ERROR, domainid, "xc_physdev_unmap_pirq irq=%d", irq);
-            }
-            rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
-            if (rc < 0) {
-                LOGED(ERROR, domainid, "xc_domain_irq_permission irq=%d", irq);
-            }
-        }
-        fclose(f);
-    }
-skip_irq:
-    rc = 0;
-out_fail:
-    pci_remove_detatched(egc, prs, rc); /* must be last */
-}
-
-static void pci_remove_qemu_trad_watch_state_cb(libxl__egc *egc,
-                                                libxl__xswait_state *xswa,
-                                                int rc,
-                                                const char *state)
-{
-    pci_remove_state *prs = CONTAINER_OF(xswa, *prs, xswait);
-    STATE_AO_GC(prs->aodev->ao);
-
-    /* Convenience aliases */
-    libxl_domid domid = prs->domid;
-    libxl_device_pci *const pcidev = prs->pcidev;
-
-    rc = check_qemu_running(gc, domid, xswa, rc, state);
-    if (rc == ERROR_NOT_READY)
-        return;
-    if (rc)
-        goto out;
-
-    rc = qemu_pci_remove_xenstore(gc, domid, pcidev, prs->force);
-
-out:
-    pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_device_del(libxl__egc *egc,
-                                      pci_remove_state *prs)
-{
-    STATE_AO_GC(prs->aodev->ao);
-    libxl__json_object *args = NULL;
-    int rc;
-
-    /* Convenience aliases */
-    libxl_device_pci *const pcidev = prs->pcidev;
-
-    rc = libxl__ev_time_register_rel(ao, &prs->timeout,
-                                     pci_remove_timeout,
-                                     LIBXL_QMP_CMD_TIMEOUT * 1000);
-    if (rc) goto out;
-
-    QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
-                           pcidev->bus, pcidev->dev, pcidev->func);
-    prs->qmp.callback = pci_remove_qmp_device_del_cb;
-    rc = libxl__ev_qmp_send(egc, &prs->qmp, "device_del", args);
-    if (rc) goto out;
-    return;
-
-out:
-    pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_device_del_cb(libxl__egc *egc,
-                                         libxl__ev_qmp *qmp,
-                                         const libxl__json_object *response,
-                                         int rc)
-{
-    EGC_GC;
-    pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
-
-    if (rc) goto out;
-
-    /* Now that the command is sent, we want to wait until QEMU has
-     * confirmed that the device is removed. */
-    /* TODO: Instead of using a poll loop { ev_timer ; query-pci }, it
-     * could be possible to listen to events sent by QEMU via QMP in order
-     * to wait for the passthrough pci-device to be removed from QEMU.  */
-    pci_remove_qmp_retry_timer_cb(egc, &prs->retry_timer, NULL,
-                                  ERROR_TIMEDOUT);
-    return;
-
-out:
-    pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_retry_timer_cb(libxl__egc *egc, libxl__ev_time *ev,
-                                          const struct timeval *requested_abs,
-                                          int rc)
-{
-    EGC_GC;
-    pci_remove_state *prs = CONTAINER_OF(ev, *prs, retry_timer);
-
-    prs->qmp.callback = pci_remove_qmp_query_cb;
-    rc = libxl__ev_qmp_send(egc, &prs->qmp, "query-pci", NULL);
-    if (rc) goto out;
-    return;
-
-out:
-    pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_qmp_query_cb(libxl__egc *egc,
-                                    libxl__ev_qmp *qmp,
-                                    const libxl__json_object *response,
-                                    int rc)
-{
-    EGC_GC;
-    pci_remove_state *prs = CONTAINER_OF(qmp, *prs, qmp);
-    const libxl__json_object *bus = NULL;
-    const char *asked_id;
-    int i, j;
-
-    /* Convenience aliases */
-    libxl__ao *const ao = prs->aodev->ao;
-    libxl_device_pci *const pcidev = prs->pcidev;
-
-    if (rc) goto out;
-
-    libxl__ev_qmp_dispose(gc, qmp);
-
-    asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
-                         pcidev->bus, pcidev->dev, pcidev->func);
-
-    /* query-pci response:
-     * [{ 'devices': [ 'qdev_id': 'str', ...  ], ... }]
-     * */
-
-    for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
-        const libxl__json_object *devices = NULL;
-        const libxl__json_object *device = NULL;
-        const libxl__json_object *o = NULL;
-        const char *id = NULL;
-
-        devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
-        if (!devices) {
-            rc = ERROR_QEMU_API;
-            goto out;
-        }
-
-        for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
-             o = libxl__json_map_get("qdev_id", device, JSON_STRING);
-             if (!o) {
-                 rc = ERROR_QEMU_API;
-                 goto out;
-             }
-             id = libxl__json_object_get_string(o);
-
-             if (id && !strcmp(asked_id, id)) {
-                 /* Device still in QEMU, need to wait longuer. */
-                 rc = libxl__ev_time_register_rel(ao, &prs->retry_timer,
-                     pci_remove_qmp_retry_timer_cb, 1000);
-                 if (rc) goto out;
-                 return;
-             }
-        }
-    }
-
-out:
-    pci_remove_detatched(egc, prs, rc); /* must be last */
-}
-
-static void pci_remove_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                               const struct timeval *requested_abs,
-                               int rc)
-{
-    EGC_GC;
-    pci_remove_state *prs = CONTAINER_OF(ev, *prs, timeout);
-
-    /* Convenience aliases */
-    libxl_device_pci *const pcidev = prs->pcidev;
-
-    LOGD(WARN, prs->domid, "timed out waiting for DM to remove "
-         PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func);
-
-    /* If we timed out, we might still want to keep destroying the device
-     * (when force==true), so let the next function decide what to do on
-     * error */
-    pci_remove_detatched(egc, prs, rc);
-}
-
-static void pci_remove_detatched(libxl__egc *egc,
-                                 pci_remove_state *prs,
-                                 int rc)
-{
-    STATE_AO_GC(prs->aodev->ao);
-    int stubdomid = 0;
-    uint32_t domainid = prs->domid;
-    bool isstubdom;
-
-    /* Convenience aliases */
-    libxl_device_pci *const pcidev = prs->pcidev;
-    libxl_domid domid = prs->domid;
-
-    /* Cleaning QMP states ASAP */
-    libxl__ev_qmp_dispose(gc, &prs->qmp);
-    libxl__ev_time_deregister(gc, &prs->timeout);
-    libxl__ev_time_deregister(gc, &prs->retry_timer);
-
-    if (rc && !prs->force)
-        goto out;
-
-    isstubdom = libxl_is_stubdom(CTX, domid, &domainid);
-
-    /* don't do multiple resets while some functions are still passed through */
-    if ( (pcidev->vdevfn & 0x7) == 0 ) {
-        libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
-    }
-
-    if (!isstubdom) {
-        rc = xc_deassign_device(CTX->xch, domid, pcidev_encode_bdf(pcidev));
-        if (rc < 0 && (prs->hvm || errno != ENOSYS))
-            LOGED(ERROR, domainid, "xc_deassign_device failed");
-    }
-
-    stubdomid = libxl_get_stubdom_id(CTX, domid);
-    if (stubdomid != 0) {
-        libxl_device_pci *pcidev_s;
-        libxl__ao_device *const stubdom_aodev = &prs->stubdom_aodev;
-
-        GCNEW(pcidev_s);
-        libxl_device_pci_init(pcidev_s);
-        libxl_device_pci_copy(CTX, pcidev_s, pcidev);
-
-        libxl__prepare_ao_device(ao, stubdom_aodev);
-        stubdom_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-        stubdom_aodev->callback = pci_remove_stubdom_done;
-        stubdom_aodev->update_json = prs->aodev->update_json;
-        libxl__device_pci_remove_common(egc, stubdomid, pcidev_s,
-                                        prs->force, stubdom_aodev);
-        return;
-    }
-
-    rc = 0;
-out:
-    pci_remove_done(egc, prs, rc);
-}
-
-static void pci_remove_stubdom_done(libxl__egc *egc,
-                                    libxl__ao_device *aodev)
-{
-    pci_remove_state *prs = CONTAINER_OF(aodev, *prs, stubdom_aodev);
-
-    pci_remove_done(egc, prs, 0);
-}
-
-static void pci_remove_done(libxl__egc *egc,
-                            pci_remove_state *prs,
-                            int rc)
-{
-    EGC_GC;
-
-    if (rc) goto out;
-
-    libxl__device_pci_remove_xenstore(gc, prs->domid, prs->pcidev);
-out:
-    device_pci_remove_common_next(egc, prs, rc);
-}
-
-static void libxl__device_pci_remove_common(libxl__egc *egc,
-                                            uint32_t domid,
-                                            libxl_device_pci *pcidev,
-                                            bool force,
-                                            libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    int rc;
-    pci_remove_state *prs;
-
-    GCNEW(prs);
-    prs->aodev = aodev;
-    prs->domid = domid;
-    prs->pcidev = pcidev;
-    prs->force = force;
-    libxl__xswait_init(&prs->xswait);
-    libxl__ev_qmp_init(&prs->qmp);
-    prs->qmp.ao = prs->aodev->ao;
-    prs->qmp.domid = prs->domid;
-    prs->qmp.payload_fd = -1;
-    libxl__ev_time_init(&prs->timeout);
-    libxl__ev_time_init(&prs->retry_timer);
-
-    prs->orig_vdev = pcidev->vdevfn & ~7U;
-
-    if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
-        if ( pci_multifunction_check(gc, pcidev, &prs->pfunc_mask) ) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        pcidev->vfunc_mask &= prs->pfunc_mask;
-    }else{
-        prs->pfunc_mask = (1 << pcidev->func);
-    }
-
-    rc = 0;
-    prs->next_func = 7;
-out:
-    device_pci_remove_common_next(egc, prs, rc);
-}
-
-static void device_pci_remove_common_next(libxl__egc *egc,
-                                          pci_remove_state *prs,
-                                          int rc)
-{
-    EGC_GC;
-
-    /* Convenience aliases */
-    libxl_domid domid = prs->domid;
-    libxl_device_pci *const pcidev = prs->pcidev;
-    libxl__ao_device *const aodev = prs->aodev;
-    const unsigned int pfunc_mask = prs->pfunc_mask;
-    const unsigned int orig_vdev = prs->orig_vdev;
-
-    if (rc) goto out;
-
-    while (prs->next_func >= 0) {
-        const int i = prs->next_func;
-        prs->next_func--;
-        if ( (1 << i) & pfunc_mask ) {
-            if ( pcidev->vfunc_mask == pfunc_mask ) {
-                pcidev->func = i;
-                pcidev->vdevfn = orig_vdev | i;
-            }else{
-                pcidev->vdevfn = orig_vdev;
-            }
-            do_pci_remove(egc, domid, pcidev, prs->force, prs);
-            return;
-        }
-    }
-
-    rc = 0;
-out:
-    libxl__ev_qmp_dispose(gc, &prs->qmp);
-    libxl__xswait_stop(gc, &prs->xswait);
-    libxl__ev_time_deregister(gc, &prs->timeout);
-    libxl__ev_time_deregister(gc, &prs->retry_timer);
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
-                            libxl_device_pci *pcidev,
-                            const libxl_asyncop_how *ao_how)
-
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__ao_device *aodev;
-
-    GCNEW(aodev);
-    libxl__prepare_ao_device(ao, aodev);
-    aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-    aodev->callback = device_addrm_aocomplete;
-    aodev->update_json = true;
-    libxl__device_pci_remove_common(egc, domid, pcidev, false, aodev);
-    return AO_INPROGRESS;
-}
-
-int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid,
-                             libxl_device_pci *pcidev,
-                             const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__ao_device *aodev;
-
-    GCNEW(aodev);
-    libxl__prepare_ao_device(ao, aodev);
-    aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-    aodev->callback = device_addrm_aocomplete;
-    aodev->update_json = true;
-    libxl__device_pci_remove_common(egc, domid, pcidev, true, aodev);
-    return AO_INPROGRESS;
-}
-
-static int libxl__device_pci_from_xs_be(libxl__gc *gc,
-                                        const char *be_path,
-                                        libxl_devid nr, void *data)
-{
-    char *s;
-    unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
-    libxl_device_pci *pci = data;
-
-    s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, nr));
-    sscanf(s, PCI_BDF, &domain, &bus, &dev, &func);
-
-    s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vdevfn-%d", be_path, nr));
-    if (s)
-        vdevfn = strtol(s, (char **) NULL, 16);
-
-    pcidev_struct_fill(pci, domain, bus, dev, func, vdevfn);
-
-    s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/opts-%d", be_path, nr));
-    if (s) {
-        char *saveptr;
-        char *p = strtok_r(s, ",=", &saveptr);
-        do {
-            while (*p == ' ')
-                p++;
-            if (!strcmp(p, "msitranslate")) {
-                p = strtok_r(NULL, ",=", &saveptr);
-                pci->msitranslate = atoi(p);
-            } else if (!strcmp(p, "power_mgmt")) {
-                p = strtok_r(NULL, ",=", &saveptr);
-                pci->power_mgmt = atoi(p);
-            } else if (!strcmp(p, "permissive")) {
-                p = strtok_r(NULL, ",=", &saveptr);
-                pci->permissive = atoi(p);
-            }
-        } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
-    }
-
-    return 0;
-}
-
-static int libxl__device_pci_get_num(libxl__gc *gc, const char *be_path,
-                                     unsigned int *num)
-{
-    char *num_devs;
-    int rc = 0;
-
-    num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path));
-    if (!num_devs)
-        rc = ERROR_FAIL;
-    else
-        *num = atoi(num_devs);
-
-    return rc;
-}
-
-libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid, int *num)
-{
-    GC_INIT(ctx);
-    char *be_path;
-    unsigned int n, i;
-    libxl_device_pci *pcidevs = NULL;
-
-    *num = 0;
-
-    be_path = libxl__domain_device_backend_path(gc, 0, domid, 0,
-                                                LIBXL__DEVICE_KIND_PCI);
-    if (libxl__device_pci_get_num(gc, be_path, &n))
-        goto out;
-
-    pcidevs = calloc(n, sizeof(libxl_device_pci));
-
-    for (i = 0; i < n; i++)
-        libxl__device_pci_from_xs_be(gc, be_path, i, pcidevs + i);
-
-    *num = n;
-out:
-    GC_FREE;
-    return pcidevs;
-}
-
-void libxl__device_pci_destroy_all(libxl__egc *egc, uint32_t domid,
-                                   libxl__multidev *multidev)
-{
-    STATE_AO_GC(multidev->ao);
-    libxl_device_pci *pcidevs;
-    int num, i;
-
-    pcidevs = libxl_device_pci_list(CTX, domid, &num);
-    if ( pcidevs == NULL )
-        return;
-    libxl__ptr_add(gc, pcidevs);
-
-    for (i = 0; i < num; i++) {
-        /* Force remove on shutdown since, on HVM, qemu will not always
-         * respond to SCI interrupt because the guest kernel has shut down the
-         * devices by the time we even get here!
-         */
-        libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
-        libxl__device_pci_remove_common(egc, domid, pcidevs + i, true,
-                                        aodev);
-    }
-}
-
-int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid,
-                                      libxl_domain_config *const d_config)
-{
-    int i, ret;
-
-    if (!libxl_defbool_val(d_config->b_info.u.hvm.gfx_passthru))
-        return 0;
-
-    for (i = 0 ; i < d_config->num_pcidevs ; i++) {
-        uint64_t vga_iomem_start = 0xa0000 >> XC_PAGE_SHIFT;
-        uint32_t stubdom_domid;
-        libxl_device_pci *pcidev = &d_config->pcidevs[i];
-        unsigned long pci_device_class;
-
-        if (sysfs_dev_get_class(gc, pcidev, &pci_device_class))
-            continue;
-        if (pci_device_class != 0x030000) /* VGA class */
-            continue;
-
-        stubdom_domid = libxl_get_stubdom_id(CTX, domid);
-        ret = xc_domain_iomem_permission(CTX->xch, stubdom_domid,
-                                         vga_iomem_start, 0x20, 1);
-        if (ret < 0) {
-            LOGED(ERROR, domid,
-                  "failed to give stubdom%d access to iomem range "
-                  "%"PRIx64"-%"PRIx64" for VGA passthru",
-                  stubdom_domid,
-                  vga_iomem_start, (vga_iomem_start + 0x20 - 1));
-            return ret;
-        }
-        ret = xc_domain_iomem_permission(CTX->xch, domid,
-                                         vga_iomem_start, 0x20, 1);
-        if (ret < 0) {
-            LOGED(ERROR, domid,
-                  "failed to give dom%d access to iomem range "
-                  "%"PRIx64"-%"PRIx64" for VGA passthru",
-                  domid, vga_iomem_start, (vga_iomem_start + 0x20 - 1));
-            return ret;
-        }
-        break;
-    }
-
-    return 0;
-}
-
-static int libxl_device_pci_compare(const libxl_device_pci *d1,
-                                    const libxl_device_pci *d2)
-{
-    return COMPARE_PCI(d1, d2);
-}
-
-#define libxl__device_pci_update_devid NULL
-
-DEFINE_DEVICE_TYPE_STRUCT_X(pcidev, pci, PCI,
-    .get_num = libxl__device_pci_get_num,
-    .from_xenstore = libxl__device_pci_from_xs_be,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_psr.c b/tools/libxl/libxl_psr.c
deleted file mode 100644 (file)
index 9ced7d1..0000000
+++ /dev/null
@@ -1,562 +0,0 @@
-/*
- * Copyright (C) 2014      Intel Corporation
- * Author Dongxiao Xu <dongxiao.xu@intel.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-#include "libxl_internal.h"
-
-#include <xen-tools/libs.h>
-
-#define IA32_QM_CTR_ERROR_MASK         (0x3ul << 62)
-
-static void libxl__psr_log_err_msg(libxl__gc *gc, int err)
-{
-    char *msg;
-
-    switch (err) {
-    case ENOSYS:
-    case EOPNOTSUPP:
-        msg = "unsupported operation";
-        break;
-    case ESRCH:
-        msg = "invalid domain ID";
-        break;
-    case ENOTSOCK:
-        msg = "socket is not supported";
-        break;
-    case EFAULT:
-        msg = "failed to exchange data with Xen";
-        break;
-    default:
-        msg = "unknown error";
-        break;
-    }
-
-    LOGE(ERROR, "%s", msg);
-}
-
-static void libxl__psr_cmt_log_err_msg(libxl__gc *gc, int err)
-{
-    char *msg;
-
-    switch (err) {
-    case ENODEV:
-        msg = "CMT is not supported in this system";
-        break;
-    case EEXIST:
-        msg = "CMT is already attached to this domain";
-        break;
-    case ENOENT:
-        msg = "CMT is not attached to this domain";
-        break;
-    case EOVERFLOW:
-        msg = "no free RMID available";
-        break;
-    default:
-        libxl__psr_log_err_msg(gc, err);
-        return;
-    }
-
-    LOGE(ERROR, "%s", msg);
-}
-
-static void libxl__psr_alloc_log_err_msg(libxl__gc *gc,
-                                         int err,
-                                         libxl_psr_type type)
-{
-    /*
-     * Index is 'libxl_psr_type' so we set two 'CDP' to correspond to
-     * DATA and CODE.
-     */
-    const char * const feat_name[] = {
-        [LIBXL_PSR_CBM_TYPE_UNKNOWN] = "UNKNOWN",
-        [LIBXL_PSR_CBM_TYPE_L3_CBM] = "L3 CAT",
-        [LIBXL_PSR_CBM_TYPE_L3_CBM_CODE...LIBXL_PSR_CBM_TYPE_L3_CBM_DATA] =
-                                      "CDP",
-        [LIBXL_PSR_CBM_TYPE_L2_CBM] = "L2 CAT",
-        [LIBXL_PSR_CBM_TYPE_MBA_THRTL] = "MBA",
-    };
-    char *msg;
-
-    switch (err) {
-    case ENODEV:
-        msg = "is not supported in this system";
-        break;
-    case ENOENT:
-        msg = "is not enabled on the socket";
-        break;
-    case EOVERFLOW:
-        msg = "no free COS available";
-        break;
-    case EEXIST:
-        msg = "The same CBM is already set to this domain";
-        break;
-    case ENXIO:
-        msg = "Unable to set code or data CBM when CDP is disabled";
-        break;
-    case EINVAL:
-        msg = "Invalid input or some internal values are not expected";
-        break;
-    case ERANGE:
-        msg = "Socket number is wrong";
-        break;
-    case ENOSPC:
-        msg = "Value array exceeds the range";
-        break;
-
-    default:
-        libxl__psr_log_err_msg(gc, err);
-        return;
-    }
-
-    LOG(ERROR, "%s: %s", feat_name[type], msg);
-}
-
-static int libxl__pick_socket_cpu(libxl__gc *gc, uint32_t socketid)
-{
-    int i, nr_cpus;
-    libxl_cputopology *topology;
-    int cpu = ERROR_FAIL;
-
-    topology = libxl_get_cpu_topology(CTX, &nr_cpus);
-    if (!topology)
-        return ERROR_FAIL;
-
-    for (i = 0; i < nr_cpus; i++)
-        if (topology[i].socket == socketid) {
-            cpu = i;
-            break;
-        }
-
-    libxl_cputopology_list_free(topology, nr_cpus);
-    return cpu;
-}
-
-int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    rc = xc_psr_cmt_attach(ctx->xch, domid);
-    if (rc < 0) {
-        libxl__psr_cmt_log_err_msg(gc, errno);
-        rc = ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    rc = xc_psr_cmt_detach(ctx->xch, domid);
-    if (rc < 0) {
-        libxl__psr_cmt_log_err_msg(gc, errno);
-        rc = ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid)
-{
-    int rc;
-    uint32_t rmid;
-
-    rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
-    if (rc < 0)
-        return 0;
-
-    return !!rmid;
-}
-
-int libxl_psr_cmt_enabled(libxl_ctx *ctx)
-{
-    return xc_psr_cmt_enabled(ctx->xch);
-}
-
-int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    rc = xc_psr_cmt_get_total_rmid(ctx->xch, total_rmid);
-    if (rc < 0) {
-        libxl__psr_cmt_log_err_msg(gc, errno);
-        rc = ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx,
-                                    uint32_t socketid,
-                                    uint32_t *l3_cache_size)
-{
-    GC_INIT(ctx);
-
-    int rc;
-    int cpu = libxl__pick_socket_cpu(gc, socketid);
-
-    if (cpu < 0) {
-        LOGE(ERROR, "failed to get socket cpu");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = xc_psr_cmt_get_l3_cache_size(ctx->xch, cpu, l3_cache_size);
-    if (rc < 0) {
-        libxl__psr_cmt_log_err_msg(gc, errno);
-        rc = ERROR_FAIL;
-    }
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type)
-{
-    GC_INIT(ctx);
-    uint32_t event_mask;
-    int rc;
-
-    rc = xc_psr_cmt_get_l3_event_mask(ctx->xch, &event_mask);
-    if (rc < 0) {
-        libxl__psr_cmt_log_err_msg(gc, errno);
-        rc = 0;
-    } else {
-        rc = event_mask & (1 << (type - 1));
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_cmt_get_sample(libxl_ctx *ctx,
-                             uint32_t domid,
-                             libxl_psr_cmt_type type,
-                             uint64_t scope,
-                             uint64_t *sample_r,
-                             uint64_t *tsc_r)
-{
-    GC_INIT(ctx);
-    unsigned int rmid;
-    uint32_t upscaling_factor;
-    uint64_t monitor_data;
-    int cpu, rc;
-
-    rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid);
-    if (rc < 0 || rmid == 0) {
-        LOGED(ERROR, domid, "fail to get the domain rmid, "
-              "or domain is not attached with platform QoS monitoring service");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    cpu = libxl__pick_socket_cpu(gc, scope);
-    if (cpu < 0) {
-        LOGED(ERROR, domid, "failed to get socket cpu");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = xc_psr_cmt_get_data(ctx->xch, rmid, cpu, type - 1,
-                             &monitor_data, tsc_r);
-    if (rc < 0) {
-        LOGED(ERROR, domid, "failed to get monitoring data");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = xc_psr_cmt_get_l3_upscaling_factor(ctx->xch, &upscaling_factor);
-    if (rc < 0) {
-        LOGED(ERROR, domid, "failed to get L3 upscaling factor");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    *sample_r = monitor_data * upscaling_factor;
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx,
-                                      uint32_t domid,
-                                      uint32_t socketid,
-                                      uint32_t *l3_cache_occupancy)
-{
-    uint64_t data;
-    int rc;
-
-    rc = libxl_psr_cmt_get_sample(ctx, domid,
-                                  LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY,
-                                  socketid, &data, NULL);
-    if (rc < 0)
-        goto out;
-
-    *l3_cache_occupancy = data / 1024;
-out:
-    return rc;
-}
-
-static inline xc_psr_type libxl__psr_type_to_libxc_psr_type(
-    libxl_psr_type type)
-{
-    BUILD_BUG_ON(sizeof(libxl_psr_type) != sizeof(xc_psr_type));
-    return (xc_psr_type)type;
-}
-
-int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid,
-                          libxl_psr_cbm_type type, libxl_bitmap *target_map,
-                          uint64_t cbm)
-{
-    return libxl_psr_set_val(ctx, domid, type, target_map, cbm);
-}
-
-int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid,
-                          libxl_psr_cbm_type type, uint32_t target,
-                          uint64_t *cbm_r)
-{
-    return libxl_psr_get_val(ctx, domid, type, target, cbm_r);
-}
-
-static xc_psr_feat_type libxl__feat_type_to_libxc_feat_type(
-                            libxl_psr_feat_type type, unsigned int lvl)
-{
-    xc_psr_feat_type xc_type;
-
-    switch (type) {
-    case LIBXL_PSR_FEAT_TYPE_CAT:
-        assert(lvl == 3 || lvl == 2);
-
-        if (lvl == 3)
-            xc_type = XC_PSR_CAT_L3;
-        if (lvl == 2)
-            xc_type = XC_PSR_CAT_L2;
-        break;
-    case LIBXL_PSR_FEAT_TYPE_MBA:
-        xc_type = XC_PSR_MBA;
-        break;
-    default:
-        /* Could not happen */
-        assert(0);
-    }
-
-    return xc_type;
-}
-
-static void libxl__hw_info_to_libxl_cat_info(
-                libxl_psr_feat_type type, libxl_psr_hw_info *hw_info,
-                libxl_psr_cat_info *cat_info)
-{
-    assert(type == LIBXL_PSR_FEAT_TYPE_CAT);
-
-    cat_info->id = hw_info->id;
-    cat_info->cos_max = hw_info->u.cat.cos_max;
-    cat_info->cbm_len = hw_info->u.cat.cbm_len;
-    cat_info->cdp_enabled = hw_info->u.cat.cdp_enabled;
-}
-
-int libxl_psr_cat_get_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
-                           unsigned int *nr, unsigned int lvl)
-{
-    GC_INIT(ctx);
-    int rc;
-    unsigned int i;
-    libxl_psr_hw_info *hw_info;
-    libxl_psr_cat_info *ptr;
-
-    rc = libxl_psr_get_hw_info(ctx, LIBXL_PSR_FEAT_TYPE_CAT, lvl, nr, &hw_info);
-    if (rc)
-        goto out;
-
-    ptr = libxl__malloc(NOGC, *nr * sizeof(libxl_psr_cat_info));
-
-    for (i = 0; i < *nr; i++)
-        libxl__hw_info_to_libxl_cat_info(LIBXL_PSR_FEAT_TYPE_CAT,
-                                         &hw_info[i],
-                                         &ptr[i]);
-
-    *info = ptr;
-    libxl_psr_hw_info_list_free(hw_info, *nr);
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
-                              int *nr)
-{
-    int rc;
-    unsigned int num;
-
-    rc = libxl_psr_cat_get_info(ctx, info, &num, 3);
-    if (!rc)
-        *nr = num;
-
-    return rc;
-}
-
-void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr)
-{
-    int i;
-
-    for (i = 0; i < nr; i++)
-        libxl_psr_cat_info_dispose(&list[i]);
-    free(list);
-}
-
-int libxl_psr_set_val(libxl_ctx *ctx, uint32_t domid,
-                      libxl_psr_type type, libxl_bitmap *target_map,
-                      uint64_t val)
-{
-    GC_INIT(ctx);
-    int rc, socketid, nr_sockets;
-    xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
-
-    rc = libxl__count_physical_sockets(gc, &nr_sockets);
-    if (rc) {
-        LOG(ERROR, "failed to get system socket count");
-        goto out;
-    }
-
-    libxl_for_each_set_bit(socketid, *target_map) {
-        if (socketid >= nr_sockets)
-            break;
-
-        if (xc_psr_set_domain_data(ctx->xch, domid, xc_type,
-                                   socketid, val)) {
-            libxl__psr_alloc_log_err_msg(gc, errno, type);
-            rc = ERROR_FAIL;
-        }
-    }
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_psr_get_val(libxl_ctx *ctx, uint32_t domid,
-                      libxl_psr_type type, unsigned int target,
-                      uint64_t *val)
-{
-    GC_INIT(ctx);
-    int rc = 0;
-    xc_psr_type xc_type = libxl__psr_type_to_libxc_psr_type(type);
-
-    if (xc_psr_get_domain_data(ctx->xch, domid, xc_type,
-                               target, val)) {
-        libxl__psr_alloc_log_err_msg(gc, errno, type);
-        rc = ERROR_FAIL;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-static void libxl__xc_hw_info_to_libxl_hw_info(
-                libxl_psr_feat_type type, xc_psr_hw_info *xc_info,
-                libxl_psr_hw_info *xl_info)
-{
-    switch (type) {
-    case LIBXL_PSR_FEAT_TYPE_CAT:
-        xl_info->u.cat.cos_max = xc_info->cat.cos_max;
-        xl_info->u.cat.cbm_len = xc_info->cat.cbm_len;
-        xl_info->u.cat.cdp_enabled = xc_info->cat.cdp_enabled;
-        break;
-    case LIBXL_PSR_FEAT_TYPE_MBA:
-        xl_info->u.mba.cos_max = xc_info->mba.cos_max;
-        xl_info->u.mba.thrtl_max = xc_info->mba.thrtl_max;
-        xl_info->u.mba.linear = xc_info->mba.linear;
-        break;
-    default:
-        assert(0);
-    }
-}
-
-int libxl_psr_get_hw_info(libxl_ctx *ctx, libxl_psr_feat_type type,
-                          unsigned int lvl, unsigned int *nr,
-                          libxl_psr_hw_info **info)
-{
-    GC_INIT(ctx);
-    int rc, nr_sockets;
-    unsigned int i = 0, socketid;
-    libxl_bitmap socketmap;
-    libxl_psr_hw_info *ptr;
-    xc_psr_feat_type xc_type;
-    xc_psr_hw_info hw_info;
-
-    libxl_bitmap_init(&socketmap);
-
-    xc_type = libxl__feat_type_to_libxc_feat_type(type, lvl);
-
-    rc = libxl__count_physical_sockets(gc, &nr_sockets);
-    if (rc) {
-        LOG(ERROR, "failed to get system socket count");
-        goto out;
-    }
-
-    libxl_socket_bitmap_alloc(ctx, &socketmap, nr_sockets);
-    rc = libxl_get_online_socketmap(ctx, &socketmap);
-    if (rc) {
-        LOGE(ERROR, "failed to get available sockets");
-        goto out;
-    }
-
-    ptr = libxl__malloc(NOGC, nr_sockets * sizeof(libxl_psr_hw_info));
-
-    libxl_for_each_set_bit(socketid, socketmap) {
-        ptr[i].id = socketid;
-        if (xc_psr_get_hw_info(ctx->xch, socketid, xc_type, &hw_info)) {
-            rc = ERROR_FAIL;
-            free(ptr);
-            goto out;
-        }
-
-        libxl__xc_hw_info_to_libxl_hw_info(type, &hw_info, &ptr[i]);
-
-        i++;
-    }
-
-    *info = ptr;
-    *nr = i;
-out:
-    libxl_bitmap_dispose(&socketmap);
-    GC_FREE;
-    return rc;
-}
-
-void libxl_psr_hw_info_list_free(libxl_psr_hw_info *list, unsigned int nr)
-{
-    unsigned int i;
-
-    for (i = 0; i < nr; i++)
-        libxl_psr_hw_info_dispose(&list[i]);
-    free(list);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_pvcalls.c b/tools/libxl/libxl_pvcalls.c
deleted file mode 100644 (file)
index 870318e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018      Aporeto
- * Author Stefano Stabellini <stefano@aporeto.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__device_pvcallsif_setdefault(libxl__gc *gc, uint32_t domid,
-                                              libxl_device_pvcallsif *pvcallsif,
-                                              bool hotplug)
-{
-    return libxl__resolve_domid(gc, pvcallsif->backend_domname,
-                                &pvcallsif->backend_domid);
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(pvcallsif)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(pvcallsif)
-
-#define libxl__add_pvcallsifs NULL
-#define libxl_device_pvcallsif_list NULL
-#define libxl_device_pvcallsif_compare NULL
-
-LIBXL_DEFINE_DEVICE_REMOVE(pvcallsif)
-
-DEFINE_DEVICE_TYPE_STRUCT(pvcallsif, PVCALLS);
diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c
deleted file mode 100644 (file)
index c394000..0000000
+++ /dev/null
@@ -1,1934 +0,0 @@
-/*
- * Copyright (C) 2011      Citrix Ltd.
- * Author Anthony PERARD <anthony.perard@citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-/*
- * This file implement a client for QMP (QEMU Monitor Protocol). For the
- * Specification, see in the QEMU repository.
- *
- * WARNING - Do not trust QEMU when writing codes for new commands or when
- *           improving the client code.
- */
-
-/*
- * Logic used to send command to QEMU
- *
- * qmp_open():
- *  Will open a socket and connect to QEMU.
- *
- * qmp_next():
- *  Will read data sent by QEMU and then call qmp_handle_response() once a
- *  complete QMP message is received.
- *  The function return on timeout/error or once every data received as been
- *  processed.
- *
- * qmp_handle_response()
- *  This process json messages received from QEMU and update different list and
- *  may call callback function.
- *  `libxl__qmp_handler.wait_for_id` is reset once a message with this ID is
- *    processed.
- *  `libxl__qmp_handler.callback_list`: list with ID of command sent and
- *    optional assotiated callback function. The return value of a callback is
- *    set in context.
- *
- * qmp_send():
- *  Simply prepare a QMP command and send it to QEMU.
- *  It also add a `struct callback_id_pair` on the
- *  `libxl__qmp_handler.callback_list` via qmp_send_prepare().
- *
- * qmp_synchronous_send():
- *  This function calls qmp_send(), then wait for QEMU to reply to the command.
- *  The wait is done by calling qmp_next() over and over again until either
- *  there is a response for the command or there is an error.
- *
- *  An ID can be set for each QMP command, this is set into
- *  `libxl__qmp_handler.wait_for_id`. qmp_next will check every response's ID
- *  again this field and change the value of the field once the ID is found.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <sys/un.h>
-
-#include <yajl/yajl_gen.h>
-
-#include "_libxl_list.h"
-#include "libxl_internal.h"
-
-/* #define DEBUG_RECEIVED */
-
-#ifdef DEBUG_RECEIVED
-#  define DEBUG_REPORT_RECEIVED(dom, buf, len) \
-    LOGD(DEBUG, dom, "received: '%.*s'", len, buf)
-#else
-#  define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0)
-#endif
-
-#ifdef DEBUG_QMP_CLIENT
-#  define LOG_QMP(f, ...) LOGD(DEBUG, ev->domid, f, ##__VA_ARGS__)
-#else
-#  define LOG_QMP(f, ...)
-#endif
-
-/*
- * QMP types & constant
- */
-
-#define QMP_RECEIVE_BUFFER_SIZE 4096
-#define QMP_MAX_SIZE_RX_BUF MB(1)
-
-/*
- * qmp_callback_t is call whenever a message from QMP contain the "id"
- * associated with the callback.
- * "tree" contain the JSON tree that is in "return" of a QMP message. If QMP
- * sent an error message, "tree" will be NULL.
- */
-typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
-                              const libxl__json_object *tree,
-                              void *opaque);
-
-typedef struct qmp_request_context {
-    int rc;
-} qmp_request_context;
-
-typedef struct callback_id_pair {
-    int id;
-    qmp_callback_t callback;
-    void *opaque;
-    qmp_request_context *context;
-    LIBXL_STAILQ_ENTRY(struct callback_id_pair) next;
-} callback_id_pair;
-
-struct libxl__qmp_handler {
-    int qmp_fd;
-    bool connected;
-    time_t timeout;
-    /* wait_for_id will be used by the synchronous send function */
-    int wait_for_id;
-
-    char buffer[QMP_RECEIVE_BUFFER_SIZE + 1];
-
-    libxl_ctx *ctx;
-    uint32_t domid;
-
-    int last_id_used;
-    LIBXL_STAILQ_HEAD(callback_list, callback_id_pair) callback_list;
-    struct {
-        int major;
-        int minor;
-        int micro;
-    } version;
-};
-
-static int qmp_send(libxl__qmp_handler *qmp,
-                    const char *cmd, libxl__json_object *args,
-                    qmp_callback_t callback, void *opaque,
-                    qmp_request_context *context);
-
-static const int QMP_SOCKET_CONNECT_TIMEOUT = 5;
-
-/*
- * QMP callbacks functions
- */
-
-static int qmp_capabilities_callback(libxl__qmp_handler *qmp,
-                                     const libxl__json_object *o, void *unused)
-{
-    qmp->connected = true;
-
-    return 0;
-}
-
-/*
- * QMP commands
- */
-
-static int enable_qmp_capabilities(libxl__qmp_handler *qmp)
-{
-    return qmp_send(qmp, "qmp_capabilities", NULL,
-                    qmp_capabilities_callback, NULL, NULL);
-}
-
-/*
- * Helpers
- */
-
-static libxl__qmp_message_type qmp_response_type(const libxl__json_object *o)
-{
-    libxl__qmp_message_type type;
-    libxl__json_map_node *node = NULL;
-    int i = 0;
-
-    for (i = 0; (node = libxl__json_map_node_get(o, i)); i++) {
-        if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0)
-            return type;
-    }
-
-    return LIBXL__QMP_MESSAGE_TYPE_INVALID;
-}
-
-static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp,
-                                                  const libxl__json_object *o)
-{
-    const libxl__json_object *id_object = libxl__json_map_get("id", o,
-                                                              JSON_INTEGER);
-    int id = -1;
-    callback_id_pair *pp = NULL;
-
-    if (id_object) {
-        id = libxl__json_object_get_integer(id_object);
-
-        LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
-            if (pp->id == id) {
-                return pp;
-            }
-        }
-    }
-    return NULL;
-}
-
-static void qmp_handle_error_response(libxl__gc *gc, libxl__qmp_handler *qmp,
-                                      const libxl__json_object *resp)
-{
-    callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
-
-    resp = libxl__json_map_get("error", resp, JSON_MAP);
-    resp = libxl__json_map_get("desc", resp, JSON_STRING);
-
-    if (pp) {
-        if (pp->callback) {
-            int rc = pp->callback(qmp, NULL, pp->opaque);
-            if (pp->context) {
-                pp->context->rc = rc;
-            }
-        }
-        if (pp->id == qmp->wait_for_id) {
-            /* tell that the id have been processed */
-            qmp->wait_for_id = 0;
-        }
-        LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next);
-        free(pp);
-    }
-
-    LOGD(ERROR, qmp->domid, "received an error message from QMP server: %s",
-         libxl__json_object_get_string(resp));
-}
-
-static int qmp_handle_response(libxl__gc *gc, libxl__qmp_handler *qmp,
-                               const libxl__json_object *resp)
-{
-    libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID;
-
-    type = qmp_response_type(resp);
-    LOGD(DEBUG, qmp->domid, "message type: %s", libxl__qmp_message_type_to_string(type));
-
-    switch (type) {
-    case LIBXL__QMP_MESSAGE_TYPE_QMP: {
-        const libxl__json_object *o;
-        o = libxl__json_map_get("QMP", resp, JSON_MAP);
-        o = libxl__json_map_get("version", o, JSON_MAP);
-        o = libxl__json_map_get("qemu", o, JSON_MAP);
-        qmp->version.major = libxl__json_object_get_integer(
-            libxl__json_map_get("major", o, JSON_INTEGER));
-        qmp->version.minor = libxl__json_object_get_integer(
-            libxl__json_map_get("minor", o, JSON_INTEGER));
-        qmp->version.micro = libxl__json_object_get_integer(
-            libxl__json_map_get("micro", o, JSON_INTEGER));
-        LOGD(DEBUG, qmp->domid, "QEMU version: %d.%d.%d",
-             qmp->version.major, qmp->version.minor, qmp->version.micro);
-        /* On the greeting message from the server, enable QMP capabilities */
-        return enable_qmp_capabilities(qmp);
-    }
-    case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
-        callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
-
-        if (pp) {
-            if (pp->callback) {
-                int rc = pp->callback(qmp,
-                             libxl__json_map_get("return", resp, JSON_ANY),
-                             pp->opaque);
-                if (pp->context) {
-                    pp->context->rc = rc;
-                }
-            }
-            if (pp->id == qmp->wait_for_id) {
-                /* tell that the id have been processed */
-                qmp->wait_for_id = 0;
-            }
-            LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair,
-                                next);
-            free(pp);
-        }
-        return 0;
-    }
-    case LIBXL__QMP_MESSAGE_TYPE_ERROR:
-        qmp_handle_error_response(gc, qmp, resp);
-        return -1;
-    case LIBXL__QMP_MESSAGE_TYPE_EVENT:
-        return 0;
-    case LIBXL__QMP_MESSAGE_TYPE_INVALID:
-        return -1;
-    }
-    return 0;
-}
-
-/*
- * return values:
- *   < 0  if qemu's version <  asked version
- *   = 0  if qemu's version == asked version
- *   > 0  if qemu's version >  asked version
- */
-static int qmp_ev_qemu_compare_version(libxl__ev_qmp *ev, int major,
-                                       int minor, int micro)
-{
-#define CHECK_VERSION(level) do { \
-    if (ev->qemu_version.level > (level)) return +1; \
-    if (ev->qemu_version.level < (level)) return -1; \
-} while (0)
-
-    CHECK_VERSION(major);
-    CHECK_VERSION(minor);
-    CHECK_VERSION(micro);
-
-#undef CHECK_VERSION
-
-    return 0;
-}
-
-/*
- * Handler functions
- */
-
-static libxl__qmp_handler *qmp_init_handler(libxl__gc *gc, uint32_t domid)
-{
-    libxl__qmp_handler *qmp = NULL;
-
-    qmp = calloc(1, sizeof (libxl__qmp_handler));
-    if (qmp == NULL) {
-        LOGED(ERROR, domid, "Failed to allocate qmp_handler");
-        return NULL;
-    }
-    qmp->ctx = CTX;
-    qmp->domid = domid;
-    qmp->timeout = 5;
-
-    LIBXL_STAILQ_INIT(&qmp->callback_list);
-
-    return qmp;
-}
-
-static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path,
-                    int timeout)
-{
-    GC_INIT(qmp->ctx);
-    int ret = -1;
-    int i = 0;
-    struct sockaddr_un addr;
-
-    qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-    if (qmp->qmp_fd < 0) {
-        goto out;
-    }
-    ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1);
-    if (ret) {
-        ret = -1;
-        goto out;
-    }
-    ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1);
-    if (ret) {
-        ret = -1;
-        goto out;
-    }
-
-    ret = libxl__prepare_sockaddr_un(gc, &addr, qmp_socket_path, "QMP socket");
-    if (ret)
-        goto out;
-
-    do {
-        ret = connect(qmp->qmp_fd, (struct sockaddr *) &addr, sizeof(addr));
-        if (ret == 0)
-            break;
-        if (errno == ENOENT || errno == ECONNREFUSED) {
-            /* ENOENT       : Socket may not have shown up yet
-             * ECONNREFUSED : Leftover socket hasn't been removed yet */
-            continue;
-        }
-        ret = -1;
-        goto out;
-    } while ((++i / 5 <= timeout) && (usleep(200 * 1000) <= 0));
-
-out:
-    if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd);
-
-    GC_FREE;
-    return ret;
-}
-
-static void qmp_close(libxl__qmp_handler *qmp)
-{
-    callback_id_pair *pp = NULL;
-    callback_id_pair *tmp = NULL;
-
-    close(qmp->qmp_fd);
-    LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
-        free(tmp);
-        tmp = pp;
-    }
-    free(tmp);
-}
-
-static int qmp_next(libxl__gc *gc, libxl__qmp_handler *qmp)
-{
-    ssize_t rd;
-    char *s = NULL;
-    char *s_end = NULL;
-
-    char *incomplete = NULL;
-    size_t incomplete_size = 0;
-    int rc = 0;
-
-    do {
-        fd_set rfds;
-        int ret = 0;
-        struct timeval timeout = {
-            .tv_sec = qmp->timeout,
-            .tv_usec = 0,
-        };
-
-        FD_ZERO(&rfds);
-        FD_SET(qmp->qmp_fd, &rfds);
-
-        ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout);
-        if (ret == 0) {
-            LOGD(ERROR, qmp->domid, "timeout");
-            return -1;
-        } else if (ret < 0) {
-            if (errno == EINTR)
-                continue;
-            LOGED(ERROR, qmp->domid, "Select error");
-            return -1;
-        }
-
-        rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE);
-        if (rd == 0) {
-            LOGD(ERROR, qmp->domid, "Unexpected end of socket");
-            return -1;
-        } else if (rd < 0) {
-            LOGED(ERROR, qmp->domid, "Socket read error");
-            return rd;
-        }
-        qmp->buffer[rd] = '\0';
-
-        DEBUG_REPORT_RECEIVED(qmp->domid, qmp->buffer, (int)rd);
-
-        if (incomplete) {
-            size_t current_pos = s - incomplete;
-            incomplete = libxl__realloc(gc, incomplete,
-                                        incomplete_size + rd + 1);
-            strncat(incomplete + incomplete_size, qmp->buffer, rd);
-            s = incomplete + current_pos;
-            incomplete_size += rd;
-            s_end = incomplete + incomplete_size;
-        } else {
-            incomplete = libxl__strndup(gc, qmp->buffer, rd);
-            incomplete_size = rd;
-            s = incomplete;
-            s_end = s + rd;
-            rd = 0;
-        }
-
-        do {
-            char *end = NULL;
-
-            end = strstr(s, "\r\n");
-            if (end) {
-                libxl__json_object *o = NULL;
-
-                *end = '\0';
-
-                o = libxl__json_parse(gc, s);
-
-                if (o) {
-                    rc = qmp_handle_response(gc, qmp, o);
-                } else {
-                    LOGD(ERROR, qmp->domid, "Parse error of : %s", s);
-                    return -1;
-                }
-
-                s = end + 2;
-            } else {
-                break;
-            }
-        } while (s < s_end);
-    } while (s < s_end);
-
-    return rc;
-}
-
-static char *qmp_prepare_cmd(libxl__gc *gc, const char *cmd,
-                             const libxl__json_object *args,
-                             int id)
-{
-    yajl_gen hand = NULL;
-    /* memory for 'buf' is owned by 'hand' */
-    const unsigned char *buf;
-    libxl_yajl_length len;
-    yajl_gen_status s;
-    char *ret = NULL;
-
-    hand = libxl_yajl_gen_alloc(NULL);
-
-    if (!hand) {
-        return NULL;
-    }
-
-#if HAVE_YAJL_V2
-    /* Disable beautify for data sent to QEMU */
-    yajl_gen_config(hand, yajl_gen_beautify, 0);
-#endif
-
-    yajl_gen_map_open(hand);
-    libxl__yajl_gen_asciiz(hand, "execute");
-    libxl__yajl_gen_asciiz(hand, cmd);
-    libxl__yajl_gen_asciiz(hand, "id");
-    yajl_gen_integer(hand, id);
-    if (args) {
-        libxl__yajl_gen_asciiz(hand, "arguments");
-        libxl__json_object_to_yajl_gen(gc, hand, args);
-    }
-    yajl_gen_map_close(hand);
-
-    s = yajl_gen_get_buf(hand, &buf, &len);
-
-    if (s != yajl_gen_status_ok)
-        goto out;
-
-    ret = libxl__sprintf(gc, "%*.*s\r\n", (int)len, (int)len, buf);
-
-out:
-    yajl_gen_free(hand);
-    return ret;
-}
-
-static char *qmp_send_prepare(libxl__gc *gc, libxl__qmp_handler *qmp,
-                              const char *cmd, libxl__json_object *args,
-                              qmp_callback_t callback, void *opaque,
-                              qmp_request_context *context)
-{
-    char *buf;
-    callback_id_pair *elm;
-
-    buf = qmp_prepare_cmd(gc, cmd, args, ++qmp->last_id_used);
-
-    if (!buf) {
-        LOGD(ERROR, qmp->domid, "Failed to generate a qmp command");
-        goto out;
-    }
-
-    elm = malloc(sizeof (callback_id_pair));
-    if (elm == NULL) {
-        LOGED(ERROR, qmp->domid, "Failed to allocate a QMP callback");
-        goto out;
-    }
-    elm->id = qmp->last_id_used;
-    elm->callback = callback;
-    elm->opaque = opaque;
-    elm->context = context;
-    LIBXL_STAILQ_INSERT_TAIL(&qmp->callback_list, elm, next);
-
-    LOGD(DEBUG, qmp->domid, "next qmp command: '%s'", buf);
-
-out:
-    return buf;
-}
-
-static int qmp_send(libxl__qmp_handler *qmp,
-                    const char *cmd, libxl__json_object *args,
-                    qmp_callback_t callback, void *opaque,
-                    qmp_request_context *context)
-{
-    char *buf = NULL;
-    int rc = -1;
-    GC_INIT(qmp->ctx);
-
-    buf = qmp_send_prepare(gc, qmp, cmd, args, callback, opaque, context);
-
-    if (buf == NULL) {
-        goto out;
-    }
-
-    if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, strlen(buf),
-                            "QMP command", "QMP socket"))
-        goto out;
-
-    rc = qmp->last_id_used;
-out:
-    GC_FREE;
-    return rc;
-}
-
-static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd,
-                                libxl__json_object *args,
-                                qmp_callback_t callback, void *opaque,
-                                int ask_timeout)
-{
-    int id = 0;
-    int ret = 0;
-    GC_INIT(qmp->ctx);
-    qmp_request_context context = { .rc = 0 };
-
-    id = qmp_send(qmp, cmd, args, callback, opaque, &context);
-    if (id <= 0) {
-        return ERROR_FAIL;
-    }
-    qmp->wait_for_id = id;
-
-    while (qmp->wait_for_id == id) {
-        if ((ret = qmp_next(gc, qmp)) < 0) {
-            break;
-        }
-    }
-
-    if (qmp->wait_for_id != id && ret == 0) {
-        ret = context.rc;
-    }
-
-    GC_FREE;
-
-    return ret;
-}
-
-static void qmp_free_handler(libxl__qmp_handler *qmp)
-{
-    free(qmp);
-}
-
-/*
- * QMP Parameters Helpers
- */
-static void qmp_parameters_common_add(libxl__gc *gc,
-                                      libxl__json_object **param,
-                                      const char *name,
-                                      libxl__json_object *obj)
-{
-    libxl__json_map_node *arg = NULL;
-
-    if (!*param) {
-        *param = libxl__json_object_alloc(gc, JSON_MAP);
-    }
-
-    GCNEW(arg);
-
-    arg->map_key = libxl__strdup(gc, name);
-    arg->obj = obj;
-
-    flexarray_append((*param)->u.map, arg);
-}
-
-void libxl__qmp_param_add_string(libxl__gc *gc,
-                                 libxl__json_object **param,
-                                 const char *name, const char *argument)
-{
-    libxl__json_object *obj;
-
-    obj = libxl__json_object_alloc(gc, JSON_STRING);
-    obj->u.string = libxl__strdup(gc, argument);
-
-    qmp_parameters_common_add(gc, param, name, obj);
-}
-
-void libxl__qmp_param_add_bool(libxl__gc *gc,
-                               libxl__json_object **param,
-                               const char *name, bool b)
-{
-    libxl__json_object *obj;
-
-    obj = libxl__json_object_alloc(gc, JSON_BOOL);
-    obj->u.b = b;
-    qmp_parameters_common_add(gc, param, name, obj);
-}
-
-void libxl__qmp_param_add_integer(libxl__gc *gc,
-                                  libxl__json_object **param,
-                                  const char *name, const int i)
-{
-    libxl__json_object *obj;
-
-    obj = libxl__json_object_alloc(gc, JSON_INTEGER);
-    obj->u.i = i;
-
-    qmp_parameters_common_add(gc, param, name, obj);
-}
-
-/*
- * API
- */
-
-libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid)
-{
-    int ret = 0;
-    libxl__qmp_handler *qmp = NULL;
-    char *qmp_socket;
-
-    qmp = qmp_init_handler(gc, domid);
-    if (!qmp) return NULL;
-
-    qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
-    if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
-        LOGED(ERROR, domid, "Connection error");
-        qmp_free_handler(qmp);
-        return NULL;
-    }
-
-    LOGD(DEBUG, domid, "connected to %s", qmp_socket);
-
-    /* Wait for the response to qmp_capabilities */
-    while (!qmp->connected) {
-        if ((ret = qmp_next(gc, qmp)) < 0) {
-            break;
-        }
-    }
-
-    if (!qmp->connected) {
-        LOGD(ERROR, domid, "Failed to connect to QMP");
-        libxl__qmp_close(qmp);
-        return NULL;
-    }
-    return qmp;
-}
-
-void libxl__qmp_close(libxl__qmp_handler *qmp)
-{
-    if (!qmp)
-        return;
-    qmp_close(qmp);
-    qmp_free_handler(qmp);
-}
-
-void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid)
-{
-    char *qmp_socket;
-
-    qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
-    if (unlink(qmp_socket) == -1) {
-        if (errno != ENOENT) {
-            LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
-        }
-    }
-
-    qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid);
-    if (unlink(qmp_socket) == -1) {
-        if (errno != ENOENT) {
-            LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
-        }
-    }
-}
-
-static int qmp_run_command(libxl__gc *gc, int domid,
-                           const char *cmd, libxl__json_object *args,
-                           qmp_callback_t callback, void *opaque)
-{
-    libxl__qmp_handler *qmp = NULL;
-    int rc = 0;
-
-    qmp = libxl__qmp_initialize(gc, domid);
-    if (!qmp)
-        return ERROR_FAIL;
-
-    rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout);
-
-    libxl__qmp_close(qmp);
-    return rc;
-}
-
-int libxl__qmp_restore(libxl__gc *gc, int domid, const char *state_file)
-{
-    libxl__json_object *args = NULL;
-
-    libxl__qmp_param_add_string(gc, &args, "filename", state_file);
-
-    return qmp_run_command(gc, domid, "xen-load-devices-state", args,
-                           NULL, NULL);
-}
-
-int libxl__qmp_resume(libxl__gc *gc, int domid)
-{
-    return qmp_run_command(gc, domid, "cont", NULL, NULL, NULL);
-}
-
-int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
-                                const char *host, const char *port)
-{
-    libxl__json_object *args = NULL;
-    libxl__json_object *addr = NULL;
-    libxl__json_object *data = NULL;
-
-    /* 'addr': {
-     *   'type': 'inet',
-     *   'data': {
-     *     'host': '$nbd_host',
-     *     'port': '$nbd_port'
-     *   }
-     * }
-     */
-    libxl__qmp_param_add_string(gc, &data, "host", host);
-    libxl__qmp_param_add_string(gc, &data, "port", port);
-
-    libxl__qmp_param_add_string(gc, &addr, "type", "inet");
-    qmp_parameters_common_add(gc, &addr, "data", data);
-
-    qmp_parameters_common_add(gc, &args, "addr", addr);
-
-    return qmp_run_command(gc, domid, "nbd-server-start", args, NULL, NULL);
-}
-
-int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk)
-{
-    libxl__json_object *args = NULL;
-
-    libxl__qmp_param_add_string(gc, &args, "device", disk);
-    libxl__qmp_param_add_bool(gc, &args, "writable", true);
-
-    return qmp_run_command(gc, domid, "nbd-server-add", args, NULL, NULL);
-}
-
-int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary)
-{
-    libxl__json_object *args = NULL;
-
-    libxl__qmp_param_add_bool(gc, &args, "enable", true);
-    libxl__qmp_param_add_bool(gc, &args, "primary", primary);
-
-    return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
-}
-
-int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid)
-{
-    return qmp_run_command(gc, domid, "query-xen-replication-status", NULL,
-                           NULL, NULL);
-}
-
-int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid)
-{
-    return qmp_run_command(gc, domid, "xen-colo-do-checkpoint",
-                           NULL, NULL, NULL);
-}
-
-int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary)
-{
-    libxl__json_object *args = NULL;
-
-    libxl__qmp_param_add_bool(gc, &args, "enable", false);
-    libxl__qmp_param_add_bool(gc, &args, "primary", primary);
-
-    return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
-}
-
-int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid)
-{
-    return qmp_run_command(gc, domid, "nbd-server-stop", NULL, NULL, NULL);
-}
-
-int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parent,
-                                 const char *child, const char *node)
-{
-    libxl__json_object *args = NULL;
-
-    libxl__qmp_param_add_string(gc, &args, "parent", parent);
-    if (child)
-        libxl__qmp_param_add_string(gc, &args, "child", child);
-    if (node)
-        libxl__qmp_param_add_string(gc, &args, "node", node);
-
-    return qmp_run_command(gc, domid, "x-blockdev-change", args, NULL, NULL);
-}
-
-static int hmp_callback(libxl__qmp_handler *qmp,
-                        const libxl__json_object *response,
-                        void *opaque)
-{
-    char **output = opaque;
-    GC_INIT(qmp->ctx);
-    int rc;
-
-    rc = 0;
-    if (!output)
-        goto out;
-
-    *output = NULL;
-
-    if (libxl__json_object_is_string(response)) {
-        *output = libxl__strdup(NOGC, libxl__json_object_get_string(response));
-        goto out;
-    }
-
-    LOG(ERROR, "Response has unexpected format");
-    rc = ERROR_FAIL;
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
-                   char **output)
-{
-    libxl__json_object *args = NULL;
-
-    libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
-
-    return qmp_run_command(gc, domid, "human-monitor-command", args,
-                           hmp_callback, output);
-}
-
-
-typedef struct {
-    libxl__ev_qmp qmp;
-    char **output; /* user pointer */
-} qemu_monitor_command_state;
-
-static void qemu_monitor_command_done(libxl__egc *, libxl__ev_qmp *,
-                                      const libxl__json_object *response,
-                                      int rc);
-
-int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
-                               const char *command_line, char **output,
-                               const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    qemu_monitor_command_state *qmcs;
-    libxl__json_object *args = NULL;
-    int rc;
-
-    if (!output) {
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    GCNEW(qmcs);
-    libxl__ev_qmp_init(&qmcs->qmp);
-    qmcs->qmp.ao = ao;
-    qmcs->qmp.domid = domid;
-    qmcs->qmp.payload_fd = -1;
-    qmcs->qmp.callback = qemu_monitor_command_done;
-    qmcs->output = output;
-    libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
-    rc = libxl__ev_qmp_send(egc, &qmcs->qmp, "human-monitor-command", args);
-out:
-    if (rc) return AO_CREATE_FAIL(rc);
-    return AO_INPROGRESS;
-}
-
-static void qemu_monitor_command_done(libxl__egc *egc, libxl__ev_qmp *qmp,
-                                      const libxl__json_object *response,
-                                      int rc)
-{
-    STATE_AO_GC(qmp->ao);
-    qemu_monitor_command_state *qmcs = CONTAINER_OF(qmp, *qmcs, qmp);
-
-    if (rc) goto out;
-
-    if (!libxl__json_object_is_string(response)) {
-        rc = ERROR_QEMU_API;
-        LOGD(ERROR, qmp->domid, "Response has unexpected format");
-        goto out;
-    }
-
-    *(qmcs->output) =
-        libxl__strdup(NOGC, libxl__json_object_get_string(response));
-    rc = 0;
-
-out:
-    libxl__ev_qmp_dispose(gc, qmp);
-    libxl__ao_complete(egc, ao, rc);
-}
-
-/*
- * Functions using libxl__ev_qmp
- */
-
-static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
-                       const libxl__json_object *response, int rc);
-static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
-                              const libxl__json_object *response, int rc);
-static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset);
-static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
-                           const libxl__json_object *response, int rc);
-
-/* calls dsps->callback_device_model_done when done */
-void libxl__qmp_suspend_save(libxl__egc *egc,
-                             libxl__domain_suspend_state *dsps)
-{
-    EGC_GC;
-    int rc;
-    libxl__ev_qmp *ev = &dsps->qmp;
-
-    ev->ao = dsps->ao;
-    ev->domid = dsps->domid;
-    ev->callback = dm_stopped;
-    ev->payload_fd = -1;
-
-    rc = libxl__ev_qmp_send(egc, ev, "stop", NULL);
-    if (rc)
-        goto error;
-
-    return;
-
-error:
-    dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
-                       const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
-    const char *const filename = dsps->dm_savefile;
-    uint32_t dm_domid = libxl_get_stubdom_id(CTX, dsps->domid);
-
-    if (rc)
-        goto error;
-
-    if (dm_domid) {
-        /* see Linux stubdom interface in docs/stubdom.txt */
-        dm_state_save_to_fdset(egc, ev, 1);
-        return;
-    }
-
-    ev->payload_fd = open(filename, O_WRONLY | O_CREAT, 0600);
-    if (ev->payload_fd < 0) {
-        LOGED(ERROR, ev->domid,
-              "Failed to open file %s for QEMU", filename);
-        rc = ERROR_FAIL;
-        goto error;
-    }
-
-    ev->callback = dm_state_fd_ready;
-    rc = libxl__ev_qmp_send(egc, ev, "add-fd", NULL);
-    if (rc)
-        goto error;
-
-    return;
-
-error:
-    if (ev->payload_fd >= 0) {
-        close(ev->payload_fd);
-        libxl__remove_file(gc, filename);
-        ev->payload_fd = -1;
-    }
-    dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
-                              const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    int fdset;
-    const libxl__json_object *o;
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
-
-    close(ev->payload_fd);
-    ev->payload_fd = -1;
-
-    if (rc)
-        goto error;
-
-    o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
-    if (!o) {
-        rc = ERROR_QEMU_API;
-        goto error;
-    }
-    fdset = libxl__json_object_get_integer(o);
-    dm_state_save_to_fdset(egc, ev, fdset);
-    return;
-
-error:
-    assert(rc);
-    libxl__remove_file(gc, dsps->dm_savefile);
-    dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset)
-{
-    EGC_GC;
-    int rc;
-    libxl__json_object *args = NULL;
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
-
-    ev->callback = dm_state_saved;
-
-    /* The `live` parameter was added to QEMU 2.11. It signals QEMU that
-     * the save operation is for a live migration rather than for taking a
-     * snapshot. */
-    if (qmp_ev_qemu_compare_version(ev, 2, 11, 0) >= 0)
-        libxl__qmp_param_add_bool(gc, &args, "live", dsps->live);
-    QMP_PARAMETERS_SPRINTF(&args, "filename", "/dev/fdset/%d", fdset);
-    rc = libxl__ev_qmp_send(egc, ev, "xen-save-devices-state", args);
-    if (rc)
-        goto error;
-
-    return;
-
-error:
-    assert(rc);
-    if (!libxl_get_stubdom_id(CTX, dsps->domid))
-        libxl__remove_file(gc, dsps->dm_savefile);
-    dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
-                           const libxl__json_object *response, int rc)
-{
-    EGC_GC;
-    libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
-
-    if (rc)
-        libxl__remove_file(gc, dsps->dm_savefile);
-
-    dsps->callback_device_model_done(egc, dsps, rc);
-}
-
-
-/* ------------ Implementation of libxl__ev_qmp ---------------- */
-
-/*
- * Possible internal state compared to qmp_state:
- *
- * qmp_state     External   cfd    efd     id     rx_buf* tx_buf* msg* lock
- * disconnected   Idle       NULL   Idle    reset  free    free    free Idle
- * waiting_lock   Active     open   Idle    reset  used    free    set  Active
- * connecting     Active     open   IN      reset  used    free    set  Acquired
- * cap.neg        Active     open   IN|OUT  sent   used    cap_neg set  Acquired
- * cap.neg        Active     open   IN      sent   used    free    set  Acquired
- * connected      Connected  open   IN      any    used    free    free Acquired
- * waiting_reply  Active     open   IN|OUT  sent   used    free    set  Acquired
- * waiting_reply  Active     open   IN|OUT  sent   used    user's  free Acquired
- * waiting_reply  Active     open   IN      sent   used    free    free Acquired
- * broken[1]      none[2]    any    Active  any    any     any     any  any
- *
- * [1] When an internal function return an error, it can leave ev_qmp in a
- * `broken` state but only if the caller is another internal function.
- * That `broken` needs to be cleaned up, e.i. transitionned to the
- * `disconnected` state, before the control of ev_qmp is released outsides
- * of ev_qmp implementation.
- *
- * [2] This internal state should not be visible externally, see [1].
- *
- * Possible buffers states:
- * - receiving buffer:
- *                     free   used
- *     rx_buf           NULL   NULL or allocated
- *     rx_buf_size      0      allocation size of `rx_buf`
- *     rx_buf_used      0      <= rx_buf_size, actual data in the buffer
- * - transmitting buffer:
- *                     free   used
- *     tx_buf           NULL   contains data
- *     tx_buf_len       0      size of data
- *     tx_buf_off       0      <= tx_buf_len, data already sent
- * - queued user command:
- *                     free  set
- *     msg              NULL  contains data
- *     msg_id           0     id assoctiated with the command in `msg`
- *
- * - Allowed internal state transition:
- * disconnected                     -> waiting_lock
- * waiting_lock                     -> connecting
- * connection                       -> capability_negotiation
- * capability_negotiation/connected -> waiting_reply
- * waiting_reply                    -> connected
- * any                              -> broken
- * broken                           -> disconnected
- * any                              -> disconnected
- *
- * The QEMU Machine Protocol (QMP) specification can be found in the QEMU
- * repository:
- * https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/interop/qmp-spec.txt
- */
-
-/* prototypes */
-
-static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
-                               int fd, short events, short revents);
-static int qmp_ev_callback_writable(libxl__gc *gc,
-                                    libxl__ev_qmp *ev, int fd);
-static int qmp_ev_callback_readable(libxl__egc *egc,
-                                    libxl__ev_qmp *ev, int fd);
-static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
-                               libxl__json_object **o_r);
-static int qmp_ev_handle_message(libxl__egc *egc,
-                                 libxl__ev_qmp *ev,
-                                 const libxl__json_object *resp);
-
-/* helpers */
-
-static void qmp_ev_ensure_reading_writing(libxl__gc *gc, libxl__ev_qmp *ev)
-    /* Update the state of `efd` to match the permited state
-     * on entry: !disconnected */
-{
-    short events = POLLIN;
-
-    if (ev->state == qmp_state_waiting_lock)
-        /* We can't modify the efd yet, as it isn't registered. */
-        return;
-
-    if (ev->tx_buf)
-        events |= POLLOUT;
-    else if ((ev->state == qmp_state_waiting_reply) && ev->msg)
-        events |= POLLOUT;
-
-    libxl__ev_fd_modify(gc, &ev->efd, events);
-}
-
-static void qmp_ev_set_state(libxl__gc *gc, libxl__ev_qmp *ev,
-                             libxl__qmp_state new_state)
-    /* on entry: !broken and !disconnected */
-{
-    switch (new_state) {
-    case qmp_state_disconnected:
-        break;
-    case qmp_state_waiting_lock:
-        assert(ev->state == qmp_state_disconnected);
-        break;
-    case qmp_state_connecting:
-        assert(ev->state == qmp_state_waiting_lock);
-        break;
-    case qmp_state_capability_negotiation:
-        assert(ev->state == qmp_state_connecting);
-        break;
-    case qmp_state_waiting_reply:
-        assert(ev->state == qmp_state_capability_negotiation ||
-               ev->state == qmp_state_connected);
-        break;
-    case qmp_state_connected:
-        assert(ev->state == qmp_state_waiting_reply);
-        break;
-    }
-
-    ev->state = new_state;
-
-    qmp_ev_ensure_reading_writing(gc, ev);
-}
-
-static void qmp_ev_tx_buf_clear(libxl__ev_qmp *ev)
-{
-    ev->tx_buf = NULL;
-    ev->tx_buf_len = 0;
-    ev->tx_buf_off = 0;
-}
-
-static int qmp_error_class_to_libxl_error_code(libxl__gc *gc,
-                                               const char *eclass)
-{
-    const libxl_enum_string_table *t = libxl_error_string_table;
-    const char skip[] = "QMP_";
-    const size_t skipl = sizeof(skip) - 1;
-
-    /* compare "QMP_GENERIC_ERROR" from libxl_error to "GenericError"
-     * generated by the QMP server */
-
-    for (; t->s; t++) {
-            const char *s = eclass;
-            const char *se = t->s;
-        if (strncasecmp(t->s, skip, skipl))
-            continue;
-        se += skipl;
-        while (*s && *se) {
-            /* skip underscores */
-            if (*se == '_') {
-                se++;
-                continue;
-            }
-            if (tolower(*s) != tolower(*se))
-                break;
-            s++, se++;
-        }
-        if (!*s && !*se)
-            return t->v;
-    }
-
-    LOG(ERROR, "Unknown QMP error class '%s'", eclass);
-    return ERROR_UNKNOWN_QMP_ERROR;
-}
-
-/* Setup connection */
-
-static void qmp_ev_lock_aquired(libxl__egc *, libxl__ev_slowlock *,
-                                int rc);
-static void lock_error_callback(libxl__egc *, libxl__ev_immediate *);
-
-static int qmp_ev_connect(libxl__egc *egc, libxl__ev_qmp *ev)
-    /* disconnected -> waiting_lock/connecting but with `msg` free
-     * on error: broken */
-{
-    EGC_GC;
-    int fd;
-    int rc;
-
-    /* Convenience aliases */
-    libxl__ev_slowlock *lock = &ev->lock;
-
-    assert(ev->state == qmp_state_disconnected);
-
-    libxl__carefd_begin();
-    fd = socket(AF_UNIX, SOCK_STREAM, 0);
-    ev->cfd = libxl__carefd_opened(CTX, fd);
-    if (!ev->cfd) {
-        LOGED(ERROR, ev->domid, "socket() failed");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = libxl_fd_set_nonblock(CTX, libxl__carefd_fd(ev->cfd), 1);
-    if (rc)
-        goto out;
-
-    qmp_ev_set_state(gc, ev, qmp_state_waiting_lock);
-
-    lock->ao = ev->ao;
-    lock->domid = ev->domid;
-    lock->callback = qmp_ev_lock_aquired;
-    libxl__ev_slowlock_lock(egc, &ev->lock);
-
-    return 0;
-
-out:
-    return rc;
-}
-
-static void qmp_ev_lock_aquired(libxl__egc *egc, libxl__ev_slowlock *lock,
-                                int rc)
-    /* waiting_lock (with `lock' Acquired) -> connecting
-     * on error: broken */
-{
-    libxl__ev_qmp *ev = CONTAINER_OF(lock, *ev, lock);
-    EGC_GC;
-    const char *qmp_socket_path;
-    struct sockaddr_un un;
-    int r;
-
-    if (rc) goto out;
-
-    qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid);
-
-    LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path);
-
-    rc = libxl__prepare_sockaddr_un(gc, &un, qmp_socket_path,
-                                    "QMP socket");
-    if (rc)
-        goto out;
-
-    r = connect(libxl__carefd_fd(ev->cfd),
-                (struct sockaddr *) &un, sizeof(un));
-    if (r && errno != EINPROGRESS) {
-        LOGED(ERROR, ev->domid, "Failed to connect to QMP socket %s",
-              qmp_socket_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__ev_fd_register(gc, &ev->efd, qmp_ev_fd_callback,
-                               libxl__carefd_fd(ev->cfd), POLLIN);
-    if (rc)
-        goto out;
-
-    qmp_ev_set_state(gc, ev, qmp_state_connecting);
-
-    return;
-
-out:
-    /* An error occurred and we need to let the caller know.  At this
-     * point, we can only do so via the callback. Unfortunately, the
-     * callback of libxl__ev_slowlock_lock() might be called synchronously,
-     * but libxl__ev_qmp_send() promise that it will not call the callback
-     * synchronously. So we have to arrange to call the callback
-     * asynchronously. */
-    ev->rc = rc;
-    ev->ei.callback = lock_error_callback;
-    libxl__ev_immediate_register(egc, &ev->ei);
-}
-
-static void lock_error_callback(libxl__egc *egc, libxl__ev_immediate *ei)
-    /* broken -> disconnected */
-{
-    EGC_GC;
-    libxl__ev_qmp *ev = CONTAINER_OF(ei, *ev, ei);
-
-    int rc = ev->rc;
-
-    /* On error, deallocate all private resources */
-    libxl__ev_qmp_dispose(gc, ev);
-
-    /* And tell libxl__ev_qmp user about the error */
-    ev->callback(egc, ev, NULL, rc); /* must be last */
-}
-
-/* QMP FD callbacks */
-
-static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
-                               int fd, short events, short revents)
-    /* On entry, ev_fd is (of course) Active.  The ev_qmp may be in any
-     * state where this is permitted.  qmp_ev_fd_callback will do the work
-     * necessary to make progress, depending on the current state, and make
-     * the appropriate state transitions and callbacks.  */
-{
-    libxl__ev_qmp *ev = CONTAINER_OF(ev_fd, *ev, efd);
-    STATE_AO_GC(ev->ao);
-    int rc;
-
-    if (revents & (POLLHUP|POLLERR)) {
-        int r;
-        int error_val = 0;
-        socklen_t opt_len = sizeof(error_val);
-
-        r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error_val, &opt_len);
-        if (r)
-            LOGED(ERROR, ev->domid, "getsockopt failed");
-        if (!r && error_val) {
-            errno = error_val;
-            LOGED(ERROR, ev->domid, "error on QMP socket");
-        } else {
-            LOGD(ERROR, ev->domid,
-                 "received POLLHUP|POLLERR from QMP socket");
-        }
-        rc = ERROR_PROTOCOL_ERROR_QMP;
-        goto error;
-    }
-
-    if (revents & ~(POLLIN|POLLOUT)) {
-        LOGD(ERROR, ev->domid,
-             "unexpected poll event 0x%x on QMP socket (expected POLLIN "
-             "and/or POLLOUT)",
-            revents);
-        rc = ERROR_FAIL;
-        goto error;
-    }
-
-    if (revents & POLLOUT) {
-        rc = qmp_ev_callback_writable(gc, ev, fd);
-        if (rc)
-            goto error;
-    }
-
-    if (revents & POLLIN) {
-        rc = qmp_ev_callback_readable(egc, ev, fd);
-        if (rc < 0)
-            goto error;
-        if (rc == 1) {
-            /* user callback has been called */
-            return;
-        }
-    }
-
-    return;
-
-error:
-    assert(rc);
-
-    LOGD(ERROR, ev->domid,
-         "Error happened with the QMP connection to QEMU");
-
-    /* On error, deallocate all private ressources */
-    libxl__ev_qmp_dispose(gc, ev);
-
-    /* And tell libxl__ev_qmp user about the error */
-    ev->callback(egc, ev, NULL, rc); /* must be last */
-}
-
-static int qmp_ev_callback_writable(libxl__gc *gc,
-                                    libxl__ev_qmp *ev, int fd)
-    /* on entry: !disconnected
-     * on return, one of these state transition:
-     *   waiting_reply (with msg set) -> waiting_reply (with msg free)
-     *   tx_buf set -> same state or tx_buf free
-     * on error: broken */
-{
-    int rc;
-    ssize_t r;
-
-    if (ev->state == qmp_state_waiting_reply) {
-        if (ev->msg) {
-            assert(!ev->tx_buf);
-            ev->tx_buf = ev->msg;
-            ev->tx_buf_len = strlen(ev->msg);
-            ev->tx_buf_off = 0;
-            ev->id = ev->msg_id;
-            ev->msg = NULL;
-            ev->msg_id = 0;
-        }
-    }
-
-    assert(ev->tx_buf);
-
-    LOG_QMP("sending: '%.*s'", (int)ev->tx_buf_len, ev->tx_buf);
-
-    /*
-     * We will send a file descriptor associated with a command on the
-     * first byte of this command.
-     */
-    if (ev->state == qmp_state_waiting_reply &&
-        ev->payload_fd >= 0 &&
-        ev->tx_buf_off == 0) {
-
-        rc = libxl__sendmsg_fds(gc, fd, ev->tx_buf[ev->tx_buf_off],
-                                1, &ev->payload_fd, "QMP socket");
-        /* Check for EWOULDBLOCK, and return to try again later */
-        if (rc == ERROR_NOT_READY)
-            return 0;
-        if (rc)
-            return rc;
-        ev->tx_buf_off++;
-    }
-
-    while (ev->tx_buf_off < ev->tx_buf_len) {
-        ssize_t max_write = ev->tx_buf_len - ev->tx_buf_off;
-        r = write(fd, ev->tx_buf + ev->tx_buf_off, max_write);
-        if (r < 0) {
-            if (errno == EINTR)
-                continue;
-            if (errno == EWOULDBLOCK)
-                break;
-            LOGED(ERROR, ev->domid, "failed to write to QMP socket");
-            return ERROR_FAIL;
-        }
-        assert(r > 0 && r <= max_write);
-        ev->tx_buf_off += r;
-    }
-
-    if (ev->tx_buf_off == ev->tx_buf_len)
-        qmp_ev_tx_buf_clear(ev);
-
-    qmp_ev_ensure_reading_writing(gc, ev);
-
-    return 0;
-}
-
-static int qmp_ev_callback_readable(libxl__egc *egc,
-                                    libxl__ev_qmp *ev, int fd)
-    /*
-     * Return values:
-     *   < 0    libxl error code
-     *   0      success
-     *   1      success, but a user callback has been called,
-     *          `ev` should not be used anymore.
-     *
-     * This function will update the rx buffer and possibly update
-     * ev->state:
-     *  connecting             -> capability_negotiation
-     *  capability_negotiation -> waiting_reply
-     *  waiting_reply          -> connected
-     * on error: broken
-     */
-{
-    STATE_AO_GC(ev->ao);
-    int rc;
-    ssize_t r;
-
-    while (1) {
-        while (1) {
-            libxl__json_object *o = NULL;
-
-            /* parse rx buffer to find one json object */
-            rc = qmp_ev_get_next_msg(egc, ev, &o);
-            if (rc == ERROR_NOTFOUND)
-                break;
-            else if (rc)
-                return rc;
-
-            /* Must be last and return when the user callback is called */
-            rc = qmp_ev_handle_message(egc, ev, o);
-            if (rc)
-                /* returns both rc values -ERROR_* and 1 */
-                return rc;
-        }
-
-        /* Check if the buffer still have space, or increase size */
-        if (ev->rx_buf_size - ev->rx_buf_used < QMP_RECEIVE_BUFFER_SIZE) {
-            size_t newsize = ev->rx_buf_size * 2 + QMP_RECEIVE_BUFFER_SIZE;
-
-            if (newsize > QMP_MAX_SIZE_RX_BUF) {
-                LOGD(ERROR, ev->domid,
-                     "QMP receive buffer is too big (%zu > %lld)",
-                     newsize, QMP_MAX_SIZE_RX_BUF);
-                return ERROR_BUFFERFULL;
-            }
-            ev->rx_buf_size = newsize;
-            ev->rx_buf = libxl__realloc(gc, ev->rx_buf, ev->rx_buf_size);
-        }
-
-        r = read(fd, ev->rx_buf + ev->rx_buf_used,
-                 ev->rx_buf_size - ev->rx_buf_used);
-        if (r < 0) {
-            if (errno == EINTR)
-                continue;
-            if (errno == EWOULDBLOCK)
-                break;
-            LOGED(ERROR, ev->domid, "error reading QMP socket");
-            return ERROR_FAIL;
-        }
-
-        if (r == 0) {
-            LOGD(ERROR, ev->domid, "Unexpected EOF on QMP socket");
-            return ERROR_PROTOCOL_ERROR_QMP;
-        }
-
-        LOG_QMP("received %ldB: '%.*s'", r,
-                (int)r, ev->rx_buf + ev->rx_buf_used);
-
-        ev->rx_buf_used += r;
-        assert(ev->rx_buf_used <= ev->rx_buf_size);
-    }
-
-    return 0;
-}
-
-/* Handle messages received from QMP server */
-
-static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
-                               libxl__json_object **o_r)
-    /* Find a JSON object and store it in o_r.
-     * return ERROR_NOTFOUND if no object is found.
-     *
-     * !disconnected -> same state (with rx buffer updated)
-     */
-{
-    STATE_AO_GC(ev->ao);
-    size_t len;
-    char *end = NULL;
-    const char eom[] = "\r\n";
-    const size_t eoml = sizeof(eom) - 1;
-    libxl__json_object *o = NULL;
-
-    if (!ev->rx_buf_used)
-        return ERROR_NOTFOUND;
-
-    /* Search for the end of a QMP message: "\r\n" */
-    end = memmem(ev->rx_buf, ev->rx_buf_used, eom, eoml);
-    if (!end)
-        return ERROR_NOTFOUND;
-    len = (end - ev->rx_buf) + eoml;
-
-    LOG_QMP("parsing %luB: '%.*s'", len, (int)len, ev->rx_buf);
-
-    /* Replace \r by \0 so that libxl__json_parse can use strlen */
-    ev->rx_buf[len - eoml] = '\0';
-    o = libxl__json_parse(gc, ev->rx_buf);
-
-    if (!o) {
-        LOGD(ERROR, ev->domid, "Parse error");
-        return ERROR_PROTOCOL_ERROR_QMP;
-    }
-
-    ev->rx_buf_used -= len;
-    memmove(ev->rx_buf, ev->rx_buf + len, ev->rx_buf_used);
-
-    LOG_QMP("JSON object received: %s", JSON(o));
-
-    *o_r = o;
-
-    return 0;
-}
-
-static int qmp_ev_parse_error_messages(libxl__egc *egc,
-                                       libxl__ev_qmp *ev,
-                                       const libxl__json_object *resp);
-
-static int qmp_ev_handle_message(libxl__egc *egc,
-                                 libxl__ev_qmp *ev,
-                                 const libxl__json_object *resp)
-    /*
-     * This function will handle every messages sent by the QMP server.
-     * Return values:
-     *   < 0    libxl error code
-     *   0      success
-     *   1      success, but a user callback has been called,
-     *          `ev` should not be used anymore.
-     *
-     * Possible state changes:
-     * connecting -> capability_negotiation
-     * capability_negotiation -> waiting_reply
-     * waiting_reply -> waiting_reply/connected
-     *
-     * on error: broken
-     */
-{
-    STATE_AO_GC(ev->ao);
-    int id;
-    char *buf;
-    int rc = 0;
-    const libxl__json_object *o;
-    const libxl__json_object *response;
-    libxl__qmp_message_type type = qmp_response_type(resp);
-
-    switch (type) {
-    case LIBXL__QMP_MESSAGE_TYPE_QMP:
-        /* greeting message */
-
-        if (ev->state != qmp_state_connecting) {
-            LOGD(ERROR, ev->domid,
-                 "Unexpected greeting message received");
-            return ERROR_PROTOCOL_ERROR_QMP;
-        }
-
-        /*
-         * Store advertised QEMU version
-         * { "QMP": { "version": {
-         *     "qemu": { "major": int, "minor": int, "micro": int } } } }
-         */
-        o = libxl__json_map_get("QMP", resp, JSON_MAP);
-        o = libxl__json_map_get("version", o, JSON_MAP);
-        o = libxl__json_map_get("qemu", o, JSON_MAP);
-#define GRAB_VERSION(level) do { \
-        ev->qemu_version.level = libxl__json_object_get_integer( \
-            libxl__json_map_get(#level, o, JSON_INTEGER)); \
-        } while (0)
-        GRAB_VERSION(major);
-        GRAB_VERSION(minor);
-        GRAB_VERSION(micro);
-#undef GRAB_VERSION
-        LOGD(DEBUG, ev->domid, "QEMU version: %d.%d.%d",
-             ev->qemu_version.major,
-             ev->qemu_version.minor,
-             ev->qemu_version.micro);
-
-        /* Prepare next message to send */
-        assert(!ev->tx_buf);
-        ev->id = ev->next_id++;
-        buf = qmp_prepare_cmd(gc, "qmp_capabilities", NULL, ev->id);
-        if (!buf) {
-            LOGD(ERROR, ev->domid,
-                 "Failed to generate qmp_capabilities command");
-            return ERROR_FAIL;
-        }
-        ev->tx_buf = buf;
-        ev->tx_buf_len = strlen(buf);
-        ev->tx_buf_off = 0;
-        qmp_ev_set_state(gc, ev, qmp_state_capability_negotiation);
-
-        return 0;
-
-    case LIBXL__QMP_MESSAGE_TYPE_RETURN:
-    case LIBXL__QMP_MESSAGE_TYPE_ERROR:
-        /*
-         * Reply to a command (success/error) or server error
-         *
-         * In this cases, we are parsing two possibles responses:
-         * - success:
-         * { "return": json-value, "id": int }
-         * - error:
-         * { "error": { "class": string, "desc": string }, "id": int }
-         */
-
-        o = libxl__json_map_get("id", resp, JSON_INTEGER);
-        if (!o) {
-            /*
-             * If "id" isn't present, an error occur on the server before
-             * it has read the "id" provided by libxl.
-             *
-             * We deliberately squash all errors into
-             * ERROR_PROTOCOL_ERROR_QMP as qmp_ev_parse_error_messages may
-             * also return ERROR_QMP_* but those are reserved for errors
-             * return by the caller's command.
-             */
-            qmp_ev_parse_error_messages(egc, ev, resp);
-            return ERROR_PROTOCOL_ERROR_QMP;
-        }
-
-        id = libxl__json_object_get_integer(o);
-
-        if (id != ev->id) {
-            LOGD(ERROR, ev->domid,
-                 "Message from QEMU with unexpected id %d: %s",
-                 id, JSON(resp));
-            return ERROR_PROTOCOL_ERROR_QMP;
-        }
-
-        switch (ev->state) {
-        case qmp_state_capability_negotiation:
-            if (type != LIBXL__QMP_MESSAGE_TYPE_RETURN) {
-                LOGD(ERROR, ev->domid,
-                     "Error during capability negotiation: %s",
-                     JSON(resp));
-                return ERROR_PROTOCOL_ERROR_QMP;
-            }
-            qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
-            return 0;
-        case qmp_state_waiting_reply:
-            if (type == LIBXL__QMP_MESSAGE_TYPE_RETURN) {
-                response = libxl__json_map_get("return", resp, JSON_ANY);
-                rc = 0;
-            } else {
-                /* error message */
-                response = NULL;
-                rc = qmp_ev_parse_error_messages(egc, ev, resp);
-            }
-            qmp_ev_set_state(gc, ev, qmp_state_connected);
-            ev->callback(egc, ev, response, rc); /* must be last */
-            return 1;
-        default:
-            LOGD(ERROR, ev->domid, "Unexpected message: %s", JSON(resp));
-            return ERROR_PROTOCOL_ERROR_QMP;
-        }
-        return 0;
-
-    case LIBXL__QMP_MESSAGE_TYPE_EVENT:
-        /* Events are ignored */
-        return 0;
-
-    case LIBXL__QMP_MESSAGE_TYPE_INVALID:
-        LOGD(ERROR, ev->domid, "Unexpected message received: %s",
-             JSON(resp));
-        return ERROR_PROTOCOL_ERROR_QMP;
-
-    default:
-        abort();
-    }
-
-    return 0;
-}
-
-static int qmp_ev_parse_error_messages(libxl__egc *egc,
-                                       libxl__ev_qmp *ev,
-                                       const libxl__json_object *resp)
-    /* no state change */
-{
-    STATE_AO_GC(ev->ao);
-    int rc;
-    const char *s;
-    const libxl__json_object *o;
-    const libxl__json_object *err;
-
-    /*
-     * { "error": { "class": string, "desc": string } }
-     */
-
-    err = libxl__json_map_get("error", resp, JSON_MAP);
-
-    o = libxl__json_map_get("class", err, JSON_STRING);
-    if (!o) {
-        LOGD(ERROR, ev->domid,
-             "Protocol error: missing 'class' member in error message");
-        return ERROR_PROTOCOL_ERROR_QMP;
-    }
-    s = libxl__json_object_get_string(o);
-    if (s)
-        rc = qmp_error_class_to_libxl_error_code(gc, s);
-    else
-        rc = ERROR_PROTOCOL_ERROR_QMP;
-
-    o = libxl__json_map_get("desc", err, JSON_STRING);
-    if (!o) {
-        LOGD(ERROR, ev->domid,
-             "Protocol error: missing 'desc' member in error message");
-        return ERROR_PROTOCOL_ERROR_QMP;
-    }
-    s = libxl__json_object_get_string(o);
-    if (s)
-        LOGD(ERROR, ev->domid, "%s", s);
-    else
-        LOGD(ERROR, ev->domid, "Received unexpected error: %s",
-             JSON(resp));
-    return rc;
-}
-
-/*
- * libxl__ev_qmp_*
- */
-
-void libxl__ev_qmp_init(libxl__ev_qmp *ev)
-    /* disconnected -> disconnected */
-{
-    /* Start with an message ID that is obviously generated by libxl
-     * "xlq\0" */
-    ev->next_id = 0x786c7100;
-
-    ev->cfd = NULL;
-    libxl__ev_fd_init(&ev->efd);
-    ev->state = qmp_state_disconnected;
-    ev->id = 0;
-
-    ev->rx_buf = NULL;
-    ev->rx_buf_size = ev->rx_buf_used = 0;
-    qmp_ev_tx_buf_clear(ev);
-
-    ev->msg = NULL;
-    ev->msg_id = 0;
-
-    ev->qemu_version.major = -1;
-    ev->qemu_version.minor = -1;
-    ev->qemu_version.micro = -1;
-
-    libxl__ev_qmplock_init(&ev->lock);
-    ev->rc = 0;
-}
-
-int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
-                       const char *cmd, libxl__json_object *args)
-    /* disconnected -> waiting_lock/connecting
-     * connected -> waiting_reply (with msg set)
-     * on error: disconnected */
-{
-    STATE_AO_GC(ev->ao);
-    int rc;
-
-    LOGD(DEBUG, ev->domid, " ev %p, cmd '%s'", ev, cmd);
-
-    assert(ev->state == qmp_state_disconnected ||
-           ev->state == qmp_state_connected);
-    assert(cmd);
-
-    /* Connect to QEMU if not already connected */
-    if (ev->state == qmp_state_disconnected) {
-        rc = qmp_ev_connect(egc, ev);
-        if (rc)
-            goto error;
-    }
-
-    /* Prepare user command */
-    ev->msg_id = ev->next_id++;
-    ev->msg = qmp_prepare_cmd(gc, cmd, args, ev->msg_id);
-    if (!ev->msg) {
-        LOGD(ERROR, ev->domid, "Failed to generate caller's command %s",
-             cmd);
-        rc = ERROR_FAIL;
-        goto error;
-    }
-    if (ev->state == qmp_state_connected) {
-        qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
-    }
-
-    return 0;
-
-error:
-    libxl__ev_qmp_dispose(gc, ev);
-    return rc;
-}
-
-void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev)
-    /* * -> disconnected */
-{
-    LOGD(DEBUG, ev->domid, " ev %p", ev);
-
-    libxl__ev_fd_deregister(gc, &ev->efd);
-    libxl__carefd_close(ev->cfd);
-    libxl__ev_slowlock_dispose(gc, &ev->lock);
-
-    libxl__ev_qmp_init(ev);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_remus.c b/tools/libxl/libxl_remus.c
deleted file mode 100644 (file)
index 6338a1b..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *        Yang Hongyang <hongyang.yang@easystack.cn>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-extern const libxl__checkpoint_device_instance_ops remus_device_nic;
-extern const libxl__checkpoint_device_instance_ops remus_device_drbd_disk;
-static const libxl__checkpoint_device_instance_ops *remus_ops[] = {
-    &remus_device_nic,
-    &remus_device_drbd_disk,
-    NULL,
-};
-
-/*----- helper functions -----*/
-
-static int init_device_subkind(libxl__checkpoint_devices_state *cds)
-{
-    /* init device subkind-specific state in the libxl ctx */
-    int rc;
-    STATE_AO_GC(cds->ao);
-
-    if (libxl__netbuffer_enabled(gc)) {
-        rc = init_subkind_nic(cds);
-        if (rc) goto out;
-    }
-
-    rc = init_subkind_drbd_disk(cds);
-    if (rc) goto out;
-
-    rc = 0;
-out:
-    return rc;
-}
-
-static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds)
-{
-    /* cleanup device subkind-specific state in the libxl ctx */
-    STATE_AO_GC(cds->ao);
-
-    if (libxl__netbuffer_enabled(gc))
-        cleanup_subkind_nic(cds);
-
-    cleanup_subkind_drbd_disk(cds);
-}
-
-/*-------------------- Remus setup and teardown ---------------------*/
-
-static void remus_setup_done(libxl__egc *egc,
-                             libxl__checkpoint_devices_state *cds, int rc);
-static void remus_setup_failed(libxl__egc *egc,
-                               libxl__checkpoint_devices_state *cds, int rc);
-static void remus_checkpoint_stream_written(
-    libxl__egc *egc, libxl__stream_write_state *sws, int rc);
-static void libxl__remus_domain_suspend_callback(void *data);
-static void libxl__remus_domain_resume_callback(void *data);
-static void libxl__remus_domain_save_checkpoint_callback(void *data);
-
-void libxl__remus_setup(libxl__egc *egc, libxl__remus_state *rs)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-    const libxl_domain_remus_info *const info = dss->remus;
-    libxl__srm_save_autogen_callbacks *const callbacks =
-        &dss->sws.shs.callbacks.save.a;
-
-    STATE_AO_GC(dss->ao);
-
-    if (libxl_defbool_val(info->netbuf)) {
-        if (!libxl__netbuffer_enabled(gc)) {
-            LOGD(ERROR, dss->domid,
-                 "Remus: No support for network buffering");
-            goto out;
-        }
-        cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VIF);
-    }
-
-    if (libxl_defbool_val(info->diskbuf))
-        cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VBD);
-
-    cds->ao = ao;
-    cds->domid = dss->domid;
-    cds->callback = remus_setup_done;
-    cds->ops = remus_ops;
-    cds->concrete_data = rs;
-    rs->interval = info->interval;
-
-    if (init_device_subkind(cds)) {
-        LOGD(ERROR, dss->domid,
-             "Remus: failed to init device subkind");
-        goto out;
-    }
-
-    dss->sws.checkpoint_callback = remus_checkpoint_stream_written;
-
-    callbacks->suspend = libxl__remus_domain_suspend_callback;
-    callbacks->postcopy = libxl__remus_domain_resume_callback;
-    callbacks->checkpoint = libxl__remus_domain_save_checkpoint_callback;
-
-    libxl__checkpoint_devices_setup(egc, cds);
-    return;
-
-out:
-    dss->callback(egc, dss, ERROR_FAIL);
-}
-
-static void remus_setup_done(libxl__egc *egc,
-                             libxl__checkpoint_devices_state *cds, int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-    STATE_AO_GC(dss->ao);
-
-    if (!rc) {
-        libxl__domain_save(egc, dss);
-        return;
-    }
-
-    LOGD(ERROR, dss->domid, "Remus: failed to setup device, rc %d", rc);
-    cds->callback = remus_setup_failed;
-    libxl__checkpoint_devices_teardown(egc, cds);
-}
-
-static void remus_setup_failed(libxl__egc *egc,
-                               libxl__checkpoint_devices_state *cds, int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-    STATE_AO_GC(dss->ao);
-
-    if (rc)
-        LOGD(ERROR, dss->domid,
-             "Remus: failed to teardown device after setup failed, rc %d", rc);
-
-    cleanup_device_subkind(cds);
-
-    dss->callback(egc, dss, rc);
-}
-
-static void remus_teardown_done(libxl__egc *egc,
-                                libxl__checkpoint_devices_state *cds,
-                                int rc);
-void libxl__remus_teardown(libxl__egc *egc,
-                           libxl__remus_state *rs,
-                           int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-
-    EGC_GC;
-
-    LOGD(WARN, dss->domid, "Remus: Domain suspend terminated with rc %d,"
-         " teardown Remus devices...", rc);
-    cds->callback = remus_teardown_done;
-    libxl__checkpoint_devices_teardown(egc, cds);
-}
-
-static void remus_teardown_done(libxl__egc *egc,
-                                libxl__checkpoint_devices_state *cds,
-                                int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-    STATE_AO_GC(dss->ao);
-
-    if (rc)
-        LOGD(ERROR, dss->domid, "Remus: failed to teardown device,"
-            " rc %d", rc);
-
-    cleanup_device_subkind(cds);
-
-    dss->callback(egc, dss, rc);
-}
-
-/*---------------------- remus callbacks (save) -----------------------*/
-
-static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
-                                libxl__domain_suspend_state *dsps, int ok);
-static void remus_devices_postsuspend_cb(libxl__egc *egc,
-                                         libxl__checkpoint_devices_state *cds,
-                                         int rc);
-static void remus_devices_preresume_cb(libxl__egc *egc,
-                                       libxl__checkpoint_devices_state *cds,
-                                       int rc);
-
-static void libxl__remus_domain_suspend_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__egc *egc = shs->egc;
-    libxl__domain_save_state *dss = shs->caller_state;
-    libxl__domain_suspend_state *dsps = &dss->dsps;
-
-    dsps->callback_common_done = remus_domain_suspend_callback_common_done;
-    libxl__domain_suspend(egc, dsps);
-}
-
-static void remus_domain_suspend_callback_common_done(libxl__egc *egc,
-                                libxl__domain_suspend_state *dsps, int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
-
-    if (rc)
-        goto out;
-
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-    cds->callback = remus_devices_postsuspend_cb;
-    libxl__checkpoint_devices_postsuspend(egc, cds);
-    return;
-
-out:
-    dss->rc = rc;
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void remus_devices_postsuspend_cb(libxl__egc *egc,
-                                         libxl__checkpoint_devices_state *cds,
-                                         int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-
-    if (rc)
-        goto out;
-
-    rc = 0;
-
-out:
-    if (rc)
-        dss->rc = rc;
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-static void libxl__remus_domain_resume_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__egc *egc = shs->egc;
-    libxl__domain_save_state *dss = shs->caller_state;
-    STATE_AO_GC(dss->ao);
-
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-    cds->callback = remus_devices_preresume_cb;
-    libxl__checkpoint_devices_preresume(egc, cds);
-}
-
-static void remus_devices_preresume_cb(libxl__egc *egc,
-                                       libxl__checkpoint_devices_state *cds,
-                                       int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-    STATE_AO_GC(dss->ao);
-
-    if (rc)
-        goto out;
-
-    /* Resumes the domain and the device model */
-    rc = libxl__domain_resume_deprecated(gc, dss->domid, /* Fast Suspend */1);
-    if (rc)
-        goto out;
-
-    rc = 0;
-
-out:
-    if (rc)
-        dss->rc = rc;
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-/*----- remus asynchronous checkpoint callback -----*/
-
-static void remus_devices_commit_cb(libxl__egc *egc,
-                                    libxl__checkpoint_devices_state *cds,
-                                    int rc);
-static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
-                                  const struct timeval *requested_abs,
-                                  int rc);
-
-static void libxl__remus_domain_save_checkpoint_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__domain_save_state *dss = shs->caller_state;
-    libxl__egc *egc = shs->egc;
-    STATE_AO_GC(dss->ao);
-
-    libxl__stream_write_start_checkpoint(egc, &dss->sws);
-}
-
-static void remus_checkpoint_stream_written(
-    libxl__egc *egc, libxl__stream_write_state *sws, int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(sws, *dss, sws);
-
-    /* Convenience aliases */
-    libxl__checkpoint_devices_state *const cds = &dss->cds;
-
-    STATE_AO_GC(dss->ao);
-
-    if (rc) {
-        LOGD(ERROR, dss->domid, "Failed to save device model."
-             " Terminating Remus..");
-        goto out;
-    }
-
-    cds->callback = remus_devices_commit_cb;
-    libxl__checkpoint_devices_commit(egc, cds);
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void remus_devices_commit_cb(libxl__egc *egc,
-                                    libxl__checkpoint_devices_state *cds,
-                                    int rc)
-{
-    libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds);
-
-    STATE_AO_GC(dss->ao);
-
-    if (rc) {
-        LOGD(ERROR, dss->domid, "Failed to do device commit op."
-            " Terminating Remus..");
-        goto out;
-    }
-
-    /*
-     * At this point, we have successfully checkpointed the guest and
-     * committed it at the backup. We'll come back after the checkpoint
-     * interval to checkpoint the guest again. Until then, let the guest
-     * continue execution.
-     */
-
-    /* Set checkpoint interval timeout */
-    rc = libxl__ev_time_register_rel(ao, &dss->rs.checkpoint_timeout,
-                                     remus_next_checkpoint,
-                                     dss->rs.interval);
-
-    if (rc)
-        goto out;
-
-    return;
-
-out:
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0);
-}
-
-static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev,
-                                  const struct timeval *requested_abs,
-                                  int rc)
-{
-    libxl__domain_save_state *dss =
-                            CONTAINER_OF(ev, *dss, rs.checkpoint_timeout);
-
-    STATE_AO_GC(dss->ao);
-
-    if (rc == ERROR_TIMEDOUT) /* As intended */
-        rc = 0;
-
-    /*
-     * Time to checkpoint the guest again. We return 1 to libxc
-     * (xc_domain_save.c). in order to continue executing the infinite loop
-     * (suspend, checkpoint, resume) in xc_domain_save().
-     */
-
-    if (rc)
-        dss->rc = rc;
-
-    libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
-}
-
-/*---------------------- remus callbacks (restore) -----------------------*/
-
-/*----- remus asynchronous checkpoint callback -----*/
-
-static void remus_checkpoint_stream_done(
-    libxl__egc *egc, libxl__stream_read_state *srs, int rc);
-
-static void libxl__remus_domain_restore_checkpoint_callback(void *data)
-{
-    libxl__save_helper_state *shs = data;
-    libxl__domain_create_state *dcs = shs->caller_state;
-    libxl__egc *egc = shs->egc;
-    STATE_AO_GC(dcs->ao);
-
-    libxl__stream_read_start_checkpoint(egc, &dcs->srs);
-}
-
-static void remus_checkpoint_stream_done(
-    libxl__egc *egc, libxl__stream_read_state *stream, int rc)
-{
-    libxl__xc_domain_saverestore_async_callback_done(egc, &stream->shs, rc);
-}
-
-void libxl__remus_restore_setup(libxl__egc *egc,
-                                libxl__domain_create_state *dcs)
-{
-    /* Convenience aliases */
-    libxl__srm_restore_autogen_callbacks *const callbacks =
-        &dcs->srs.shs.callbacks.restore.a;
-
-    callbacks->checkpoint = libxl__remus_domain_restore_checkpoint_callback;
-    dcs->srs.checkpoint_callback = remus_checkpoint_stream_done;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_remus_disk_drbd.c b/tools/libxl/libxl_remus_disk_drbd.c
deleted file mode 100644 (file)
index d08e470..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2014 FUJITSU LIMITED
- * Author Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*** drbd implementation ***/
-const int DRBD_SEND_CHECKPOINT = 20;
-const int DRBD_WAIT_CHECKPOINT_ACK = 30;
-
-typedef struct libxl__remus_drbd_disk {
-    int ctl_fd;
-    int ackwait;
-} libxl__remus_drbd_disk;
-
-int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
-{
-    libxl__remus_state *rs = cds->concrete_data;
-    STATE_AO_GC(cds->ao);
-
-    rs->drbd_probe_script = GCSPRINTF("%s/block-drbd-probe",
-                                      libxl__xen_script_dir_path());
-
-    return 0;
-}
-
-void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
-{
-    return;
-}
-
-/*----- match(), setup() and teardown() -----*/
-
-/* callbacks */
-static void match_async_exec_cb(libxl__egc *egc,
-                                libxl__async_exec_state *aes,
-                                int rc, int status);
-
-/* implementations */
-
-static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev);
-
-static void drbd_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    STATE_AO_GC(dev->cds->ao);
-
-    match_async_exec(egc, dev);
-}
-
-static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    int arraysize, nr = 0, rc;
-    const libxl_device_disk *disk = dev->backend_dev;
-    libxl__async_exec_state *aes = &dev->aodev.aes;
-    libxl__remus_state *rs = dev->cds->concrete_data;
-    STATE_AO_GC(dev->cds->ao);
-
-    /* setup env & args */
-    arraysize = 1;
-    GCNEW_ARRAY(aes->env, arraysize);
-    aes->env[nr++] = NULL;
-    assert(nr <= arraysize);
-
-    arraysize = 3;
-    nr = 0;
-    GCNEW_ARRAY(aes->args, arraysize);
-    aes->args[nr++] = rs->drbd_probe_script;
-    aes->args[nr++] = disk->pdev_path;
-    aes->args[nr++] = NULL;
-    assert(nr <= arraysize);
-
-    aes->ao = dev->cds->ao;
-    aes->what = GCSPRINTF("%s %s", aes->args[0], aes->args[1]);
-    aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
-    aes->callback = match_async_exec_cb;
-    aes->stdfds[0] = -1;
-    aes->stdfds[1] = -1;
-    aes->stdfds[2] = -1;
-
-    rc = libxl__async_exec_start(aes);
-    if (rc)
-        goto out;
-
-    return;
-
-out:
-    dev->aodev.rc = rc;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-static void match_async_exec_cb(libxl__egc *egc,
-                                libxl__async_exec_state *aes,
-                                int rc, int status)
-{
-    libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
-    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
-    libxl__remus_drbd_disk *drbd_disk;
-    const libxl_device_disk *disk = dev->backend_dev;
-
-    STATE_AO_GC(aodev->ao);
-
-    if (rc)
-        goto out;
-
-    if (status) {
-        rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
-        /* BUG: seems to assume that any exit status means `no match' */
-        /* BUG: exit status will have been logged as an error */
-        goto out;
-    }
-
-    /* ops matched */
-    dev->matched = true;
-
-    GCNEW(drbd_disk);
-    dev->concrete_data = drbd_disk;
-    drbd_disk->ackwait = 0;
-    drbd_disk->ctl_fd = open(disk->pdev_path, O_RDONLY);
-    if (drbd_disk->ctl_fd < 0) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-static void drbd_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    libxl__remus_drbd_disk *drbd_disk = dev->concrete_data;
-    STATE_AO_GC(dev->cds->ao);
-
-    close(drbd_disk->ctl_fd);
-    dev->aodev.rc = 0;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-/*----- checkpointing APIs -----*/
-
-/* callbacks */
-static void checkpoint_async_call_done(libxl__egc *egc,
-                                       libxl__ev_child *child,
-                                       pid_t pid, int status);
-
-/* API implementations */
-
-/* this op will not wait and block, so implement as sync op */
-static void drbd_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    STATE_AO_GC(dev->cds->ao);
-
-    libxl__remus_drbd_disk *rdd = dev->concrete_data;
-
-    if (!rdd->ackwait) {
-        if (ioctl(rdd->ctl_fd, DRBD_SEND_CHECKPOINT, 0) <= 0)
-            rdd->ackwait = 1;
-    }
-
-    dev->aodev.rc = 0;
-    dev->aodev.callback(egc, &dev->aodev);
-}
-
-
-static void drbd_preresume_async(libxl__checkpoint_device *dev);
-
-static void drbd_preresume(libxl__egc *egc, libxl__checkpoint_device *dev)
-{
-    ASYNC_CALL(egc, dev->cds->ao, &dev->aodev.child, dev,
-               drbd_preresume_async,
-               checkpoint_async_call_done);
-}
-
-static void drbd_preresume_async(libxl__checkpoint_device *dev)
-{
-    libxl__remus_drbd_disk *rdd = dev->concrete_data;
-    int ackwait = rdd->ackwait;
-
-    if (ackwait) {
-        ioctl(rdd->ctl_fd, DRBD_WAIT_CHECKPOINT_ACK, 0);
-        ackwait = 0;
-    }
-
-    _exit(ackwait);
-}
-
-static void checkpoint_async_call_done(libxl__egc *egc,
-                                       libxl__ev_child *child,
-                                       pid_t pid, int status)
-{
-    int rc;
-    libxl__ao_device *aodev = CONTAINER_OF(child, *aodev, child);
-    libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
-    libxl__remus_drbd_disk *rdd = dev->concrete_data;
-
-    STATE_AO_GC(aodev->ao);
-
-    if (!WIFEXITED(status)) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rdd->ackwait = WEXITSTATUS(status);
-    rc = 0;
-
-out:
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-const libxl__checkpoint_device_instance_ops remus_device_drbd_disk = {
-    .kind = LIBXL__DEVICE_KIND_VBD,
-    .setup = drbd_setup,
-    .teardown = drbd_teardown,
-    .postsuspend = drbd_postsuspend,
-    .preresume = drbd_preresume,
-};
diff --git a/tools/libxl/libxl_save_callout.c b/tools/libxl/libxl_save_callout.c
deleted file mode 100644 (file)
index 0b11495..0000000
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright (C) 2012      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/* stream_fd is as from the caller (eventually, the application).
- * It may be 0, 1 or 2, in which case we need to dup it elsewhere.
- * The actual fd value is not included in the supplied argnums; rather
- * it will be automatically supplied by run_helper as the 2nd argument.
- *
- * preserve_fds are fds that the caller is intending to pass to the
- * helper so which need cloexec clearing.  They may not be 0, 1 or 2.
- * An entry may be -1 in which case it will be ignored.
- */
-static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
-                       const char *mode_arg,
-                       int stream_fd, int back_channel_fd,
-                       const int *preserve_fds, int num_preserve_fds,
-                       const unsigned long *argnums, int num_argnums);
-
-static void helper_failed(libxl__egc*, libxl__save_helper_state *shs, int rc);
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable*, int rc);
-static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
-                                   int fd, short events, short revents);
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
-                          pid_t pid, int status);
-static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs);
-
-/*----- entrypoints -----*/
-
-void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs,
-                              libxl__save_helper_state *shs)
-{
-    STATE_AO_GC(dcs->ao);
-
-    /* Convenience aliases */
-    const uint32_t domid = dcs->guest_domid;
-    const int restore_fd = dcs->libxc_fd;
-    const int send_back_fd = dcs->send_back_fd;
-    libxl__domain_build_state *const state = &dcs->build_state;
-
-    unsigned cbflags =
-        libxl__srm_callout_enumcallbacks_restore(&shs->callbacks.restore.a);
-
-    const unsigned long argnums[] = {
-        domid,
-        state->store_port,
-        state->store_domid, state->console_port,
-        state->console_domid,
-        cbflags, dcs->restore_params.checkpointed_stream,
-    };
-
-    shs->ao = ao;
-    shs->domid = domid;
-    shs->recv_callback = libxl__srm_callout_received_restore;
-    if (dcs->restore_params.checkpointed_stream ==
-        LIBXL_CHECKPOINTED_STREAM_COLO)
-        shs->completion_callback = libxl__colo_restore_teardown;
-    else
-        shs->completion_callback = libxl__xc_domain_restore_done;
-    shs->caller_state = dcs;
-    shs->need_results = 1;
-
-    run_helper(egc, shs, "--restore-domain", restore_fd, send_back_fd, 0, 0,
-               argnums, ARRAY_SIZE(argnums));
-}
-
-void libxl__xc_domain_save(libxl__egc *egc, libxl__domain_save_state *dss,
-                           libxl__save_helper_state *shs)
-{
-    STATE_AO_GC(dss->ao);
-
-    unsigned cbflags =
-        libxl__srm_callout_enumcallbacks_save(&shs->callbacks.save.a);
-
-    const unsigned long argnums[] = {
-        dss->domid, dss->xcflags, cbflags,
-        dss->checkpointed_stream,
-    };
-
-    shs->ao = ao;
-    shs->domid = dss->domid;
-    shs->recv_callback = libxl__srm_callout_received_save;
-    shs->completion_callback = libxl__xc_domain_save_done;
-    shs->caller_state = dss;
-    shs->need_results = 0;
-
-    run_helper(egc, shs, "--save-domain", dss->fd, dss->recv_fd,
-               NULL, 0,
-               argnums, ARRAY_SIZE(argnums));
-    return;
-}
-
-
-void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
-                           libxl__save_helper_state *shs, int return_value)
-{
-    shs->egc = egc;
-    libxl__srm_callout_sendreply(return_value, shs);
-    shs->egc = 0;
-}
-
-void libxl__save_helper_init(libxl__save_helper_state *shs)
-{
-    libxl__ao_abortable_init(&shs->abrt);
-    libxl__ev_fd_init(&shs->readable);
-    libxl__ev_child_init(&shs->child);
-}
-
-/*----- helper execution -----*/
-
-/* This function can not fail. */
-static int dup_cloexec(libxl__gc *gc, int fd, const char *what)
-{
-    int dup_fd = fd;
-
-    if (fd <= 2) {
-        dup_fd = dup(fd);
-        if (dup_fd < 0) {
-            LOGE(ERROR,"dup %s", what);
-            exit(-1);
-        }
-    }
-    libxl_fd_set_cloexec(CTX, dup_fd, 0);
-
-    return dup_fd;
-}
-
-/*
- * Both save and restore share four parameters:
- * 1) Path to libxl-save-helper.
- * 2) --[restore|save]-domain.
- * 3) stream file descriptor.
- * 4) back channel file descriptor.
- * n) save/restore specific parameters.
- * 5) A \0 at the end.
- */
-#define HELPER_NR_ARGS 5
-static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
-                       const char *mode_arg,
-                       int stream_fd, int back_channel_fd,
-                       const int *preserve_fds, int num_preserve_fds,
-                       const unsigned long *argnums, int num_argnums)
-{
-    STATE_AO_GC(shs->ao);
-    const char *args[HELPER_NR_ARGS + num_argnums];
-    const char **arg = args;
-    int i, rc;
-
-    /* Resources we must free */
-    libxl__carefd *childs_pipes[2] = { 0,0 };
-
-    /* Convenience aliases */
-    const uint32_t domid = shs->domid;
-
-    shs->rc = 0;
-    shs->completed = 0;
-    shs->pipes[0] = shs->pipes[1] = 0;
-    libxl__save_helper_init(shs);
-
-    shs->abrt.ao = shs->ao;
-    shs->abrt.callback = helper_stop;
-    rc = libxl__ao_abortable_register(&shs->abrt);
-    if (rc) goto out;
-
-    shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
-                                " stdin pipe", domid);
-    shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
-                                 " stdout pipe", domid);
-
-    *arg++ = getenv("LIBXL_SAVE_HELPER") ?: LIBEXEC_BIN "/" "libxl-save-helper";
-    *arg++ = mode_arg;
-    const char **stream_fd_arg = arg++;
-    const char **back_channel_fd_arg = arg++;
-    for (i=0; i<num_argnums; i++)
-        *arg++ = GCSPRINTF("%lu", argnums[i]);
-    *arg++ = 0;
-    assert(arg == args + ARRAY_SIZE(args));
-
-    libxl__carefd_begin();
-    int childfd;
-    for (childfd=0; childfd<2; childfd++) {
-        /* Setting up the pipe for the child's fd childfd */
-        int fds[2];
-        if (libxl_pipe(CTX,fds)) {
-            rc = ERROR_FAIL;
-            libxl__carefd_unlock();
-            goto out;
-        }
-        int childs_end = childfd==0 ? 0 /*read*/  : 1 /*write*/;
-        int our_end    = childfd==0 ? 1 /*write*/ : 0 /*read*/;
-        childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]);
-        shs->pipes[childfd] =   libxl__carefd_record(CTX, fds[our_end]);
-    }
-    libxl__carefd_unlock();
-
-    pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited);
-    if (!pid) {
-        stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd");
-        *stream_fd_arg = GCSPRINTF("%d", stream_fd);
-
-        if (back_channel_fd >= 0)
-            back_channel_fd = dup_cloexec(gc, back_channel_fd,
-                                          "migration back channel fd");
-        *back_channel_fd_arg = GCSPRINTF("%d", back_channel_fd);
-
-        for (i=0; i<num_preserve_fds; i++)
-            if (preserve_fds[i] >= 0) {
-                assert(preserve_fds[i] > 2);
-                libxl_fd_set_cloexec(CTX, preserve_fds[i], 0);
-            }
-
-        libxl__exec(gc,
-                    libxl__carefd_fd(childs_pipes[0]),
-                    libxl__carefd_fd(childs_pipes[1]),
-                    -1,
-                    args[0], (char**)args, 0);
-    }
-
-    libxl__carefd_close(childs_pipes[0]);
-    libxl__carefd_close(childs_pipes[1]);
-
-    rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable,
-                               libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI);
-    if (rc) goto out;
-    return;
-
- out:
-    libxl__carefd_close(childs_pipes[0]);
-    libxl__carefd_close(childs_pipes[1]);
-    helper_failed(egc, shs, rc);;
-}
-
-static void helper_failed(libxl__egc *egc, libxl__save_helper_state *shs,
-                          int rc)
-{
-    STATE_AO_GC(shs->ao);
-
-    if (!shs->rc)
-        shs->rc = rc;
-
-    libxl__ev_fd_deregister(gc, &shs->readable);
-
-    if (!libxl__save_helper_inuse(shs)) {
-        helper_done(egc, shs);
-        return;
-    }
-
-    libxl__kill(gc, shs->child.pid, SIGKILL, "save/restore helper");
-}
-
-static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
-{
-    libxl__save_helper_state *shs = CONTAINER_OF(abrt, *shs, abrt);
-    STATE_AO_GC(shs->ao);
-
-    if (!libxl__save_helper_inuse(shs)) {
-        helper_failed(egc, shs, rc);
-        return;
-    }
-
-    if (!shs->rc)
-        shs->rc = rc;
-
-    libxl__kill(gc, shs->child.pid, SIGTERM, "save/restore helper");
-}
-
-void libxl__save_helper_abort(libxl__egc *egc,
-                              libxl__save_helper_state *shs)
-{
-    helper_stop(egc, &shs->abrt, ERROR_FAIL);
-}
-
-static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
-                                   int fd, short events, short revents)
-{
-    libxl__save_helper_state *shs = CONTAINER_OF(ev, *shs, readable);
-    STATE_AO_GC(shs->ao);
-    int rc, errnoval;
-
-    if (revents & (POLLERR|POLLPRI)) {
-        LOGD(ERROR, shs->domid, "%s signaled POLLERR|POLLPRI (%#x)",
-             shs->stdout_what, revents);
-        rc = ERROR_FAIL;
- out:
-        /* this is here because otherwise we bypass the decl of msg[] */
-        helper_failed(egc, shs, rc);
-        return;
-    }
-
-    uint16_t msglen;
-    errnoval = libxl_read_exactly(CTX, fd, &msglen, sizeof(msglen),
-                                  shs->stdout_what, "ipc msg header");
-    if (errnoval) { rc = ERROR_FAIL; goto out; }
-
-    unsigned char msg[msglen];
-    errnoval = libxl_read_exactly(CTX, fd, msg, msglen,
-                                  shs->stdout_what, "ipc msg body");
-    if (errnoval) { rc = ERROR_FAIL; goto out; }
-
-    shs->egc = egc;
-    shs->recv_callback(msg, msglen, shs);
-    shs->egc = 0;
-    return;
-}
-
-static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
-                          pid_t pid, int status)
-{
-    libxl__save_helper_state *shs = CONTAINER_OF(ch, *shs, child);
-    STATE_AO_GC(shs->ao);
-
-    /* Convenience aliases */
-    const uint32_t domid = shs->domid;
-
-    const char *what =
-        GCSPRINTF("domain %"PRIu32" save/restore helper", domid);
-
-    if (status) {
-        libxl_report_child_exitstatus(CTX, XTL_ERROR, what, pid, status);
-        if (!shs->rc)
-            shs->rc = ERROR_FAIL;
-    }
-
-    if (shs->need_results) {
-        if (!shs->rc) {
-            LOGD(ERROR,shs->domid,"%s exited without providing results",what);
-            shs->rc = ERROR_FAIL;
-        }
-    }
-
-    if (!shs->completed) {
-        if (!shs->rc) {
-            LOGD(ERROR,shs->domid,"%s exited without signaling completion",what);
-            shs->rc = ERROR_FAIL;
-        }
-    }
-
-    helper_done(egc, shs);
-    return;
-}
-
-static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs)
-{
-    STATE_AO_GC(shs->ao);
-
-    libxl__ao_abortable_deregister(&shs->abrt);
-    libxl__ev_fd_deregister(gc, &shs->readable);
-    libxl__carefd_close(shs->pipes[0]);  shs->pipes[0] = 0;
-    libxl__carefd_close(shs->pipes[1]);  shs->pipes[1] = 0;
-    assert(!libxl__save_helper_inuse(shs));
-
-    shs->egc = egc;
-    shs->completion_callback(egc, shs->caller_state,
-                             shs->rc, shs->retval, shs->errnoval);
-    shs->egc = 0;
-}
-
-/*----- generic helpers for the autogenerated code -----*/
-
-const libxl__srm_save_autogen_callbacks*
-libxl__srm_callout_get_callbacks_save(void *user)
-{
-    libxl__save_helper_state *shs = user;
-    return &shs->callbacks.save.a;
-}
-
-const libxl__srm_restore_autogen_callbacks*
-libxl__srm_callout_get_callbacks_restore(void *user)
-{
-    libxl__save_helper_state *shs = user;
-    return &shs->callbacks.restore.a;
-}
-
-void libxl__srm_callout_sendreply(int r, void *user)
-{
-    libxl__save_helper_state *shs = user;
-    libxl__egc *egc = shs->egc;
-    STATE_AO_GC(shs->ao);
-    int errnoval;
-
-    errnoval = libxl_write_exactly(CTX, libxl__carefd_fd(shs->pipes[0]),
-                                   &r, sizeof(r), shs->stdin_what,
-                                   "callback return value");
-    if (errnoval)
-        helper_failed(egc, shs, ERROR_FAIL);
-}
-
-void libxl__srm_callout_callback_log(uint32_t level, uint32_t errnoval,
-                  const char *context, const char *formatted, void *user)
-{
-    libxl__save_helper_state *shs = user;
-    STATE_AO_GC(shs->ao);
-    xtl_log(CTX->lg, level, errnoval, context, "%s", formatted);
-}
-
-void libxl__srm_callout_callback_progress(const char *context,
-                   const char *doing_what, unsigned long done,
-                   unsigned long total, void *user)
-{
-    libxl__save_helper_state *shs = user;
-    STATE_AO_GC(shs->ao);
-    xtl_progress(CTX->lg, context, doing_what, done, total);
-}
-
-int libxl__srm_callout_callback_complete(int retval, int errnoval,
-                                         void *user)
-{
-    libxl__save_helper_state *shs = user;
-    STATE_AO_GC(shs->ao);
-
-    shs->completed = 1;
-    shs->retval = retval;
-    shs->errnoval = errnoval;
-    libxl__ev_fd_deregister(gc, &shs->readable);
-    return 0;
-}
diff --git a/tools/libxl/libxl_save_helper.c b/tools/libxl/libxl_save_helper.c
deleted file mode 100644 (file)
index 65dff38..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2012      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-/*
- * The libxl-save-helper utility speaks a protocol to its caller for
- * the callbacks.  The protocol is as follows.
- *
- * The helper talks on stdin and stdout, in binary in machine
- * endianness.  The helper speaks first, and only when it has a
- * callback to make.  It writes a 16-bit number being the message
- * length, and then the message body.
- *
- * Each message starts with a 16-bit number indicating which of the
- * messages it is, and then some arguments in a binary marshalled form.
- * If the callback does not need a reply (it returns void), the helper
- * just continues.  Otherwise the helper waits for its caller to send a
- * single int which is to be the return value from the callback.
- *
- * Where feasible the stubs and callbacks have prototypes identical to
- * those required by xc_domain_save and xc_domain_restore, so that the
- * autogenerated functions can be used/provided directly.
- *
- * The actual messages are in the array @msgs in libxl_save_msgs_gen.pl
- */
-
-#include "libxl_osdeps.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <assert.h>
-#include <inttypes.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#include "libxl.h"
-#include "libxl_utils.h"
-
-#include "xenctrl.h"
-#include "xenguest.h"
-#include "_libxl_save_msgs_helper.h"
-
-/*----- logger -----*/
-
-__attribute__((format(printf, 5, 0)))
-static void tellparent_vmessage(xentoollog_logger *logger_in,
-                                xentoollog_level level,
-                                int errnoval,
-                                const char *context,
-                                const char *format,
-                                va_list al)
-{
-    char *formatted;
-    int r = vasprintf(&formatted, format, al);
-    if (r < 0) { perror("memory allocation failed during logging"); exit(-1); }
-    helper_stub_log(level, errnoval, context, formatted, 0);
-    free(formatted);
-}
-
-static void tellparent_progress(struct xentoollog_logger *logger_in,
-                                const char *context,
-                                const char *doing_what, int percent,
-                                unsigned long done, unsigned long total)
-{
-    helper_stub_progress(context, doing_what, done, total, 0);
-}
-
-static void tellparent_destroy(struct xentoollog_logger *logger_in)
-{
-    abort();
-}
-
-/*----- globals -----*/
-
-static const char *program = "libxl-save-helper";
-static xentoollog_logger logger = {
-    tellparent_vmessage,
-    tellparent_progress,
-    tellparent_destroy,
-};
-static xc_interface *xch;
-static int io_fd;
-
-/*----- error handling -----*/
-
-static void fail(int errnoval, const char *fmt, ...)
-    __attribute__((noreturn,format(printf,2,3)));
-static void fail(int errnoval, const char *fmt, ...)
-{
-    va_list al;
-    va_start(al,fmt);
-    xtl_logv(&logger,XTL_ERROR,errnoval,program,fmt,al);
-    exit(-1);
-}
-
-static int read_exactly(int fd, void *buf, size_t len)
-/* returns 0 if we get eof, even if we got it midway through; 1 if ok */
-{
-    while (len) {
-        ssize_t r = read(fd, buf, len);
-        if (r<=0) return r;
-        assert(r <= len);
-        len -= r;
-        buf = (char*)buf + r;
-    }
-    return 1;
-}
-
-static void *xmalloc(size_t sz)
-{
-    if (!sz) return 0;
-    void *r = malloc(sz);
-    if (!r) { perror("memory allocation failed"); exit(-1); }
-    return r;
-}
-
-/*----- signal handling -----*/
-
-static int unwriteable_fd;
-
-static void save_signal_handler(int num)
-{
-    /*
-     * We want to be able to interrupt save.  But the code in libxc
-     * which does the actual saving is straight-through, and we need
-     * to execute its error path to put the guest back to sanity.
-     *
-     * So what we do is this: when we get the signal, we dup2
-     * the result of open("/dev/null",O_RDONLY) onto the output fd.
-     *
-     * This is guaranteed to 1. interrupt libxc's write (causing it to
-     * return short, or maybe EINTR); 2. make the next write give
-     * EBADF, so that: 3. at latest, libxc will notice when it next
-     * tries to write data and will then go into its cleanup path.
-     *
-     * We make no effort here to sanitise the resulting errors.
-     * That's libxl's job.
-     */
-    int esave = errno;
-
-    int r = dup2(unwriteable_fd, io_fd);
-    if (r != io_fd)
-        /* we can't write an xtl message because we might end up
-         * interleaving on our control stream; we can't use stdio
-         * because it's not async-signal-safe */
-        abort();
-
-    errno = esave;
-}
-
-static void setup_signals(void (*handler)(int))
-{
-    struct sigaction sa;
-    sigset_t spmask;
-    int r;
-
-    unwriteable_fd = open("/dev/null",O_RDONLY);
-    if (unwriteable_fd < 0) fail(errno,"open /dev/null for reading");
-
-    LIBXL_FILLZERO(sa);
-    sa.sa_handler = handler;
-    sigemptyset(&sa.sa_mask);
-    r = sigaction(SIGTERM, &sa, 0);
-    if (r) fail(errno,"sigaction SIGTERM failed");
-
-    sigemptyset(&spmask);
-    sigaddset(&spmask,SIGTERM);
-    r = sigprocmask(SIG_UNBLOCK,&spmask,0);
-    if (r) fail(errno,"sigprocmask unblock SIGTERM failed");
-}
-
-/*----- helper functions called by autogenerated stubs -----*/
-
-unsigned char * helper_allocbuf(int len, void *user)
-{
-    return xmalloc(len);
-}
-
-static void transmit(const unsigned char *msg, int len, void *user)
-{
-    while (len) {
-        int r = write(1, msg, len);
-        if (r<0) { perror("write"); exit(-1); }
-        assert(r >= 0);
-        assert(r <= len);
-        len -= r;
-        msg += r;
-    }
-}
-
-void helper_transmitmsg(unsigned char *msg_freed, int len_in, void *user)
-{
-    assert(len_in < 64*1024);
-    uint16_t len = len_in;
-    transmit((const void*)&len, sizeof(len), user);
-    transmit(msg_freed, len, user);
-    free(msg_freed);
-}
-
-int helper_getreply(void *user)
-{
-    int v;
-    int r = read_exactly(0, &v, sizeof(v));
-    if (r<=0) exit(-2);
-    return v;
-}
-
-/*----- other callbacks -----*/
-
-static void startup(const char *op) {
-    xtl_log(&logger,XTL_DEBUG,0,program,"starting %s",op);
-
-    xch = xc_interface_open(&logger,&logger,0);
-    if (!xch) fail(errno,"xc_interface_open failed");
-}
-
-static void complete(int retval) {
-    int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */
-    xtl_log(&logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval);
-    helper_stub_complete(retval,errnoval,0);
-    xc_interface_close(xch);
-    exit(0);
-}
-
-int main(int argc, char **argv)
-{
-    int r;
-    int send_back_fd, recv_fd;
-
-#define NEXTARG (++argv, assert(*argv), *argv)
-
-    const char *mode = *++argv;
-    assert(mode);
-
-    if (!strcmp(mode,"--save-domain")) {
-        static struct save_callbacks cb;
-
-        io_fd =                             atoi(NEXTARG);
-        recv_fd =                           atoi(NEXTARG);
-        uint32_t dom =                      strtoul(NEXTARG,0,10);
-        uint32_t flags =                    strtoul(NEXTARG,0,10);
-        unsigned cbflags =                  strtoul(NEXTARG,0,10);
-        xc_stream_type_t stream_type =      strtoul(NEXTARG,0,10);
-        assert(!*++argv);
-
-        helper_setcallbacks_save(&cb, cbflags);
-
-        startup("save");
-        setup_signals(save_signal_handler);
-
-        r = xc_domain_save(xch, io_fd, dom, flags, &cb, stream_type, recv_fd);
-        complete(r);
-
-    } else if (!strcmp(mode,"--restore-domain")) {
-        static struct restore_callbacks cb;
-
-        io_fd =                             atoi(NEXTARG);
-        send_back_fd =                      atoi(NEXTARG);
-        uint32_t dom =                      strtoul(NEXTARG,0,10);
-        unsigned store_evtchn =             strtoul(NEXTARG,0,10);
-        domid_t store_domid =               strtoul(NEXTARG,0,10);
-        unsigned console_evtchn =           strtoul(NEXTARG,0,10);
-        domid_t console_domid =             strtoul(NEXTARG,0,10);
-        unsigned cbflags =                  strtoul(NEXTARG,0,10);
-        xc_stream_type_t stream_type =      strtoul(NEXTARG,0,10);
-        assert(!*++argv);
-
-        helper_setcallbacks_restore(&cb, cbflags);
-
-        unsigned long store_mfn = 0;
-        unsigned long console_mfn = 0;
-
-        startup("restore");
-        setup_signals(SIG_DFL);
-
-        r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn,
-                              store_domid, console_evtchn, &console_mfn,
-                              console_domid, stream_type, &cb, send_back_fd);
-        helper_stub_restore_results(store_mfn,console_mfn,0);
-        complete(r);
-
-    } else {
-        assert(!"unexpected mode argument");
-    }
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_save_msgs_gen.pl b/tools/libxl/libxl_save_msgs_gen.pl
deleted file mode 100755 (executable)
index 5bfbd4f..0000000
+++ /dev/null
@@ -1,396 +0,0 @@
-#!/usr/bin/perl -w
-
-use warnings;
-use strict;
-use POSIX;
-
-our $debug = 0; # produce copious debugging output at run-time?
-
-our @msgs = (
-    # flags:
-    #   s  - applicable to save
-    #   r  - applicable to restore
-    #   c  - function pointer in callbacks struct rather than fixed function
-    #   x  - function pointer is in struct {save,restore}_callbacks
-    #         and its null-ness needs to be passed through to the helper's xc
-    #   W  - needs a return value; callback is synchronous
-    #   A  - needs a return value; callback is asynchronous
-    [ 'sr',     "log",                   [qw(uint32_t level
-                                             uint32_t errnoval
-                                             STRING context
-                                             STRING formatted)] ],
-    [ 'sr',     "progress",              [qw(STRING context
-                                             STRING doing_what),
-                                            'unsigned long', 'done',
-                                            'unsigned long', 'total'] ],
-    [ 'srcxA',  "suspend", [] ],
-    [ 'srcxA',  "postcopy", [] ],
-    [ 'srcxA',  "checkpoint", [] ],
-    [ 'srcxA',  "wait_checkpoint", [] ],
-    [ 'scxA',   "switch_qemu_logdirty",  [qw(uint32_t domid
-                                          unsigned enable)] ],
-    [ 'rcxW',   "static_data_done",      [qw(unsigned missing)] ],
-    [ 'rcx',    "restore_results",       ['xen_pfn_t', 'store_gfn',
-                                          'xen_pfn_t', 'console_gfn'] ],
-    [ 'srW',    "complete",              [qw(int retval
-                                             int errnoval)] ],
-);
-
-#----------------------------------------
-
-our %cbs;
-our %func;
-our %func_ah;
-our @outfuncs;
-our %out_decls;
-our %out_body;
-our $msgnum = 0;
-
-die unless @ARGV==1;
-die if $ARGV[0] =~ m/^-/;
-
-our ($intendedout) = @ARGV;
-
-$intendedout =~ m/([a-z]+)\.([ch])$/ or die;
-my ($want_ah, $ch) = ($1, $2);
-
-my $declprefix = '';
-
-foreach my $ah (qw(callout helper)) {
-    $out_body{$ah} .=
-        <<END_BOTH.($ah eq 'callout' ? <<END_CALLOUT : <<END_HELPER);
-#include "libxl_osdeps.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-END_BOTH
-
-#include "libxl_internal.h"
-
-END_CALLOUT
-
-#include <xenctrl.h>
-#include <xenguest.h>
-#include "_libxl_save_msgs_${ah}.h"
-
-END_HELPER
-}
-
-die $want_ah unless defined $out_body{$want_ah};
-
-sub f_decl ($$$$) {
-    my ($name, $ah, $c_rtype, $c_decl) = @_;
-    $out_decls{$name} = "${declprefix}$c_rtype $name$c_decl;\n";
-    $func{$name} = "$c_rtype $name$c_decl\n{\n" . ($func{$name} || '');
-    $func_ah{$name} = $ah;
-}
-
-sub f_more ($$) {
-    my ($name, $addbody) = @_;
-    $func{$name} ||= '';
-    $func{$name} .= $addbody;
-    push @outfuncs, $name;
-}
-
-our $libxl = "libxl__srm";
-our $callback = "${libxl}_callout_callback";
-our $receiveds = "${libxl}_callout_received";
-our $sendreply = "${libxl}_callout_sendreply";
-our $getcallbacks = "${libxl}_callout_get_callbacks";
-our $enumcallbacks = "${libxl}_callout_enumcallbacks";
-sub cbtype ($) { "${libxl}_".$_[0]."_autogen_callbacks"; };
-
-f_decl($sendreply, 'callout', 'void', "(int r, void *user)");
-
-our $helper = "helper";
-our $encode = "${helper}_stub";
-our $allocbuf = "${helper}_allocbuf";
-our $transmit = "${helper}_transmitmsg";
-our $getreply = "${helper}_getreply";
-our $setcallbacks = "${helper}_setcallbacks";
-
-f_decl($allocbuf, 'helper', 'unsigned char *', '(int len, void *user)');
-f_decl($transmit, 'helper', 'void',
-       '(unsigned char *msg_freed, int len, void *user)');
-f_decl($getreply, 'helper', 'int', '(void *user)');
-
-sub typeid ($) { my ($t) = @_; $t =~ s/\W/_/; return $t; };
-
-$out_body{'callout'} .= <<END;
-static int bytes_get(const unsigned char **msg,
-                    const unsigned char *const endmsg,
-                    void *result, int rlen)
-{
-    if (endmsg - *msg < rlen) return 0;
-    memcpy(result, *msg, rlen);
-    *msg += rlen;
-    return 1;
-}
-
-END
-$out_body{'helper'} .= <<END;
-static void bytes_put(unsigned char *const buf, int *len,
-                     const void *value, int vlen)
-{
-    assert(vlen < INT_MAX/2 - *len);
-    if (buf)
-       memcpy(buf + *len, value, vlen);
-    *len += vlen;
-}
-
-END
-
-foreach my $simpletype (qw(int uint16_t uint32_t unsigned), 'unsigned long', 'xen_pfn_t') {
-    my $typeid = typeid($simpletype);
-    $out_body{'callout'} .= <<END;
-static int ${typeid}_get(const unsigned char **msg,
-                        const unsigned char *const endmsg,
-                        $simpletype *result)
-{
-    return bytes_get(msg, endmsg, result, sizeof(*result));
-}
-
-END
-    $out_body{'helper'} .= <<END;
-static void ${typeid}_put(unsigned char *const buf, int *len,
-                        const $simpletype value)
-{
-    bytes_put(buf, len, &value, sizeof(value));
-}
-
-END
-}
-
-$out_body{'callout'} .= <<END;
-static int BLOCK_get(const unsigned char **msg,
-                      const unsigned char *const endmsg,
-                      const uint8_t **result, uint32_t *result_size)
-{
-    if (!uint32_t_get(msg, endmsg, result_size)) return 0;
-    if (endmsg - *msg < *result_size) return 0;
-    *result = (const void*)*msg;
-    *msg += *result_size;
-    return 1;
-}
-
-static int STRING_get(const unsigned char **msg,
-                      const unsigned char *const endmsg,
-                      const char **result)
-{
-    const uint8_t *data;
-    uint32_t datalen;
-    if (!BLOCK_get(msg, endmsg, &data, &datalen)) return 0;
-    if (datalen == 0) return 0;
-    if (data[datalen-1] != '\\0') return 0;
-    *result = (const void*)data;
-    return 1;
-}
-
-END
-$out_body{'helper'} .= <<END;
-static void BLOCK_put(unsigned char *const buf,
-                      int *len,
-                     const uint8_t *bytes, uint32_t size)
-{
-    uint32_t_put(buf, len, size);
-    bytes_put(buf, len, bytes, size);
-}
-
-static void STRING_put(unsigned char *const buf,
-                      int *len,
-                      const char *string)
-{
-    size_t slen = strlen(string);
-    assert(slen < INT_MAX / 4);
-    assert(slen < (uint32_t)0x40000000);
-    BLOCK_put(buf, len, (const void*)string, slen+1);
-}
-
-END
-
-foreach my $sr (qw(save restore)) {
-    f_decl("${getcallbacks}_${sr}", 'callout',
-           "const ".cbtype($sr)." *",
-           "(void *data)");
-
-    f_decl("${receiveds}_${sr}", 'callout', 'int',
-          "(const unsigned char *msg, uint32_t len, void *user)");
-
-    f_decl("${enumcallbacks}_${sr}", 'callout', 'unsigned',
-           "(const ".cbtype($sr)." *cbs)");
-    f_more("${enumcallbacks}_${sr}", "    unsigned cbflags = 0;\n");
-
-    f_decl("${setcallbacks}_${sr}", 'helper', 'void',
-           "(struct ${sr}_callbacks *cbs, unsigned cbflags)");
-
-    f_more("${receiveds}_${sr}",
-           <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
-    const unsigned char *const endmsg = msg + len;
-    uint16_t mtype;
-    if (!uint16_t_get(&msg, endmsg, &mtype)) return 0;
-END_ALWAYS
-    fprintf(stderr,"libxl callout receiver: got len=%u mtype=%u\\n",len,mtype);
-END_DEBUG
-    switch (mtype) {
-
-END_ALWAYS
-
-    $cbs{$sr} = "typedef struct ".cbtype($sr)." {\n";
-}
-
-foreach my $msginfo (@msgs) {
-    my ($flags, $name, $args) = @$msginfo;
-    $msgnum++;
-
-    my $f_more_sr = sub {
-        my ($contents_spec, $fnamebase) = @_;
-        $fnamebase ||= "${receiveds}";
-        foreach my $sr (qw(save restore)) {
-            $sr =~ m/^./;
-            next unless $flags =~ m/$&/;
-            my $contents = (!ref $contents_spec) ? $contents_spec :
-                $contents_spec->($sr);
-            f_more("${fnamebase}_${sr}", $contents);
-        }
-    };
-
-    $f_more_sr->("    case $msgnum: { /* $name */\n");
-    if ($flags =~ m/W/) {
-        $f_more_sr->("        int r;\n");
-    }
-
-    my $c_rtype_helper = $flags =~ m/[WA]/ ? 'int' : 'void';
-    my $c_rtype_callout = $flags =~ m/W/ ? 'int' : 'void';
-    my $c_decl = '(';
-    my $c_callback_args = '';
-
-    f_more("${encode}_$name",
-           <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
-    unsigned char *buf = 0;
-    int len = 0, allocd = 0;
-
-END_ALWAYS
-    fprintf(stderr,"libxl-save-helper: encoding $name\\n");
-END_DEBUG
-    for (;;) {
-        uint16_t_put(buf, &len, $msgnum /* $name */);
-END_ALWAYS
-
-    my @args = @$args;
-    my $c_recv = '';
-    my ($argtype, $arg);
-    while (($argtype, $arg, @args) = @args) {
-       my $typeid = typeid($argtype);
-        my $c_args = "$arg";
-        my $c_get_args = "&$arg";
-       if ($argtype eq 'STRING') {
-           $c_decl .= "const char *$arg, ";
-           $f_more_sr->("        const char *$arg;\n");
-        } elsif ($argtype eq 'BLOCK') {
-            $c_decl .= "const uint8_t *$arg, uint32_t ${arg}_size, ";
-            $c_args .= ", ${arg}_size";
-            $c_get_args .= ", &${arg}_size";
-           $f_more_sr->("        const uint8_t *$arg;\n".
-                         "        uint32_t ${arg}_size;\n");
-       } else {
-           $c_decl .= "$argtype $arg, ";
-           $f_more_sr->("        $argtype $arg;\n");
-       }
-       $c_callback_args .= "$c_args, ";
-       $c_recv.=
-            "        if (!${typeid}_get(&msg, endmsg, $c_get_args)) return 0;\n";
-        f_more("${encode}_$name", "    ${typeid}_put(buf, &len, $c_args);\n");
-    }
-    $f_more_sr->($c_recv);
-    $c_decl .= "void *user)";
-    $c_callback_args .= "user";
-
-    $f_more_sr->("        if (msg != endmsg) return 0;\n");
-
-    my $c_callback;
-    if ($flags !~ m/c/) {
-        $c_callback = "${callback}_$name";
-    } else {
-        $f_more_sr->(sub {
-            my ($sr) = @_;
-            $cbs{$sr} .= "    $c_rtype_callout (*${name})$c_decl;\n";
-            return
-          "        const ".cbtype($sr)." *const cbs =\n".
-            "            ${getcallbacks}_${sr}(user);\n";
-                       });
-        $c_callback = "cbs->${name}";
-    }
-    my $c_make_callback = "$c_callback($c_callback_args)";
-    if ($flags !~ m/W/) {
-       $f_more_sr->("        $c_make_callback;\n");
-    } else {
-        $f_more_sr->("        r = $c_make_callback;\n".
-                     "        $sendreply(r, user);\n");
-       f_decl($sendreply, 'callout', 'void', '(int r, void *user)');
-    }
-    if ($flags =~ m/x/) {
-        my $c_v = "(1u<<$msgnum)";
-        my $c_cb = "cbs->$name";
-        $f_more_sr->("    if ($c_cb) cbflags |= $c_v;\n", $enumcallbacks);
-        $f_more_sr->("    if (cbflags & $c_v) $c_cb = ${encode}_${name};\n",
-                     $setcallbacks);
-    }
-    $f_more_sr->("        return 1;\n    }\n\n");
-    f_decl("${callback}_$name", 'callout', $c_rtype_callout, $c_decl);
-    f_decl("${encode}_$name", 'helper', $c_rtype_helper, $c_decl);
-    f_more("${encode}_$name",
-"        if (buf) break;
-        buf = ${helper}_allocbuf(len, user);
-        assert(buf);
-        allocd = len;
-        len = 0;
-    }
-    assert(len == allocd);
-    ${transmit}(buf, len, user);
-");
-    if ($flags =~ m/[WA]/) {
-       f_more("${encode}_$name",
-               (<<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS));
-    int r = ${helper}_getreply(user);
-END_ALWAYS
-    fprintf(stderr,"libxl-save-helper: $name got reply %d\\n",r);
-END_DEBUG
-    return r;
-END_ALWAYS
-    }
-}
-
-print "/* AUTOGENERATED by $0 DO NOT EDIT */\n\n" or die $!;
-
-foreach my $sr (qw(save restore)) {
-    f_more("${enumcallbacks}_${sr}",
-           "    return cbflags;\n");
-    f_more("${receiveds}_${sr}",
-           "    default:\n".
-           "        return 0;\n".
-           "    }\n");
-    $cbs{$sr} .= "} ".cbtype($sr).";\n\n";
-    if ($ch eq 'h') {
-        print $cbs{$sr} or die $!;
-        print "struct ${sr}_callbacks;\n";
-    }
-}
-
-if ($ch eq 'c') {
-    foreach my $name (@outfuncs) {
-        next unless defined $func{$name};
-        $func{$name} .= "}\n\n";
-        $out_body{$func_ah{$name}} .= $func{$name};
-        delete $func{$name};
-    }
-    print $out_body{$want_ah} or die $!;
-} else {
-    foreach my $name (sort keys %out_decls) {
-        next unless $func_ah{$name} eq $want_ah;
-        print $out_decls{$name} or die $!;
-    }
-}
-
-close STDOUT or die $!;
diff --git a/tools/libxl/libxl_sched.c b/tools/libxl/libxl_sched.c
deleted file mode 100644 (file)
index 7c53dc6..0000000
+++ /dev/null
@@ -1,986 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid,
-                                   uint32_t vcpuid,
-                                   const libxl_bitmap *cpumap_hard,
-                                   const libxl_bitmap *cpumap_soft,
-                                   unsigned flags)
-{
-    GC_INIT(ctx);
-    libxl_bitmap hard, soft;
-    int rc;
-
-    libxl_bitmap_init(&hard);
-    libxl_bitmap_init(&soft);
-
-    if (!cpumap_hard && !cpumap_soft && !flags) {
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    /*
-     * Xen wants writable hard and/or soft cpumaps, to put back in them
-     * the effective hard and/or soft affinity that will be used.
-     */
-    if (cpumap_hard) {
-        rc = libxl_cpu_bitmap_alloc(ctx, &hard, 0);
-        if (rc)
-            goto out;
-
-        libxl__bitmap_copy_best_effort(gc, &hard, cpumap_hard);
-        flags |= XEN_VCPUAFFINITY_HARD;
-    }
-    if (cpumap_soft) {
-        rc = libxl_cpu_bitmap_alloc(ctx, &soft, 0);
-        if (rc)
-            goto out;
-
-        libxl__bitmap_copy_best_effort(gc, &soft, cpumap_soft);
-        flags |= XEN_VCPUAFFINITY_SOFT;
-    }
-
-    if (xc_vcpu_setaffinity(ctx->xch, domid, vcpuid,
-                            cpumap_hard ? hard.map : NULL,
-                            cpumap_soft ? soft.map : NULL,
-                            flags)) {
-        LOGED(ERROR, domid, "Setting vcpu affinity");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /*
-     * Let's check the results. Hard affinity will never be empty, but it
-     * is possible that Xen will use something different from what we asked
-     * for various reasons. If that's the case, report it.
-     */
-    if (cpumap_hard &&
-        !libxl_bitmap_equal(cpumap_hard, &hard, 0))
-        LOGD(DEBUG, domid, "New hard affinity for vcpu %d has unreachable cpus", vcpuid);
-    /*
-     * Soft affinity can both be different from what asked and empty. Check
-     * for (and report) both.
-     */
-    if (cpumap_soft) {
-        if (!libxl_bitmap_equal(cpumap_soft, &soft, 0))
-            LOGD(DEBUG, domid, "New soft affinity for vcpu %d has unreachable cpus",
-                 vcpuid);
-        if (libxl_bitmap_is_empty(&soft))
-            LOGD(WARN, domid, "All cpus in soft affinity of vcpu %d are unreachable."
-                 " Only hard affinity will be considered for scheduling",
-                 vcpuid);
-    }
-
-    rc = 0;
- out:
-    libxl_bitmap_dispose(&hard);
-    libxl_bitmap_dispose(&soft);
-    GC_FREE;
-    return rc;
-}
-
-int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
-                           const libxl_bitmap *cpumap_hard,
-                           const libxl_bitmap *cpumap_soft)
-{
-    return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
-                                   cpumap_soft, 0);
-}
-
-int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid,
-                                 uint32_t vcpuid,
-                                 const libxl_bitmap *cpumap_hard,
-                                 const libxl_bitmap *cpumap_soft)
-{
-    return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard,
-                                   cpumap_soft, XEN_VCPUAFFINITY_FORCE);
-}
-
-int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
-                               unsigned int max_vcpus,
-                               const libxl_bitmap *cpumap_hard,
-                               const libxl_bitmap *cpumap_soft)
-{
-    GC_INIT(ctx);
-    int i, rc = 0;
-
-    for (i = 0; i < max_vcpus; i++) {
-        if (libxl_set_vcpuaffinity(ctx, domid, i, cpumap_hard, cpumap_soft)) {
-            LOGD(WARN, domid, "Failed to set affinity for %d", i);
-            rc = ERROR_FAIL;
-        }
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_bitmap *nodemap)
-{
-    GC_INIT(ctx);
-    if (xc_domain_node_setaffinity(ctx->xch, domid, nodemap->map)) {
-        LOGED(ERROR, domid, "Setting node affinity");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    GC_FREE;
-    return 0;
-}
-
-int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_bitmap *nodemap)
-{
-    GC_INIT(ctx);
-    if (xc_domain_node_getaffinity(ctx->xch, domid, nodemap->map)) {
-        LOGED(ERROR, domid, "Getting node affinity");
-        GC_FREE;
-        return ERROR_FAIL;
-    }
-    GC_FREE;
-    return 0;
-}
-
-int libxl_get_scheduler(libxl_ctx *ctx)
-{
-    int r, sched;
-
-    GC_INIT(ctx);
-    r = xc_sched_id(ctx->xch, &sched);
-    if (r != 0) {
-        LOGE(ERROR, "getting current scheduler id");
-        sched = ERROR_FAIL;
-    }
-    GC_FREE;
-    return sched;
-}
-
-static int sched_arinc653_domain_set(libxl__gc *gc, uint32_t domid,
-                                     const libxl_domain_sched_params *scinfo)
-{
-    /* Currently, the ARINC 653 scheduler does not take any domain-specific
-         configuration, so we simply return success. */
-    return 0;
-}
-
-static int sched_null_domain_set(libxl__gc *gc, uint32_t domid,
-                                 const libxl_domain_sched_params *scinfo)
-{
-    /* There aren't any domain-specific parameters to be set. */
-    return 0;
-}
-
-static int sched_null_domain_get(libxl__gc *gc, uint32_t domid,
-                                 libxl_domain_sched_params *scinfo)
-{
-    /* There aren't any domain-specific parameters to return. */
-    return 0;
-}
-
-static int sched_credit_domain_get(libxl__gc *gc, uint32_t domid,
-                                   libxl_domain_sched_params *scinfo)
-{
-    struct xen_domctl_sched_credit sdom;
-    int rc;
-
-    rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
-    if (rc != 0) {
-        LOGED(ERROR, domid, "Getting domain sched credit");
-        return ERROR_FAIL;
-    }
-
-    libxl_domain_sched_params_init(scinfo);
-    scinfo->sched = LIBXL_SCHEDULER_CREDIT;
-    scinfo->weight = sdom.weight;
-    scinfo->cap = sdom.cap;
-
-    return 0;
-}
-
-static int sched_credit_domain_set(libxl__gc *gc, uint32_t domid,
-                                   const libxl_domain_sched_params *scinfo)
-{
-    struct xen_domctl_sched_credit sdom;
-    xc_domaininfo_t domaininfo;
-    int rc;
-
-    rc = xc_domain_getinfolist(CTX->xch, domid, 1, &domaininfo);
-    if (rc < 0) {
-        LOGED(ERROR, domid, "Getting domain info list");
-        return ERROR_FAIL;
-    }
-    if (rc != 1 || domaininfo.domain != domid)
-        return ERROR_INVAL;
-
-    rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
-    if (rc != 0) {
-        LOGED(ERROR, domid, "Getting domain sched credit");
-        return ERROR_FAIL;
-    }
-
-    if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
-        if (scinfo->weight < 1 || scinfo->weight > 65535) {
-            LOGD(ERROR, domid, "Cpu weight out of range, "
-                 "valid values are within range from 1 to 65535");
-            return ERROR_INVAL;
-        }
-        sdom.weight = scinfo->weight;
-    }
-
-    if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
-        if (scinfo->cap < 0
-            || scinfo->cap > (domaininfo.max_vcpu_id + 1) * 100) {
-            LOGD(ERROR, domid, "Cpu cap out of range, "
-                 "valid range is from 0 to %d for specified number of vcpus",
-                 ((domaininfo.max_vcpu_id + 1) * 100));
-            return ERROR_INVAL;
-        }
-        sdom.cap = scinfo->cap;
-    }
-
-    rc = xc_sched_credit_domain_set(CTX->xch, domid, &sdom);
-    if ( rc < 0 ) {
-        LOGED(ERROR, domid, "Setting domain sched credit");
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-static int sched_ratelimit_check(libxl__gc *gc, int ratelimit)
-{
-    if (ratelimit != 0 &&
-        (ratelimit <  XEN_SYSCTL_SCHED_RATELIMIT_MIN ||
-         ratelimit > XEN_SYSCTL_SCHED_RATELIMIT_MAX)) {
-        LOG(ERROR, "Ratelimit out of range, valid range is from %d to %d",
-            XEN_SYSCTL_SCHED_RATELIMIT_MIN, XEN_SYSCTL_SCHED_RATELIMIT_MAX);
-        return ERROR_INVAL;
-    }
-
-    return 0;
-}
-
-int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
-                                  libxl_sched_credit_params *scinfo)
-{
-    struct xen_sysctl_credit_schedule sparam;
-    int r, rc;
-    GC_INIT(ctx);
-
-    r = xc_sched_credit_params_get(ctx->xch, poolid, &sparam);
-    if (r < 0) {
-        LOGE(ERROR, "getting Credit scheduler parameters");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    scinfo->tslice_ms = sparam.tslice_ms;
-    scinfo->ratelimit_us = sparam.ratelimit_us;
-    scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
-
-    rc = 0;
- out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
-                                  libxl_sched_credit_params *scinfo)
-{
-    struct xen_sysctl_credit_schedule sparam;
-    int r, rc;
-    GC_INIT(ctx);
-
-    if (scinfo->tslice_ms <  XEN_SYSCTL_CSCHED_TSLICE_MIN
-        || scinfo->tslice_ms > XEN_SYSCTL_CSCHED_TSLICE_MAX) {
-        LOG(ERROR, "Time slice out of range, valid range is from %d to %d",
-            XEN_SYSCTL_CSCHED_TSLICE_MIN, XEN_SYSCTL_CSCHED_TSLICE_MAX);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
-    if (rc) {
-        goto out;
-    }
-    if (scinfo->ratelimit_us > scinfo->tslice_ms*1000) {
-        LOG(ERROR, "Ratelimit cannot be greater than timeslice");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    if (scinfo->vcpu_migr_delay_us > XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US) {
-        LOG(ERROR, "vcpu migration delay should be >= 0 and <= %dus",
-            XEN_SYSCTL_CSCHED_MGR_DLY_MAX_US);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    sparam.tslice_ms = scinfo->tslice_ms;
-    sparam.ratelimit_us = scinfo->ratelimit_us;
-    sparam.vcpu_migr_delay_us = scinfo->vcpu_migr_delay_us;
-
-    r = xc_sched_credit_params_set(ctx->xch, poolid, &sparam);
-    if ( r < 0 ) {
-        LOGE(ERROR, "Setting Credit scheduler parameters");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    scinfo->tslice_ms = sparam.tslice_ms;
-    scinfo->ratelimit_us = sparam.ratelimit_us;
-    scinfo->vcpu_migr_delay_us = sparam.vcpu_migr_delay_us;
-
-    rc = 0;
- out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid,
-                                   libxl_sched_credit2_params *scinfo)
-{
-    struct xen_sysctl_credit2_schedule sparam;
-    int r, rc;
-    GC_INIT(ctx);
-
-    r = xc_sched_credit2_params_get(ctx->xch, poolid, &sparam);
-    if (r < 0) {
-        LOGE(ERROR, "getting Credit2 scheduler parameters");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    scinfo->ratelimit_us = sparam.ratelimit_us;
-
-    rc = 0;
- out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid,
-                                   libxl_sched_credit2_params *scinfo)
-{
-    struct xen_sysctl_credit2_schedule sparam;
-    int r, rc;
-    GC_INIT(ctx);
-
-    rc = sched_ratelimit_check(gc, scinfo->ratelimit_us);
-    if (rc) goto out;
-
-    sparam.ratelimit_us = scinfo->ratelimit_us;
-
-    r = xc_sched_credit2_params_set(ctx->xch, poolid, &sparam);
-    if (r < 0) {
-        LOGE(ERROR, "Setting Credit2 scheduler parameters");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    scinfo->ratelimit_us = sparam.ratelimit_us;
-
-    rc = 0;
- out:
-    GC_FREE;
-    return rc;
-}
-
-static int sched_credit2_domain_get(libxl__gc *gc, uint32_t domid,
-                                    libxl_domain_sched_params *scinfo)
-{
-    struct xen_domctl_sched_credit2 sdom;
-    int rc;
-
-    rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
-    if (rc != 0) {
-        LOGED(ERROR, domid, "Getting domain sched credit2");
-        return ERROR_FAIL;
-    }
-
-    libxl_domain_sched_params_init(scinfo);
-    scinfo->sched = LIBXL_SCHEDULER_CREDIT2;
-    scinfo->weight = sdom.weight;
-    scinfo->cap = sdom.cap;
-
-    return 0;
-}
-
-static int sched_credit2_domain_set(libxl__gc *gc, uint32_t domid,
-                                    const libxl_domain_sched_params *scinfo)
-{
-    struct xen_domctl_sched_credit2 sdom;
-    xc_domaininfo_t info;
-    int rc;
-
-    rc = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
-    if (rc < 0) {
-        LOGED(ERROR, domid, "Getting domain info");
-        return ERROR_FAIL;
-    }
-    if (rc != 1 || info.domain != domid)
-        return ERROR_INVAL;
-
-    rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
-    if (rc != 0) {
-        LOGED(ERROR, domid, "Getting domain sched credit2");
-        return ERROR_FAIL;
-    }
-
-    if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
-        if (scinfo->weight < 1 || scinfo->weight > 65535) {
-            LOGD(ERROR, domid, "Cpu weight out of range, "
-                        "valid values are within range from 1 to 65535");
-            return ERROR_INVAL;
-        }
-        sdom.weight = scinfo->weight;
-    }
-
-    if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
-        if (scinfo->cap < 0
-            || scinfo->cap > (info.max_vcpu_id + 1) * 100) {
-            LOGD(ERROR, domid, "Cpu cap out of range, "
-                 "valid range is from 0 to %d for specified number of vcpus",
-                 ((info.max_vcpu_id + 1) * 100));
-            return ERROR_INVAL;
-        }
-        sdom.cap = scinfo->cap;
-    }
-
-    rc = xc_sched_credit2_domain_set(CTX->xch, domid, &sdom);
-    if ( rc < 0 ) {
-        LOGED(ERROR, domid, "Setting domain sched credit2");
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-static int sched_rtds_validate_params(libxl__gc *gc, int period, int budget)
-{
-    int rc;
-
-    if (period < 1) {
-        LOG(ERROR, "Invalid VCPU period of %d (it should be >= 1)", period);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    if (budget < 1) {
-        LOG(ERROR, "Invalid VCPU budget of %d (it should be >= 1)", budget);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    if (budget > period) {
-        LOG(ERROR, "VCPU budget must be smaller than or equal to period, "
-                   "but %d > %d", budget, period);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    rc = 0;
-out:
-    return rc;
-}
-
-/* Get the RTDS scheduling parameters of vcpu(s) */
-static int sched_rtds_vcpu_get(libxl__gc *gc, uint32_t domid,
-                               libxl_vcpu_sched_params *scinfo)
-{
-    uint32_t num_vcpus;
-    int i, r, rc;
-    xc_dominfo_t info;
-    struct xen_domctl_schedparam_vcpu *vcpus;
-
-    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
-    if (r < 0) {
-        LOGED(ERROR, domid, "Getting domain info");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (scinfo->num_vcpus <= 0) {
-        rc = ERROR_INVAL;
-        goto out;
-    } else {
-        num_vcpus = scinfo->num_vcpus;
-        GCNEW_ARRAY(vcpus, num_vcpus);
-        for (i = 0; i < num_vcpus; i++) {
-            if (scinfo->vcpus[i].vcpuid < 0 ||
-                scinfo->vcpus[i].vcpuid > info.max_vcpu_id) {
-                LOGD(ERROR, domid, "VCPU index is out of range, "
-                            "valid values are within range from 0 to %d",
-                            info.max_vcpu_id);
-                rc = ERROR_INVAL;
-                goto out;
-            }
-            vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
-        }
-    }
-
-    r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
-    if (r != 0) {
-        LOGED(ERROR, domid, "Getting vcpu sched rtds");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    scinfo->sched = LIBXL_SCHEDULER_RTDS;
-    for (i = 0; i < num_vcpus; i++) {
-        scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
-        scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
-        scinfo->vcpus[i].extratime =
-                !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
-        scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
-    }
-    rc = 0;
-out:
-    return rc;
-}
-
-/* Get the RTDS scheduling parameters of all vcpus of a domain */
-static int sched_rtds_vcpu_get_all(libxl__gc *gc, uint32_t domid,
-                                   libxl_vcpu_sched_params *scinfo)
-{
-    uint32_t num_vcpus;
-    int i, r, rc;
-    xc_dominfo_t info;
-    struct xen_domctl_schedparam_vcpu *vcpus;
-
-    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
-    if (r < 0) {
-        LOGED(ERROR, domid, "Getting domain info");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (scinfo->num_vcpus > 0) {
-        rc = ERROR_INVAL;
-        goto out;
-    } else {
-        num_vcpus = info.max_vcpu_id + 1;
-        GCNEW_ARRAY(vcpus, num_vcpus);
-        for (i = 0; i < num_vcpus; i++)
-            vcpus[i].vcpuid = i;
-    }
-
-    r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus);
-    if (r != 0) {
-        LOGED(ERROR, domid, "Getting vcpu sched rtds");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    scinfo->sched = LIBXL_SCHEDULER_RTDS;
-    scinfo->num_vcpus = num_vcpus;
-    scinfo->vcpus = libxl__calloc(NOGC, num_vcpus,
-                                  sizeof(libxl_sched_params));
-
-    for (i = 0; i < num_vcpus; i++) {
-        scinfo->vcpus[i].period = vcpus[i].u.rtds.period;
-        scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget;
-        scinfo->vcpus[i].extratime =
-                !!(vcpus[i].u.rtds.flags & XEN_DOMCTL_SCHEDRT_extra);
-        scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid;
-    }
-    rc = 0;
-out:
-    return rc;
-}
-
-/* Set the RTDS scheduling parameters of vcpu(s) */
-static int sched_rtds_vcpu_set(libxl__gc *gc, uint32_t domid,
-                               const libxl_vcpu_sched_params *scinfo)
-{
-    int r, rc;
-    int i;
-    uint16_t max_vcpuid;
-    xc_dominfo_t info;
-    struct xen_domctl_schedparam_vcpu *vcpus;
-
-    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
-    if (r < 0) {
-        LOGED(ERROR, domid, "Getting domain info");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    max_vcpuid = info.max_vcpu_id;
-
-    if (scinfo->num_vcpus <= 0) {
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    for (i = 0; i < scinfo->num_vcpus; i++) {
-        if (scinfo->vcpus[i].vcpuid < 0 ||
-            scinfo->vcpus[i].vcpuid > max_vcpuid) {
-            LOGD(ERROR, domid, "Invalid VCPU %d: valid range is [0, %d]",
-                        scinfo->vcpus[i].vcpuid, max_vcpuid);
-            rc = ERROR_INVAL;
-            goto out;
-        }
-        rc = sched_rtds_validate_params(gc, scinfo->vcpus[i].period,
-                                        scinfo->vcpus[i].budget);
-        if (rc) {
-            rc = ERROR_INVAL;
-            goto out;
-        }
-    }
-    GCNEW_ARRAY(vcpus, scinfo->num_vcpus);
-    for (i = 0; i < scinfo->num_vcpus; i++) {
-        vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid;
-        vcpus[i].u.rtds.period = scinfo->vcpus[i].period;
-        vcpus[i].u.rtds.budget = scinfo->vcpus[i].budget;
-        if (scinfo->vcpus[i].extratime)
-            vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
-        else
-            vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
-    }
-
-    r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
-                               vcpus, scinfo->num_vcpus);
-    if (r != 0) {
-        LOGED(ERROR, domid, "Setting vcpu sched rtds");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = 0;
-out:
-    return rc;
-}
-
-/* Set the RTDS scheduling parameters of all vcpus of a domain */
-static int sched_rtds_vcpu_set_all(libxl__gc *gc, uint32_t domid,
-                                   const libxl_vcpu_sched_params *scinfo)
-{
-    int r, rc;
-    int i;
-    uint16_t max_vcpuid;
-    xc_dominfo_t info;
-    struct xen_domctl_schedparam_vcpu *vcpus;
-    uint32_t num_vcpus;
-
-    r = xc_domain_getinfo(CTX->xch, domid, 1, &info);
-    if (r < 0) {
-        LOGED(ERROR, domid, "Getting domain info");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    max_vcpuid = info.max_vcpu_id;
-
-    if (scinfo->num_vcpus != 1) {
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    if (sched_rtds_validate_params(gc, scinfo->vcpus[0].period,
-                                   scinfo->vcpus[0].budget)) {
-        rc = ERROR_INVAL;
-        goto out;
-    }
-    num_vcpus = max_vcpuid + 1;
-    GCNEW_ARRAY(vcpus, num_vcpus);
-    for (i = 0; i < num_vcpus; i++) {
-        vcpus[i].vcpuid = i;
-        vcpus[i].u.rtds.period = scinfo->vcpus[0].period;
-        vcpus[i].u.rtds.budget = scinfo->vcpus[0].budget;
-        if (scinfo->vcpus[0].extratime)
-            vcpus[i].u.rtds.flags |= XEN_DOMCTL_SCHEDRT_extra;
-        else
-            vcpus[i].u.rtds.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
-    }
-
-    r = xc_sched_rtds_vcpu_set(CTX->xch, domid,
-                               vcpus, num_vcpus);
-    if (r != 0) {
-        LOGED(ERROR, domid, "Setting vcpu sched rtds");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-    rc = 0;
-out:
-    return rc;
-}
-
-static int sched_rtds_domain_get(libxl__gc *gc, uint32_t domid,
-                               libxl_domain_sched_params *scinfo)
-{
-    struct xen_domctl_sched_rtds sdom;
-    int rc;
-
-    rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
-    if (rc != 0) {
-        LOGED(ERROR, domid, "Getting domain sched rtds");
-        return ERROR_FAIL;
-    }
-
-    libxl_domain_sched_params_init(scinfo);
-
-    scinfo->sched = LIBXL_SCHEDULER_RTDS;
-    scinfo->period = sdom.period;
-    scinfo->budget = sdom.budget;
-
-    return 0;
-}
-
-static int sched_rtds_domain_set(libxl__gc *gc, uint32_t domid,
-                               const libxl_domain_sched_params *scinfo)
-{
-    struct xen_domctl_sched_rtds sdom;
-    int rc;
-
-    rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
-    if (rc != 0) {
-        LOGED(ERROR, domid, "Getting domain sched rtds");
-        return ERROR_FAIL;
-    }
-    if (scinfo->period != LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT)
-        sdom.period = scinfo->period;
-    if (scinfo->budget != LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT)
-        sdom.budget = scinfo->budget;
-    /* Set extratime by default */
-    if (scinfo->extratime)
-        sdom.flags |= XEN_DOMCTL_SCHEDRT_extra;
-    else
-        sdom.flags &= ~XEN_DOMCTL_SCHEDRT_extra;
-    if (sched_rtds_validate_params(gc, sdom.period, sdom.budget))
-        return ERROR_INVAL;
-
-    rc = xc_sched_rtds_domain_set(CTX->xch, domid, &sdom);
-    if (rc < 0) {
-        LOGED(ERROR, domid, "Setting domain sched rtds");
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
-                                  const libxl_domain_sched_params *scinfo)
-{
-    GC_INIT(ctx);
-    libxl_scheduler sched = scinfo->sched;
-    int ret;
-
-    if (sched == LIBXL_SCHEDULER_UNKNOWN)
-        sched = libxl__domain_scheduler(gc, domid);
-
-    switch (sched) {
-    case LIBXL_SCHEDULER_SEDF:
-        LOGD(ERROR, domid, "SEDF scheduler no longer available");
-        ret=ERROR_FEATURE_REMOVED;
-        break;
-    case LIBXL_SCHEDULER_CREDIT:
-        ret=sched_credit_domain_set(gc, domid, scinfo);
-        break;
-    case LIBXL_SCHEDULER_CREDIT2:
-        ret=sched_credit2_domain_set(gc, domid, scinfo);
-        break;
-    case LIBXL_SCHEDULER_ARINC653:
-        ret=sched_arinc653_domain_set(gc, domid, scinfo);
-        break;
-    case LIBXL_SCHEDULER_RTDS:
-        ret=sched_rtds_domain_set(gc, domid, scinfo);
-        break;
-    case LIBXL_SCHEDULER_NULL:
-        ret=sched_null_domain_set(gc, domid, scinfo);
-        break;
-    default:
-        LOGD(ERROR, domid, "Unknown scheduler");
-        ret=ERROR_INVAL;
-        break;
-    }
-
-    GC_FREE;
-    return ret;
-}
-
-int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid,
-                                const libxl_vcpu_sched_params *scinfo)
-{
-    GC_INIT(ctx);
-    libxl_scheduler sched = scinfo->sched;
-    int rc;
-
-    if (sched == LIBXL_SCHEDULER_UNKNOWN)
-        sched = libxl__domain_scheduler(gc, domid);
-
-    switch (sched) {
-    case LIBXL_SCHEDULER_SEDF:
-        LOGD(ERROR, domid, "SEDF scheduler no longer available");
-        rc = ERROR_FEATURE_REMOVED;
-        break;
-    case LIBXL_SCHEDULER_CREDIT:
-    case LIBXL_SCHEDULER_CREDIT2:
-    case LIBXL_SCHEDULER_ARINC653:
-    case LIBXL_SCHEDULER_NULL:
-        LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
-        rc = ERROR_INVAL;
-        break;
-    case LIBXL_SCHEDULER_RTDS:
-        rc = sched_rtds_vcpu_set(gc, domid, scinfo);
-        break;
-    default:
-        LOGD(ERROR, domid, "Unknown scheduler");
-        rc = ERROR_INVAL;
-        break;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid,
-                                    const libxl_vcpu_sched_params *scinfo)
-{
-    GC_INIT(ctx);
-    libxl_scheduler sched = scinfo->sched;
-    int rc;
-
-    if (sched == LIBXL_SCHEDULER_UNKNOWN)
-        sched = libxl__domain_scheduler(gc, domid);
-
-    switch (sched) {
-    case LIBXL_SCHEDULER_SEDF:
-        LOGD(ERROR, domid, "SEDF scheduler no longer available");
-        rc = ERROR_FEATURE_REMOVED;
-        break;
-    case LIBXL_SCHEDULER_CREDIT:
-    case LIBXL_SCHEDULER_CREDIT2:
-    case LIBXL_SCHEDULER_ARINC653:
-    case LIBXL_SCHEDULER_NULL:
-        LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler");
-        rc = ERROR_INVAL;
-        break;
-    case LIBXL_SCHEDULER_RTDS:
-        rc = sched_rtds_vcpu_set_all(gc, domid, scinfo);
-        break;
-    default:
-        LOGD(ERROR, domid, "Unknown scheduler");
-        rc = ERROR_INVAL;
-        break;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
-                                  libxl_domain_sched_params *scinfo)
-{
-    GC_INIT(ctx);
-    int ret;
-
-    libxl_domain_sched_params_init(scinfo);
-
-    scinfo->sched = libxl__domain_scheduler(gc, domid);
-
-    switch (scinfo->sched) {
-    case LIBXL_SCHEDULER_SEDF:
-        LOGD(ERROR, domid, "SEDF scheduler no longer available");
-        ret=ERROR_FEATURE_REMOVED;
-        break;
-    case LIBXL_SCHEDULER_CREDIT:
-        ret=sched_credit_domain_get(gc, domid, scinfo);
-        break;
-    case LIBXL_SCHEDULER_CREDIT2:
-        ret=sched_credit2_domain_get(gc, domid, scinfo);
-        break;
-    case LIBXL_SCHEDULER_RTDS:
-        ret=sched_rtds_domain_get(gc, domid, scinfo);
-        break;
-    case LIBXL_SCHEDULER_NULL:
-        ret=sched_null_domain_get(gc, domid, scinfo);
-        break;
-    default:
-        LOGD(ERROR, domid, "Unknown scheduler");
-        ret=ERROR_INVAL;
-        break;
-    }
-
-    GC_FREE;
-    return ret;
-}
-
-int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid,
-                                libxl_vcpu_sched_params *scinfo)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    scinfo->sched = libxl__domain_scheduler(gc, domid);
-
-    switch (scinfo->sched) {
-    case LIBXL_SCHEDULER_SEDF:
-        LOGD(ERROR, domid, "SEDF scheduler is no longer available");
-        rc = ERROR_FEATURE_REMOVED;
-        break;
-    case LIBXL_SCHEDULER_CREDIT:
-    case LIBXL_SCHEDULER_CREDIT2:
-    case LIBXL_SCHEDULER_ARINC653:
-    case LIBXL_SCHEDULER_NULL:
-        LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
-        rc = ERROR_INVAL;
-        break;
-    case LIBXL_SCHEDULER_RTDS:
-        rc = sched_rtds_vcpu_get(gc, domid, scinfo);
-        break;
-    default:
-        LOGD(ERROR, domid, "Unknown scheduler");
-        rc = ERROR_INVAL;
-        break;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid,
-                                    libxl_vcpu_sched_params *scinfo)
-{
-    GC_INIT(ctx);
-    int rc;
-
-    scinfo->sched = libxl__domain_scheduler(gc, domid);
-
-    switch (scinfo->sched) {
-    case LIBXL_SCHEDULER_SEDF:
-        LOGD(ERROR, domid, "SEDF scheduler is no longer available");
-        rc = ERROR_FEATURE_REMOVED;
-        break;
-    case LIBXL_SCHEDULER_CREDIT:
-    case LIBXL_SCHEDULER_CREDIT2:
-    case LIBXL_SCHEDULER_ARINC653:
-    case LIBXL_SCHEDULER_NULL:
-        LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler");
-        rc = ERROR_INVAL;
-        break;
-    case LIBXL_SCHEDULER_RTDS:
-        rc = sched_rtds_vcpu_get_all(gc, domid, scinfo);
-        break;
-    default:
-        LOGD(ERROR, domid, "Unknown scheduler");
-        rc = ERROR_INVAL;
-        break;
-    }
-
-    GC_FREE;
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_sr_stream_format.h b/tools/libxl/libxl_sr_stream_format.h
deleted file mode 100644 (file)
index 75f5190..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef LIBXL__SR_STREAM_FORMAT_H
-#define LIBXL__SR_STREAM_FORMAT_H
-
-/*
- * C structures for the Migration v2 stream format.
- * See docs/specs/libxl-migration-stream.pandoc
- */
-
-#include <stdint.h>
-
-typedef struct libxl__sr_hdr
-{
-    uint64_t ident;
-    uint32_t version;
-    uint32_t options;
-} libxl__sr_hdr;
-
-#define RESTORE_STREAM_IDENT         0x4c6962786c466d74UL
-#define RESTORE_STREAM_VERSION       0x00000002U
-
-#define RESTORE_OPT_BIG_ENDIAN       (1u << 0)
-#define RESTORE_OPT_LEGACY           (1u << 1)
-
-
-typedef struct libxl__sr_rec_hdr
-{
-    uint32_t type;
-    uint32_t length;
-} libxl__sr_rec_hdr;
-
-/* All records must be aligned up to an 8 octet boundary */
-#define REC_ALIGN_ORDER              3U
-
-#define REC_TYPE_END                    0x00000000U
-#define REC_TYPE_LIBXC_CONTEXT          0x00000001U
-#define REC_TYPE_EMULATOR_XENSTORE_DATA 0x00000002U
-#define REC_TYPE_EMULATOR_CONTEXT       0x00000003U
-#define REC_TYPE_CHECKPOINT_END         0x00000004U
-#define REC_TYPE_CHECKPOINT_STATE       0x00000005U
-
-typedef struct libxl__sr_emulator_hdr
-{
-    uint32_t id;
-    uint32_t index;
-} libxl__sr_emulator_hdr;
-
-#define EMULATOR_UNKNOWN             0x00000000U
-#define EMULATOR_QEMU_TRADITIONAL    0x00000001U
-#define EMULATOR_QEMU_UPSTREAM       0x00000002U
-
-typedef struct libxl_sr_checkpoint_state
-{
-    uint32_t id;
-} libxl_sr_checkpoint_state;
-
-#define CHECKPOINT_NEW               0x00000000U
-#define CHECKPOINT_SVM_SUSPENDED     0x00000001U
-#define CHECKPOINT_SVM_READY         0x00000002U
-#define CHECKPOINT_SVM_RESUMED       0x00000003U
-
-#endif /* LIBXL__SR_STREAM_FORMAT_H */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_stream_read.c b/tools/libxl/libxl_stream_read.c
deleted file mode 100644 (file)
index 514f6d9..0000000
+++ /dev/null
@@ -1,977 +0,0 @@
-/*
- * Copyright (C) 2015      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*
- * Infrastructure for reading and acting on the contents of a libxl
- * migration stream. There are a lot of moving parts here.
- *
- * The logic revolves around two actions; reading another record from
- * the stream, and processing the records.  The stream_continue()
- * function is responsible for choosing the next action to perform.
- *
- * The exact order of reading and processing is controlled by 'phase'.
- * All complete records are held in the record_queue before being
- * processed, and all records will be processed in queue order.
- *
- * Internal states:
- *           running  phase       in_         record   incoming
- *                                checkpoint  _queue   _record
- *
- * Undefined    undef  undef        undef       undef    undef
- * Idle         false  undef        false       0        0
- * Active       true   NORMAL       false       0/1      0/partial
- * Active       true   BUFFERING    true        any      0/partial
- * Active       true   UNBUFFERING  true        any      0
- *
- * While reading data from the stream, 'dc' is active and a callback
- * is expected.  Most actions in process_record() start a callback of
- * their own.  Those which don't return out and stream_continue() sets
- * up the next action.
- *
- * PHASE_NORMAL:
- *   This phase is used for regular migration or resume from file.
- *   Records are read one at time and immediately processed.  (The
- *   record queue will not contain more than a single record.)
- *
- * PHASE_BUFFERING:
- *   This phase is used in checkpointed streams, when libxc signals
- *   the presence of a checkpoint in the stream.  Records are read and
- *   buffered until a CHECKPOINT_END record has been read.
- *
- * PHASE_UNBUFFERING:
- *   Once a CHECKPOINT_END record has been read, all buffered records
- *   are processed.
- *
- * Note:
- *   Record buffers are not allocated from a GC; they are allocated
- *   and tracked manually.  This is to avoid OOM with Remus where the
- *   AO lives for the lifetime of the process.  Per-checkpoint AO's
- *   might be an avenue to explore.
- *
- * Entry points from outside:
- *  - libxl__stream_read_init()
- *     - Initialises state.  Must be called once before _start()
- *  - libxl__stream_read_start()
- *     - Starts reading records from the stream, and acting on them.
- *  - libxl__stream_read_start_checkpoint()
- *     - Starts buffering records at a checkpoint.  Must be called on
- *       a running stream.
- *
- * There are several chains of event:
- *
- * 1) Starting a stream follows:
- *    - libxl__stream_read_start()
- *    - stream_header_done()
- *    - stream_continue()
- *
- * 2) Reading a record follows:
- *    - stream_continue()
- *    - record_header_done()
- *    - record_body_done()
- *    - stream_continue()
- *
- * 3) Processing a record had several chains to follow, depending on
- *    the record in question.
- * 3a) "Simple" record:
- *    - process_record()
- *    - stream_continue()
- * 3b) LIBXC record:
- *    - process_record()
- *    - libxl__xc_domain_restore()
- *    - libxl__xc_domain_restore_done()
- *    - stream_continue()
- * 3c) EMULATOR record:
- *    - process_record()
- *    - stream_write_emulator()
- *    - stream_write_emulator_done()
- *    - stream_continue()
- *
- * Depending on the contents of the stream, there are likely to be several
- * parallel tasks being managed.  check_all_finished() is used to join all
- * tasks in both success and error cases.
- *
- * Failover for remus
- *  - We buffer all records until a CHECKPOINT_END record is received
- *  - We will consume the buffered records when a CHECKPOINT_END record
- *    is received
- *  - If we find some internal error, then rc or retval is not 0 in
- *    libxl__xc_domain_restore_done(). In this case, we don't resume the
- *    guest
- *  - If we need to do failover from primary, then rc and retval are both
- *    0 in libxl__xc_domain_restore_done(). In this case, the buffered
- *    state will be dropped, because we haven't received a CHECKPOINT_END
- *    record, and therefore the buffered state is inconsistent. In
- *    libxl__xc_domain_restore_done(), we just complete the stream and
- *    stream->completion_callback() will be called to resume the guest
- *
- * For back channel stream:
- * - libxl__stream_read_start()
- *    - Set up the stream to running state
- *
- * - libxl__stream_read_continue()
- *     - Set up reading the next record from a started stream.
- *       Add some codes to process_record() to handle the record.
- *       Then call stream->checkpoint_callback() to return.
- */
-
-/* Success/error/cleanup handling. */
-static void stream_complete(libxl__egc *egc,
-                            libxl__stream_read_state *stream, int rc);
-static void checkpoint_done(libxl__egc *egc,
-                            libxl__stream_read_state *stream, int rc);
-static void stream_done(libxl__egc *egc,
-                        libxl__stream_read_state *stream, int rc);
-static void conversion_done(libxl__egc *egc,
-                            libxl__conversion_helper_state *chs, int rc);
-static void check_all_finished(libxl__egc *egc,
-                               libxl__stream_read_state *stream, int rc);
-
-/* Event chain for first iteration, from _start(). */
-static void stream_header_done(libxl__egc *egc,
-                               libxl__datacopier_state *dc,
-                               int rc, int onwrite, int errnoval);
-static void stream_continue(libxl__egc *egc,
-                            libxl__stream_read_state *stream);
-static void setup_read_record(libxl__egc *egc,
-                              libxl__stream_read_state *stream);
-static void record_header_done(libxl__egc *egc,
-                               libxl__datacopier_state *dc,
-                               int rc, int onwrite, int errnoval);
-static void record_body_done(libxl__egc *egc,
-                             libxl__datacopier_state *dc,
-                             int rc, int onwrite, int errnoval);
-static bool process_record(libxl__egc *egc,
-                           libxl__stream_read_state *stream);
-
-/* Event chain for processing an emulator blob. */
-static void write_emulator_blob(libxl__egc *egc,
-                                libxl__stream_read_state *stream,
-                                libxl__sr_record_buf *rec);
-static void write_emulator_done(libxl__egc *egc,
-                                libxl__datacopier_state *dc,
-                                int rc, int onwrite, int errnoval);
-
-/* Handlers for checkpoint state mini-loop */
-static void checkpoint_state_done(libxl__egc *egc,
-                                  libxl__stream_read_state *stream, int rc);
-
-/*----- Helpers -----*/
-
-/* Helper to set up reading some data from the stream. */
-static int setup_read(libxl__stream_read_state *stream,
-                      const char *what, void *ptr, size_t nr_bytes,
-                      libxl__datacopier_callback cb)
-{
-    libxl__datacopier_state *dc = &stream->dc;
-
-    dc->readwhat      = what;
-    dc->readbuf       = ptr;
-    dc->bytes_to_read = nr_bytes;
-    dc->used          = 0;
-    dc->callback      = cb;
-
-    return libxl__datacopier_start(dc);
-}
-
-static void free_record(libxl__sr_record_buf *rec)
-{
-    if (rec) {
-        free(rec->body);
-        free(rec);
-    }
-}
-
-/*----- Entrypoints -----*/
-
-void libxl__stream_read_init(libxl__stream_read_state *stream)
-{
-    assert(stream->ao);
-
-    stream->shs.ao = stream->ao;
-    libxl__save_helper_init(&stream->shs);
-
-    stream->chs.ao = stream->ao;
-    libxl__conversion_helper_init(&stream->chs);
-
-    stream->rc = 0;
-    stream->running = false;
-    stream->in_checkpoint = false;
-    stream->sync_teardown = false;
-    FILLZERO(stream->dc);
-    FILLZERO(stream->hdr);
-    LIBXL_STAILQ_INIT(&stream->record_queue);
-    stream->phase = SRS_PHASE_NORMAL;
-    stream->recursion_guard = false;
-    stream->incoming_record = NULL;
-    FILLZERO(stream->emu_dc);
-    stream->emu_carefd = NULL;
-}
-
-void libxl__stream_read_start(libxl__egc *egc,
-                              libxl__stream_read_state *stream)
-{
-    libxl__datacopier_state *dc = &stream->dc;
-    STATE_AO_GC(stream->ao);
-    int rc = 0;
-
-    libxl__stream_read_init(stream);
-
-    stream->running = true;
-    stream->phase   = SRS_PHASE_NORMAL;
-
-    if (stream->legacy) {
-        /*
-         * Convert the legacy stream.
-         *
-         * This results in a fork()/exec() of conversion helper script.  It is
-         * passed the exiting stream->fd as an input, and returns the
-         * transformed stream via a new pipe.  The fd of this new pipe then
-         * replaces stream->fd, to make the rest of the stream read code
-         * agnostic to whether legacy conversion is happening or not.
-         */
-        libxl__conversion_helper_state *chs = &stream->chs;
-
-        chs->legacy_fd = stream->fd;
-        chs->hvm =
-            (stream->dcs->guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM);
-        chs->completion_callback = conversion_done;
-
-        rc = libxl__convert_legacy_stream(egc, &stream->chs);
-
-        if (rc) {
-            LOG(ERROR, "Failed to start the legacy stream conversion helper");
-            goto err;
-        }
-
-        /* There should be no interaction of COLO backchannels and legacy
-         * stream conversion. */
-        assert(!stream->back_channel);
-
-        /* Confirm *dc is still zeroed out, while we shuffle stream->fd. */
-        assert(dc->ao == NULL);
-        assert(stream->chs.v2_carefd);
-        stream->fd = libxl__carefd_fd(stream->chs.v2_carefd);
-        stream->dcs->libxc_fd = stream->fd;
-    }
-    /* stream->fd is now a v2 stream. */
-
-    dc->ao       = stream->ao;
-    dc->copywhat = "restore v2 stream";
-    dc->readfd   = stream->fd;
-    dc->writefd  = -1;
-
-    if (stream->back_channel)
-        return;
-
-    /* Start reading the stream header. */
-    rc = setup_read(stream, "stream header",
-                    &stream->hdr, sizeof(stream->hdr),
-                    stream_header_done);
-    if (rc)
-        goto err;
-
-    assert(!rc);
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-void libxl__stream_read_start_checkpoint(libxl__egc *egc,
-                                         libxl__stream_read_state *stream)
-{
-    assert(stream->running);
-    assert(!stream->in_checkpoint);
-
-    stream->in_checkpoint = true;
-    stream->phase = SRS_PHASE_BUFFERING;
-
-    /*
-     * Libxc has handed control of the fd to us.  Start reading some
-     * libxl records out of it.
-     */
-    stream_continue(egc, stream);
-}
-
-void libxl__stream_read_abort(libxl__egc *egc,
-                              libxl__stream_read_state *stream, int rc)
-{
-    assert(rc);
-
-    if (stream->running)
-        stream_complete(egc, stream, rc);
-}
-
-/*----- Event logic -----*/
-
-static void stream_header_done(libxl__egc *egc,
-                               libxl__datacopier_state *dc,
-                               int rc, int onwrite, int errnoval)
-{
-    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
-    libxl__sr_hdr *hdr = &stream->hdr;
-    STATE_AO_GC(dc->ao);
-
-    if (rc)
-        goto err;
-
-    hdr->ident   = be64toh(hdr->ident);
-    hdr->version = be32toh(hdr->version);
-    hdr->options = be32toh(hdr->options);
-
-    if (hdr->ident != RESTORE_STREAM_IDENT) {
-        rc = ERROR_FAIL;
-        LOG(ERROR,
-            "Invalid ident: expected 0x%016"PRIx64", got 0x%016"PRIx64,
-            RESTORE_STREAM_IDENT, hdr->ident);
-        goto err;
-    }
-    if (hdr->version != RESTORE_STREAM_VERSION) {
-        rc = ERROR_FAIL;
-        LOG(ERROR, "Unexpected Version: expected %"PRIu32", got %"PRIu32,
-            RESTORE_STREAM_VERSION, hdr->version);
-        goto err;
-    }
-    if (hdr->options & RESTORE_OPT_BIG_ENDIAN) {
-        rc = ERROR_FAIL;
-        LOG(ERROR, "Unable to handle big endian streams");
-        goto err;
-    }
-
-    LOG(DEBUG, "Stream v%"PRIu32"%s", hdr->version,
-        hdr->options & RESTORE_OPT_LEGACY ? " (from legacy)" : "");
-
-    stream_continue(egc, stream);
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-static void stream_continue(libxl__egc *egc,
-                            libxl__stream_read_state *stream)
-{
-    STATE_AO_GC(stream->ao);
-
-    /*
-     * Must not mutually recurse with process_record().
-     *
-     * For records whose processing function is synchronous
-     * (e.g. TOOLSTACK), process_record() does not start another async
-     * operation, and a further operation should be started.
-     *
-     * A naive solution, which would function in general, would be for
-     * process_record() to call stream_continue().  However, this
-     * would allow the content of the stream to cause mutual
-     * recursion, and possibly for us to fall off our stack.
-     *
-     * Instead, process_record() indicates with its return value
-     * whether a further operation needs to start, and the
-     * recursion_guard is in place to catch any code paths which get
-     * this wrong.
-     */
-    assert(stream->recursion_guard == false);
-    stream->recursion_guard = true;
-
-    switch (stream->phase) {
-    case SRS_PHASE_NORMAL:
-        /*
-         * Normal phase (regular migration or restore from file):
-         *
-         * logically:
-         *   do { read_record(); process_record(); } while ( not END );
-         *
-         * Alternate between reading a record from the stream, and
-         * processing the record.  There should never be two records
-         * in the queue.
-         */
-        if (LIBXL_STAILQ_EMPTY(&stream->record_queue))
-            setup_read_record(egc, stream);
-        else {
-            if (process_record(egc, stream))
-                setup_read_record(egc, stream);
-
-            /*
-             * process_record() had better have consumed the one and
-             * only record in the queue.
-             */
-            assert(LIBXL_STAILQ_EMPTY(&stream->record_queue));
-        }
-        break;
-
-    case SRS_PHASE_BUFFERING: {
-        /*
-         * Buffering phase (checkpointed streams only):
-         *
-         * logically:
-         *   do { read_record(); } while ( not CHECKPOINT_END );
-         *
-         * Read and buffer all records from the stream until a
-         * CHECKPOINT_END record is encountered.  We need to peek at
-         * the tail to spot the CHECKPOINT_END record, and switch to
-         * the unbuffering phase.
-         */
-        libxl__sr_record_buf *rec = LIBXL_STAILQ_LAST(
-            &stream->record_queue, libxl__sr_record_buf, entry);
-
-        assert(stream->in_checkpoint);
-
-        if (!rec || (rec->hdr.type != REC_TYPE_CHECKPOINT_END)) {
-            setup_read_record(egc, stream);
-            break;
-        }
-
-        /*
-         * There are now some number of buffered records, with a
-         * CHECKPOINT_END at the end. Start processing them all.
-         */
-        stream->phase = SRS_PHASE_UNBUFFERING;
-    }
-        /* FALLTHROUGH */
-    case SRS_PHASE_UNBUFFERING:
-        /*
-         * Unbuffering phase (checkpointed streams only):
-         *
-         * logically:
-         *   do { process_record(); } while ( not CHECKPOINT_END );
-         *
-         * Process all records collected during the buffering phase.
-         */
-        assert(stream->in_checkpoint);
-
-        while (process_record(egc, stream))
-            ; /*
-               * Nothing! process_record() helpfully tells us if no specific
-               * futher actions have been set up, in which case we want to go
-               * ahead and process the next record.
-               */
-        break;
-
-    default:
-        abort();
-    }
-
-    assert(stream->recursion_guard == true);
-    stream->recursion_guard = false;
-}
-
-static void setup_read_record(libxl__egc *egc,
-                              libxl__stream_read_state *stream)
-{
-    libxl__sr_record_buf *rec = NULL;
-    STATE_AO_GC(stream->ao);
-    int rc;
-
-    assert(stream->incoming_record == NULL);
-    stream->incoming_record = rec = libxl__zalloc(NOGC, sizeof(*rec));
-
-    rc = setup_read(stream, "record header",
-                    &rec->hdr, sizeof(rec->hdr),
-                    record_header_done);
-    if (rc)
-        goto err;
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-static void record_header_done(libxl__egc *egc,
-                               libxl__datacopier_state *dc,
-                               int rc, int onwrite, int errnoval)
-{
-    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
-    libxl__sr_record_buf *rec = stream->incoming_record;
-    STATE_AO_GC(dc->ao);
-
-    if (rc)
-        goto err;
-
-    /* No body? All done. */
-    if (rec->hdr.length == 0) {
-        record_body_done(egc, dc, 0, 0, 0);
-        return;
-    }
-
-    size_t bytes_to_read = ROUNDUP(rec->hdr.length, REC_ALIGN_ORDER);
-    rec->body = libxl__malloc(NOGC, bytes_to_read);
-
-    rc = setup_read(stream, "record body",
-                    rec->body, bytes_to_read,
-                    record_body_done);
-    if (rc)
-        goto err;
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-static void record_body_done(libxl__egc *egc,
-                             libxl__datacopier_state *dc,
-                             int rc, int onwrite, int errnoval)
-{
-    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc);
-    libxl__sr_record_buf *rec = stream->incoming_record;
-    STATE_AO_GC(dc->ao);
-
-    if (rc)
-        goto err;
-
-    LIBXL_STAILQ_INSERT_TAIL(&stream->record_queue, rec, entry);
-    stream->incoming_record = NULL;
-
-    stream_continue(egc, stream);
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-/*
- * Returns a boolean indicating whether a further action should be set
- * up by the caller.  This is needed to prevent mutual recursion with
- * stream_continue().
- *
- * It is a bug for this function to ever call stream_continue() or
- * setup_read_record().
- */
-static bool process_record(libxl__egc *egc,
-                           libxl__stream_read_state *stream)
-{
-    STATE_AO_GC(stream->ao);
-    libxl__domain_create_state *dcs = stream->dcs;
-    libxl__sr_record_buf *rec;
-    libxl_sr_checkpoint_state *srcs;
-    bool further_action_needed = false;
-    int rc = 0;
-
-    /* Pop a record from the head of the queue. */
-    assert(!LIBXL_STAILQ_EMPTY(&stream->record_queue));
-    rec = LIBXL_STAILQ_FIRST(&stream->record_queue);
-    LIBXL_STAILQ_REMOVE_HEAD(&stream->record_queue, entry);
-
-    LOG(DEBUG, "Record: %u, length %u", rec->hdr.type, rec->hdr.length);
-
-    switch (rec->hdr.type) {
-
-    case REC_TYPE_END:
-        stream_complete(egc, stream, 0);
-        break;
-
-    case REC_TYPE_LIBXC_CONTEXT:
-        libxl__xc_domain_restore(egc, dcs, &stream->shs);
-        break;
-
-    case REC_TYPE_EMULATOR_XENSTORE_DATA:
-        if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
-            rc = ERROR_FAIL;
-            LOG(ERROR,
-                "Received a xenstore emulator record when none was expected");
-            goto err;
-        }
-
-        if (rec->hdr.length < sizeof(libxl__sr_emulator_hdr)) {
-            rc = ERROR_FAIL;
-            LOG(ERROR,
-                "Emulator xenstore data record too short to contain header");
-            goto err;
-        }
-
-        rc = libxl__restore_emulator_xenstore_data(dcs,
-            rec->body + sizeof(libxl__sr_emulator_hdr),
-            rec->hdr.length - sizeof(libxl__sr_emulator_hdr));
-        if (rc)
-            goto err;
-
-        /*
-         * libxl__restore_emulator_xenstore_data() is a synchronous function.
-         * Request that our caller queues another action for us.
-         */
-        further_action_needed = true;
-        break;
-
-    case REC_TYPE_EMULATOR_CONTEXT:
-        if (dcs->guest_config->b_info.type != LIBXL_DOMAIN_TYPE_HVM) {
-            rc = ERROR_FAIL;
-            LOG(ERROR,
-                "Received an emulator context record when none was expected");
-            goto err;
-        }
-
-        write_emulator_blob(egc, stream, rec);
-        break;
-
-    case REC_TYPE_CHECKPOINT_END:
-        if (!stream->in_checkpoint) {
-            LOG(ERROR, "Unexpected CHECKPOINT_END record in stream");
-            rc = ERROR_FAIL;
-            goto err;
-        }
-        checkpoint_done(egc, stream, 0);
-        break;
-
-    case REC_TYPE_CHECKPOINT_STATE:
-        if (!stream->in_checkpoint_state) {
-            LOG(ERROR, "Unexpected CHECKPOINT_STATE record in stream");
-            rc = ERROR_FAIL;
-            goto err;
-        }
-
-        srcs = rec->body;
-        checkpoint_state_done(egc, stream, srcs->id);
-        break;
-
-    default:
-        LOG(ERROR, "Unrecognised record 0x%08x", rec->hdr.type);
-        rc = ERROR_FAIL;
-        goto err;
-    }
-
-    assert(!rc);
-    free_record(rec);
-    return further_action_needed;
-
- err:
-    assert(rc);
-    free_record(rec);
-    stream_complete(egc, stream, rc);
-    return false;
-}
-
-static void write_emulator_blob(libxl__egc *egc,
-                                libxl__stream_read_state *stream,
-                                libxl__sr_record_buf *rec)
-{
-    libxl__domain_create_state *dcs = stream->dcs;
-    libxl__datacopier_state *dc = &stream->emu_dc;
-    libxl__sr_emulator_hdr *emu_hdr;
-    STATE_AO_GC(stream->ao);
-    char path[256];
-    int rc = 0, writefd;
-
-    if (rec->hdr.length < sizeof(*emu_hdr)) {
-        rc = ERROR_FAIL;
-        LOG(ERROR, "Emulator record too short to contain header");
-        goto err;
-    }
-    emu_hdr = rec->body;
-
-    sprintf(path, LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", dcs->guest_domid);
-
-    assert(stream->emu_carefd == NULL);
-    libxl__carefd_begin();
-    writefd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
-    stream->emu_carefd = libxl__carefd_opened(CTX, writefd);
-
-    if (writefd == -1) {
-        rc = ERROR_FAIL;
-        LOGE(ERROR, "unable to open %s", path);
-        goto err;
-    }
-
-    FILLZERO(*dc);
-    dc->ao         = stream->ao;
-    dc->writewhat  = "qemu save file";
-    dc->copywhat   = "restore v2 stream";
-    dc->writefd    = writefd;
-    dc->readfd     = -1;
-    dc->maxsz      = -1;
-    dc->callback   = write_emulator_done;
-
-    rc = libxl__datacopier_start(dc);
-    if (rc)
-        goto err;
-
-    libxl__datacopier_prefixdata(egc, dc,
-                                 rec->body + sizeof(*emu_hdr),
-                                 rec->hdr.length - sizeof(*emu_hdr));
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-static void write_emulator_done(libxl__egc *egc,
-                                libxl__datacopier_state *dc,
-                                int rc, int onwrite, int errnoval)
-{
-    libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
-    STATE_AO_GC(dc->ao);
-
-    libxl__carefd_close(stream->emu_carefd);
-    stream->emu_carefd = NULL;
-
-    if (rc)
-        goto err;
-
-    stream_continue(egc, stream);
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-/*----- Success/error/cleanup handling. -----*/
-
-static void stream_complete(libxl__egc *egc,
-                            libxl__stream_read_state *stream, int rc)
-{
-    assert(stream->running);
-
-    if (stream->in_checkpoint) {
-        assert(rc);
-
-        /*
-         * If an error is encountered while in a checkpoint, pass it
-         * back to libxc.  The failure will come back around to us via
-         * libxl__xc_domain_restore_done()
-         */
-        checkpoint_done(egc, stream, rc);
-        return;
-    }
-
-    if (stream->in_checkpoint_state) {
-        assert(rc);
-
-        /*
-         * If an error is encountered while in a checkpoint, pass it
-         * back to libxc.  The failure will come back around to us via
-         * 1. normal stream
-         *    libxl__xc_domain_restore_done()
-         * 2. back_channel stream
-         *    libxl__stream_read_abort()
-         */
-        checkpoint_state_done(egc, stream, rc);
-        return;
-    }
-
-    stream_done(egc, stream, rc);
-}
-
-static void checkpoint_done(libxl__egc *egc,
-                            libxl__stream_read_state *stream, int rc)
-{
-    int ret;
-
-    assert(stream->in_checkpoint);
-
-    if (rc == 0)
-        ret = XGR_CHECKPOINT_SUCCESS;
-    else if (stream->phase == SRS_PHASE_BUFFERING)
-        ret = XGR_CHECKPOINT_FAILOVER;
-    else
-        ret = XGR_CHECKPOINT_ERROR;
-
-    stream->checkpoint_callback(egc, stream, ret);
-
-    stream->in_checkpoint = false;
-    stream->phase = SRS_PHASE_NORMAL;
-}
-
-static void stream_done(libxl__egc *egc,
-                        libxl__stream_read_state *stream, int rc)
-{
-    libxl__sr_record_buf *rec, *trec;
-
-    assert(stream->running);
-    assert(!stream->in_checkpoint);
-    assert(!stream->in_checkpoint_state);
-    stream->running = false;
-
-    if (stream->incoming_record)
-        free_record(stream->incoming_record);
-
-    if (stream->emu_carefd)
-        libxl__carefd_close(stream->emu_carefd);
-
-    /* If we started a conversion helper, we took ownership of its carefd. */
-    if (stream->chs.v2_carefd)
-        libxl__carefd_close(stream->chs.v2_carefd);
-
-    /* The record queue had better be empty if the stream believes
-     * itself to have been successful. */
-    assert(LIBXL_STAILQ_EMPTY(&stream->record_queue) || stream->rc);
-
-    LIBXL_STAILQ_FOREACH_SAFE(rec, &stream->record_queue, entry, trec)
-        free_record(rec);
-
-    if (!stream->back_channel) {
-        /*
-         * 1. In stream_done(), stream->running is set to false, so
-         *    the stream itself is not in use.
-         * 2. Read stream is a back channel stream, this means it is
-         *    only used by primary(save side) to read records sent by
-         *    secondary(restore side), so it doesn't have restore helper.
-         * 3. Back channel stream doesn't support legacy stream, so
-         *    there is no conversion helper.
-         * So we don't need invoke check_all_finished here
-         */
-        check_all_finished(egc, stream, rc);
-    }
-}
-
-void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void,
-                                   int rc, int retval, int errnoval)
-{
-    libxl__domain_create_state *dcs = dcs_void;
-    libxl__stream_read_state *stream = &dcs->srs;
-    STATE_AO_GC(dcs->ao);
-
-    /* convenience aliases */
-    const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
-
-    if (rc)
-        goto err;
-
-    if (retval) {
-        LOGEV(ERROR, errnoval, "restoring domain");
-        rc = ERROR_FAIL;
-        goto err;
-    }
-
- err:
-    check_all_finished(egc, stream, rc);
-
-    /*
-     * This function is the callback associated with the save helper
-     * task, not the stream task.  We do not know whether the stream is
-     * alive, and check_all_finished() may have torn it down around us.
-     * If the stream is not still alive, we must not continue any work.
-     */
-    if (libxl__stream_read_inuse(stream)) {
-        switch (checkpointed_stream) {
-        case LIBXL_CHECKPOINTED_STREAM_COLO:
-            if (stream->completion_callback) {
-                /*
-                 * restore, just build the secondary vm, don't close
-                 * the stream
-                 */
-                stream->completion_callback(egc, stream, 0);
-            } else {
-                /* failover, just close the stream */
-                stream_complete(egc, stream, 0);
-            }
-            break;
-        case LIBXL_CHECKPOINTED_STREAM_REMUS:
-            /*
-             * Failover from primary. Domain state is currently at a
-             * consistent checkpoint, complete the stream, and call
-             * stream->completion_callback() to resume the guest.
-             */
-            stream_complete(egc, stream, 0);
-            break;
-        case LIBXL_CHECKPOINTED_STREAM_NONE:
-            /*
-             * Libxc has indicated that it is done with the stream.
-             * Resume reading libxl records from it.
-             */
-            stream_continue(egc, stream);
-            break;
-        }
-    }
-}
-
-static void conversion_done(libxl__egc *egc,
-                            libxl__conversion_helper_state *chs, int rc)
-{
-    libxl__stream_read_state *stream = CONTAINER_OF(chs, *stream, chs);
-
-    check_all_finished(egc, stream, rc);
-}
-
-static void check_all_finished(libxl__egc *egc,
-                               libxl__stream_read_state *stream, int rc)
-{
-    STATE_AO_GC(stream->ao);
-
-    /*
-     * In the case of a failure, the _abort()'s below might cancel
-     * synchronously on top of us, or asynchronously at a later point.
-     *
-     * We must avoid the situation where all _abort() cancel
-     * synchronously and the completion_callback() gets called twice;
-     * once by the first error and once by the final stacked abort(),
-     * both of whom will find that all of the tasks have stopped.
-     *
-     * To avoid this problem, any stacked re-entry into this function is
-     * ineligible to fire the completion callback.  The outermost
-     * instance will take care of completing, once the stack has
-     * unwound.
-     */
-    if (stream->sync_teardown)
-        return;
-
-    if (!stream->rc && rc) {
-        /* First reported failure. Tear everything down. */
-        stream->rc = rc;
-        stream->sync_teardown = true;
-
-        libxl__stream_read_abort(egc, stream, rc);
-        libxl__save_helper_abort(egc, &stream->shs);
-        libxl__conversion_helper_abort(egc, &stream->chs, rc);
-
-        stream->sync_teardown = false;
-    }
-
-    /* Don't fire the callback until all our parallel tasks have stopped. */
-    if (libxl__stream_read_inuse(stream) ||
-        libxl__save_helper_inuse(&stream->shs) ||
-        libxl__conversion_helper_inuse(&stream->chs))
-        return;
-
-    if (stream->completion_callback)
-        /* back channel stream doesn't have completion_callback() */
-        stream->completion_callback(egc, stream, stream->rc);
-}
-
-/*----- Checkpoint state handlers -----*/
-
-void libxl__stream_read_checkpoint_state(libxl__egc *egc,
-                                         libxl__stream_read_state *stream)
-{
-    assert(stream->running);
-    assert(!stream->in_checkpoint);
-    assert(!stream->in_checkpoint_state);
-    stream->in_checkpoint_state = true;
-
-    setup_read_record(egc, stream);
-}
-
-static void checkpoint_state_done(libxl__egc *egc,
-                                  libxl__stream_read_state *stream, int rc)
-{
-    assert(stream->in_checkpoint_state);
-    stream->in_checkpoint_state = false;
-    stream->checkpoint_callback(egc, stream, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_stream_write.c b/tools/libxl/libxl_stream_write.c
deleted file mode 100644 (file)
index 634f324..0000000
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * Copyright (C) 2015      Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-/*
- * Infrastructure for writing a domain to a libxl migration v2 stream.
- *
- * Entry points from outside:
- *  - libxl__stream_write_start()
- *     - Start writing a stream from the start.
- *  - libxl__stream_write_start_checkpoint()
- *     - Write the records which form a checkpoint into a stream.
- *
- * In normal operation, there are two tasks running at once; this
- * stream processing, and the libxl-save-helper.  check_all_finished()
- * is used to join all the tasks in both success and error cases.
- *
- * Nomenclature for event callbacks:
- *  - $FOO_done(): Completion callback for $FOO
- *  - write_$FOO(): Set up the datacopier to write a $FOO
- *  - $BAR_header(): A $BAR record header only
- *  - $BAR_record(): A complete $BAR record with header and content
- *
- * The main loop for a plain VM writes:
- *  - Stream header
- *  - Libxc record
- *  - (optional) Emulator xenstore record
- *  - if (hvm)
- *      - Emulator context record
- *  - End record
- *
- * For checkpointed stream, there is a second loop which is triggered by a
- * save-helper checkpoint callback.  It writes:
- *  - (optional) Emulator xenstore record
- *  - if (hvm)
- *      - Emulator context record
- *  - Checkpoint end record
- *
- * For back channel stream:
- * - libxl__stream_write_start()
- *    - Set up the stream to running state
- *
- * - Use libxl__stream_write_checkpoint_state to write the record. When the
- *   record is written out, call stream->checkpoint_callback() to return.
- */
-
-/* Success/error/cleanup handling. */
-static void stream_success(libxl__egc *egc,
-                           libxl__stream_write_state *stream);
-static void stream_complete(libxl__egc *egc,
-                            libxl__stream_write_state *stream, int rc);
-static void stream_done(libxl__egc *egc,
-                        libxl__stream_write_state *stream, int rc);
-static void checkpoint_done(libxl__egc *egc,
-                            libxl__stream_write_state *stream,
-                            int rc);
-static void check_all_finished(libxl__egc *egc,
-                               libxl__stream_write_state *stream, int rc);
-
-/* Event chain for a plain VM. */
-static void stream_header_done(libxl__egc *egc,
-                               libxl__datacopier_state *dc,
-                               int rc, int onwrite, int errnoval);
-static void libxc_header_done(libxl__egc *egc,
-                              libxl__stream_write_state *stream);
-/* libxl__xc_domain_save_done() lives here, event-order wise. */
-static void write_emulator_xenstore_record(libxl__egc *egc,
-                                           libxl__stream_write_state *stream);
-static void emulator_xenstore_record_done(libxl__egc *egc,
-                                          libxl__stream_write_state *stream);
-static void write_emulator_context_record(libxl__egc *egc,
-                                          libxl__stream_write_state *stream);
-static void emulator_context_read_done(libxl__egc *egc,
-                                       libxl__datacopier_state *dc,
-                                       int rc, int onwrite, int errnoval);
-static void emulator_context_record_done(libxl__egc *egc,
-                                         libxl__stream_write_state *stream);
-static void write_end_record(libxl__egc *egc,
-                             libxl__stream_write_state *stream);
-
-/* Event chain unique to checkpointed streams. */
-static void write_checkpoint_end_record(libxl__egc *egc,
-                                        libxl__stream_write_state *stream);
-static void checkpoint_end_record_done(libxl__egc *egc,
-                                       libxl__stream_write_state *stream);
-
-/* checkpoint state */
-static void write_checkpoint_state_done(libxl__egc *egc,
-                                        libxl__stream_write_state *stream);
-static void checkpoint_state_done(libxl__egc *egc,
-                                  libxl__stream_write_state *stream, int rc);
-
-/*----- Helpers -----*/
-
-static void write_done(libxl__egc *egc,
-                       libxl__datacopier_state *dc,
-                       int rc, int onwrite, int errnoval);
-
-/* Generic helper to set up writing some data to the stream. */
-static void setup_generic_write(libxl__egc *egc,
-                                libxl__stream_write_state *stream,
-                                const char *what,
-                                libxl__sr_rec_hdr *hdr,
-                                libxl__sr_emulator_hdr *emu_hdr,
-                                void *body,
-                                sws_record_done_cb cb)
-{
-    static const uint8_t zero_padding[1U << REC_ALIGN_ORDER] = { 0 };
-
-    libxl__datacopier_state *dc = &stream->dc;
-    int rc;
-
-    assert(stream->record_done_callback == NULL);
-
-    dc->writewhat = what;
-    dc->used      = 0;
-    dc->callback  = write_done;
-    rc = libxl__datacopier_start(dc);
-
-    if (rc) {
-        stream_complete(egc, stream, rc);
-        return;
-    }
-
-    size_t padsz = ROUNDUP(hdr->length, REC_ALIGN_ORDER) - hdr->length;
-    uint32_t length = hdr->length;
-
-    /* Insert header */
-    libxl__datacopier_prefixdata(egc, dc, hdr, sizeof(*hdr));
-
-    /* Optional emulator sub-header */
-    if (emu_hdr) {
-        assert(length >= sizeof(*emu_hdr));
-        libxl__datacopier_prefixdata(egc, dc, emu_hdr, sizeof(*emu_hdr));
-        length -= sizeof(*emu_hdr);
-    }
-
-    /* Optional body */
-    if (body)
-        libxl__datacopier_prefixdata(egc, dc, body, length);
-
-    /* Any required padding */
-    if (padsz > 0)
-        libxl__datacopier_prefixdata(egc, dc,
-                                     zero_padding, padsz);
-    stream->record_done_callback = cb;
-}
-
-/* Helper to set up writing a regular record to the stream. */
-static void setup_write(libxl__egc *egc,
-                        libxl__stream_write_state *stream,
-                        const char *what,
-                        libxl__sr_rec_hdr *hdr,
-                        void *body,
-                        sws_record_done_cb cb)
-{
-    setup_generic_write(egc, stream, what, hdr, NULL, body, cb);
-}
-
-/* Helper to set up writing a record with an emulator prefix to the stream. */
-static void setup_emulator_write(libxl__egc *egc,
-                                 libxl__stream_write_state *stream,
-                                 const char *what,
-                                 libxl__sr_rec_hdr *hdr,
-                                 libxl__sr_emulator_hdr *emu_hdr,
-                                 void *body,
-                                 sws_record_done_cb cb)
-{
-    assert(stream->emu_sub_hdr.id != EMULATOR_UNKNOWN);
-    setup_generic_write(egc, stream, what, hdr, emu_hdr, body, cb);
-}
-
-
-static void write_done(libxl__egc *egc,
-                       libxl__datacopier_state *dc,
-                       int rc, int onwrite, int errnoval)
-{
-    libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
-    STATE_AO_GC(stream->ao);
-    sws_record_done_cb cb = stream->record_done_callback;
-
-    stream->record_done_callback = NULL;
-
-    if (onwrite || errnoval)
-        stream_complete(egc, stream, rc ?: ERROR_FAIL);
-    else
-        cb(egc, stream);
-}
-
-/*----- Entrypoints -----*/
-
-void libxl__stream_write_init(libxl__stream_write_state *stream)
-{
-    assert(stream->ao);
-
-    stream->shs.ao = stream->ao;
-    libxl__save_helper_init(&stream->shs);
-
-    stream->rc = 0;
-    stream->running = false;
-    stream->in_checkpoint = false;
-    stream->sync_teardown = false;
-    FILLZERO(stream->dc);
-    stream->record_done_callback = NULL;
-    FILLZERO(stream->emu_dc);
-    stream->emu_carefd = NULL;
-    FILLZERO(stream->emu_rec_hdr);
-    FILLZERO(stream->emu_sub_hdr);
-    stream->emu_body = NULL;
-    stream->device_model_version = LIBXL_DEVICE_MODEL_VERSION_UNKNOWN;
-}
-
-void libxl__stream_write_start(libxl__egc *egc,
-                               libxl__stream_write_state *stream)
-{
-    libxl__datacopier_state *dc = &stream->dc;
-    libxl__domain_save_state *dss = stream->dss;
-    STATE_AO_GC(stream->ao);
-    struct libxl__sr_hdr hdr;
-    int rc = 0;
-
-    libxl__stream_write_init(stream);
-
-    stream->running = true;
-
-    dc->ao        = ao;
-    dc->readfd    = -1;
-    dc->writewhat = "stream header";
-    dc->copywhat  = "save v2 stream";
-    dc->writefd   = stream->fd;
-    dc->maxsz     = -1;
-    dc->callback  = stream_header_done;
-
-    if (stream->back_channel)
-        return;
-
-    if (dss->type == LIBXL_DOMAIN_TYPE_HVM) {
-        stream->device_model_version =
-            libxl__device_model_version_running(gc, dss->domid);
-        switch (stream->device_model_version) {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            stream->emu_sub_hdr.id = EMULATOR_QEMU_TRADITIONAL;
-            break;
-
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            stream->emu_sub_hdr.id = EMULATOR_QEMU_UPSTREAM;
-            break;
-
-        default:
-            rc = ERROR_FAIL;
-            LOGD(ERROR, dss->domid, "Unknown emulator for HVM domain");
-            goto err;
-        }
-        stream->emu_sub_hdr.index = 0;
-    }
-
-    rc = libxl__datacopier_start(dc);
-    if (rc)
-        goto err;
-
-    FILLZERO(hdr);
-    hdr.ident   = htobe64(RESTORE_STREAM_IDENT);
-    hdr.version = htobe32(RESTORE_STREAM_VERSION);
-    hdr.options = htobe32(0);
-
-    libxl__datacopier_prefixdata(egc, dc, &hdr, sizeof(hdr));
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-void libxl__stream_write_start_checkpoint(libxl__egc *egc,
-                                          libxl__stream_write_state *stream)
-{
-    assert(stream->running);
-    assert(!stream->in_checkpoint);
-    assert(!stream->back_channel);
-    stream->in_checkpoint = true;
-
-    write_emulator_xenstore_record(egc, stream);
-}
-
-void libxl__stream_write_abort(libxl__egc *egc,
-                               libxl__stream_write_state *stream, int rc)
-{
-    assert(rc);
-
-    if (stream->running)
-        stream_complete(egc, stream, rc);
-}
-
-/*----- Event logic -----*/
-
-static void stream_header_done(libxl__egc *egc,
-                               libxl__datacopier_state *dc,
-                               int rc, int onwrite, int errnoval)
-{
-    libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
-    STATE_AO_GC(stream->ao);
-    struct libxl__sr_rec_hdr rec;
-
-    if (rc || errnoval) {
-        stream_complete(egc, stream, rc ?: ERROR_FAIL);
-        return;
-    }
-
-    FILLZERO(rec);
-    rec.type = REC_TYPE_LIBXC_CONTEXT;
-
-    setup_write(egc, stream, "libxc header",
-                &rec, NULL, libxc_header_done);
-}
-
-static void libxc_header_done(libxl__egc *egc,
-                              libxl__stream_write_state *stream)
-{
-    libxl__xc_domain_save(egc, stream->dss, &stream->shs);
-}
-
-void libxl__xc_domain_save_done(libxl__egc *egc, void *dss_void,
-                                int rc, int retval, int errnoval)
-{
-    libxl__domain_save_state *dss = dss_void;
-    libxl__stream_write_state *stream = &dss->sws;
-    STATE_AO_GC(dss->ao);
-
-    if (rc)
-        goto err;
-
-    if (retval) {
-        LOGEVD(ERROR, errnoval, dss->domid, "saving domain: %s",
-              dss->dsps.guest_responded ?
-              "domain responded to suspend request" :
-              "domain did not respond to suspend request");
-        if (!dss->dsps.guest_responded)
-            rc = ERROR_GUEST_TIMEDOUT;
-        else if (dss->rc)
-            rc = dss->rc;
-        else
-            rc = ERROR_FAIL;
-        goto err;
-    }
-
- err:
-    check_all_finished(egc, stream, rc);
-
-    /*
-     * This function is the callback associated with the save helper
-     * task, not the stream task.  We do not know whether the stream is
-     * alive, and check_all_finished() may have torn it down around us.
-     * If the stream is not still alive, we must not continue any work.
-     */
-    if (libxl__stream_write_inuse(stream)) {
-        if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE)
-            /*
-             * For remus, if libxl__xc_domain_save_done() completes,
-             * there was an error sending data to the secondary.
-             * Resume the primary ASAP. The caller doesn't care of the
-             * return value (Please refer to libxl__remus_teardown())
-             */
-            stream_complete(egc, stream, 0);
-        else
-            write_emulator_xenstore_record(egc, stream);
-    }
-}
-
-static void write_emulator_xenstore_record(libxl__egc *egc,
-                                           libxl__stream_write_state *stream)
-{
-    libxl__domain_save_state *dss = stream->dss;
-    STATE_AO_GC(stream->ao);
-    struct libxl__sr_rec_hdr rec;
-    int rc;
-    char *buf = NULL;
-    uint32_t len = 0;
-
-    if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
-        emulator_xenstore_record_done(egc, stream);
-        return;
-    }
-
-    rc = libxl__save_emulator_xenstore_data(dss, &buf, &len);
-    if (rc)
-        goto err;
-
-    /* No record? - All done. */
-    if (len == 0) {
-        emulator_xenstore_record_done(egc, stream);
-        return;
-    }
-
-    FILLZERO(rec);
-    rec.type = REC_TYPE_EMULATOR_XENSTORE_DATA;
-    rec.length = len + sizeof(stream->emu_sub_hdr);
-
-    setup_emulator_write(egc, stream, "emulator xenstore record",
-                         &rec, &stream->emu_sub_hdr, buf,
-                         emulator_xenstore_record_done);
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-static void emulator_xenstore_record_done(libxl__egc *egc,
-                                          libxl__stream_write_state *stream)
-{
-    libxl__domain_save_state *dss = stream->dss;
-
-    if (dss->type == LIBXL_DOMAIN_TYPE_HVM)
-        write_emulator_context_record(egc, stream);
-    else {
-        if (stream->in_checkpoint)
-            write_checkpoint_end_record(egc, stream);
-        else
-            write_end_record(egc, stream);
-    }
-}
-
-static void write_emulator_context_record(libxl__egc *egc,
-                                          libxl__stream_write_state *stream)
-{
-    libxl__domain_save_state *dss = stream->dss;
-    libxl__datacopier_state *dc = &stream->emu_dc;
-    STATE_AO_GC(stream->ao);
-    struct libxl__sr_rec_hdr *rec = &stream->emu_rec_hdr;
-    struct stat st;
-    int rc;
-
-    if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
-        emulator_context_record_done(egc, stream);
-        return;
-    }
-
-    /* Convenience aliases */
-    const char *const filename = dss->dsps.dm_savefile;
-
-    libxl__carefd_begin();
-    int readfd = open(filename, O_RDONLY);
-    stream->emu_carefd = libxl__carefd_opened(CTX, readfd);
-    if (readfd == -1) {
-        rc = ERROR_FAIL;
-        LOGED(ERROR, dss->domid, "unable to open %s", filename);
-        goto err;
-    }
-
-    if (fstat(readfd, &st)) {
-        rc = ERROR_FAIL;
-        LOGED(ERROR, dss->domid, "unable to fstat %s", filename);
-        goto err;
-    }
-
-    if (!S_ISREG(st.st_mode)) {
-        rc = ERROR_FAIL;
-        LOGD(ERROR, dss->domid, "%s is not a plain file!", filename);
-        goto err;
-    }
-
-    rec->type = REC_TYPE_EMULATOR_CONTEXT;
-    rec->length = st.st_size + sizeof(stream->emu_sub_hdr);
-    stream->emu_body = libxl__malloc(NOGC, st.st_size);
-
-    FILLZERO(*dc);
-    dc->ao            = stream->ao;
-    dc->readwhat      = "qemu save file";
-    dc->copywhat      = "save v2 stream";
-    dc->readfd        = readfd;
-    dc->writefd       = -1;
-    dc->maxsz         = -1;
-    dc->readbuf       = stream->emu_body;
-    dc->bytes_to_read = st.st_size;
-    dc->callback      = emulator_context_read_done;
-
-    rc = libxl__datacopier_start(dc);
-    if (rc)
-        goto err;
-
-    return;
-
- err:
-    assert(rc);
-    stream_complete(egc, stream, rc);
-}
-
-static void emulator_context_read_done(libxl__egc *egc,
-                                       libxl__datacopier_state *dc,
-                                       int rc, int onwrite, int errnoval)
-{
-    libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
-    STATE_AO_GC(stream->ao);
-
-    if (rc || onwrite || errnoval) {
-        stream_complete(egc, stream, rc ?: ERROR_FAIL);
-        return;
-    }
-
-    libxl__carefd_close(stream->emu_carefd);
-    stream->emu_carefd = NULL;
-
-    setup_emulator_write(egc, stream, "emulator record",
-                         &stream->emu_rec_hdr,
-                         &stream->emu_sub_hdr,
-                         stream->emu_body,
-                         emulator_context_record_done);
-}
-
-static void emulator_context_record_done(libxl__egc *egc,
-                                         libxl__stream_write_state *stream)
-{
-    free(stream->emu_body);
-    stream->emu_body = NULL;
-
-    if (stream->in_checkpoint)
-        write_checkpoint_end_record(egc, stream);
-    else
-        write_end_record(egc, stream);
-}
-
-static void write_end_record(libxl__egc *egc,
-                             libxl__stream_write_state *stream)
-{
-    struct libxl__sr_rec_hdr rec;
-
-    FILLZERO(rec);
-    rec.type = REC_TYPE_END;
-
-    setup_write(egc, stream, "end record",
-                &rec, NULL, stream_success);
-}
-
-static void write_checkpoint_end_record(libxl__egc *egc,
-                                        libxl__stream_write_state *stream)
-{
-    struct libxl__sr_rec_hdr rec;
-
-    FILLZERO(rec);
-    rec.type = REC_TYPE_CHECKPOINT_END;
-
-    setup_write(egc, stream, "checkpoint end record",
-                &rec, NULL, checkpoint_end_record_done);
-}
-
-static void checkpoint_end_record_done(libxl__egc *egc,
-                                       libxl__stream_write_state *stream)
-{
-    checkpoint_done(egc, stream, 0);
-}
-
-/*----- Success/error/cleanup handling. -----*/
-
-static void stream_success(libxl__egc *egc, libxl__stream_write_state *stream)
-{
-    stream_complete(egc, stream, 0);
-}
-
-static void stream_complete(libxl__egc *egc,
-                            libxl__stream_write_state *stream, int rc)
-{
-    assert(stream->running);
-
-    if (stream->in_checkpoint) {
-        assert(rc);
-
-        /*
-         * If an error is encountered while in a checkpoint, pass it
-         * back to libxc.  The failure will come back around to us via
-         * libxl__xc_domain_save_done()
-         */
-        checkpoint_done(egc, stream, rc);
-        return;
-    }
-
-    if (stream->in_checkpoint_state) {
-        assert(rc);
-
-        /*
-         * If an error is encountered while in a checkpoint, pass it
-         * back to libxc.  The failure will come back around to us via
-         * 1. normal stream
-         *    libxl__xc_domain_save_done()
-         * 2. back_channel stream
-         *    libxl__stream_write_abort()
-         */
-        checkpoint_state_done(egc, stream, rc);
-        return;
-    }
-
-    stream_done(egc, stream, rc);
-}
-
-static void stream_done(libxl__egc *egc,
-                        libxl__stream_write_state *stream, int rc)
-{
-    assert(stream->running);
-    assert(!stream->in_checkpoint_state);
-    stream->running = false;
-
-    if (stream->emu_carefd)
-        libxl__carefd_close(stream->emu_carefd);
-    free(stream->emu_body);
-
-    if (!stream->back_channel) {
-        /*
-         * 1. In stream_done(), stream->running is set to false, so
-         *    the stream itself is not in use.
-         * 2. Write stream is a back channel stream, this means it
-         *    is only used by secondary(restore side) to send records
-         *    back, so it doesn't have save helper.
-         * So we don't need invoke check_all_finished here
-         */
-         check_all_finished(egc, stream, rc);
-    }
-}
-
-static void checkpoint_done(libxl__egc *egc,
-                            libxl__stream_write_state *stream,
-                            int rc)
-{
-    assert(stream->in_checkpoint);
-
-    stream->in_checkpoint = false;
-    stream->checkpoint_callback(egc, stream, rc);
-}
-
-static void check_all_finished(libxl__egc *egc,
-                               libxl__stream_write_state *stream,
-                               int rc)
-{
-    STATE_AO_GC(stream->ao);
-
-    /*
-     * In the case of a failure, the _abort()'s below might cancel
-     * synchronously on top of us, or asynchronously at a later point.
-     *
-     * We must avoid the situation where all _abort() cancel
-     * synchronously and the completion_callback() gets called twice;
-     * once by the first error and once by the final stacked abort(),
-     * both of whom will find that all of the tasks have stopped.
-     *
-     * To avoid this problem, any stacked re-entry into this function is
-     * ineligible to fire the completion callback.  The outermost
-     * instance will take care of completing, once the stack has
-     * unwound.
-     */
-    if (stream->sync_teardown)
-        return;
-
-    if (!stream->rc && rc) {
-        /* First reported failure. Tear everything down. */
-        stream->rc = rc;
-        stream->sync_teardown = true;
-
-        libxl__stream_write_abort(egc, stream, rc);
-        libxl__save_helper_abort(egc, &stream->shs);
-
-        stream->sync_teardown = false;
-    }
-
-    /* Don't fire the callback until all our parallel tasks have stopped. */
-    if (libxl__stream_write_inuse(stream) ||
-        libxl__save_helper_inuse(&stream->shs))
-        return;
-
-    if (stream->completion_callback)
-        /* back channel stream doesn't have completion_callback() */
-        stream->completion_callback(egc, stream, stream->rc);
-}
-
-/*----- checkpoint state -----*/
-
-void libxl__stream_write_checkpoint_state(libxl__egc *egc,
-                                          libxl__stream_write_state *stream,
-                                          libxl_sr_checkpoint_state *srcs)
-{
-    struct libxl__sr_rec_hdr rec;
-
-    assert(stream->running);
-    assert(!stream->in_checkpoint);
-    assert(!stream->in_checkpoint_state);
-    stream->in_checkpoint_state = true;
-
-    FILLZERO(rec);
-    rec.type = REC_TYPE_CHECKPOINT_STATE;
-    rec.length = sizeof(*srcs);
-
-    setup_write(egc, stream, "checkpoint state", &rec,
-                srcs, write_checkpoint_state_done);
-}
-
-static void write_checkpoint_state_done(libxl__egc *egc,
-                                        libxl__stream_write_state *stream)
-{
-    checkpoint_state_done(egc, stream, 0);
-}
-
-static void checkpoint_state_done(libxl__egc *egc,
-                                  libxl__stream_write_state *stream, int rc)
-{
-    assert(stream->in_checkpoint_state);
-    stream->in_checkpoint_state = false;
-    stream->checkpoint_callback(egc, stream, rc);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_test_fdevent.c b/tools/libxl/libxl_test_fdevent.c
deleted file mode 100644 (file)
index 2d875d9..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * fdevent test helpr for the libxl event system
- */
-
-#include "libxl_internal.h"
-
-#include "libxl_test_fdevent.h"
-
-typedef struct {
-    libxl__ao *ao;
-    libxl__ev_fd fd;
-    libxl__ao_abortable abrt;
-} libxl__test_fdevent;
-
-static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
-                             int rc);
-
-static void tfe_init(libxl__test_fdevent *tfe, libxl__ao *ao)
-{
-    tfe->ao = ao;
-    libxl__ev_fd_init(&tfe->fd);
-    libxl__ao_abortable_init(&tfe->abrt);
-}
-
-static void tfe_cleanup(libxl__gc *gc, libxl__test_fdevent *tfe)
-{
-    libxl__ev_fd_deregister(gc, &tfe->fd);
-    libxl__ao_abortable_deregister(&tfe->abrt);
-}
-
-static void tfe_fd_cb(libxl__egc *egc, libxl__ev_fd *ev,
-                      int fd, short events, short revents)
-{
-    libxl__test_fdevent *tfe = CONTAINER_OF(ev,*tfe,fd);
-    STATE_AO_GC(tfe->ao);
-    fdevent_complete(egc, tfe, 0);
-}
-
-static void tfe_abrt_cb(libxl__egc *egc, libxl__ao_abortable *abrt,
-                        int rc)
-{
-    libxl__test_fdevent *tfe = CONTAINER_OF(abrt,*tfe,abrt);
-    STATE_AO_GC(tfe->ao);
-    fdevent_complete(egc, tfe, rc);
-}
-
-static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe,
-                             int rc)
-{
-    STATE_AO_GC(tfe->ao);
-    tfe_cleanup(gc, tfe);
-    libxl__ao_complete(egc, ao, rc);
-}
-
-int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
-                       libxl_asyncop_how *ao_how)
-{
-    int rc;
-    libxl__test_fdevent *tfe;
-
-    AO_CREATE(ctx, 0, ao_how);
-    GCNEW(tfe);
-
-    tfe_init(tfe, ao);
-
-    rc = libxl__ev_fd_register(gc, &tfe->fd, tfe_fd_cb, fd, events);
-    if (rc) goto out;
-
-    tfe->abrt.ao = ao;
-    tfe->abrt.callback = tfe_abrt_cb;
-    rc = libxl__ao_abortable_register(&tfe->abrt);
-    if (rc) goto out;
-
-    return AO_INPROGRESS;
-
- out:
-    tfe_cleanup(gc, tfe);
-    return AO_CREATE_FAIL(rc);
-}
diff --git a/tools/libxl/libxl_test_fdevent.h b/tools/libxl/libxl_test_fdevent.h
deleted file mode 100644 (file)
index 82a307e..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef TEST_FDEVENT_H
-#define TEST_FDEVENT_H
-
-#include <pthread.h>
-
-int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events,
-                       libxl_asyncop_how *ao_how)
-                       LIBXL_EXTERNAL_CALLERS_ONLY;
-/* This operation waits for one of the poll events to occur on fd, and
- * then completes successfully.  (Or, it can be aborted.) */
-
-#endif /*TEST_FDEVENT_H*/
diff --git a/tools/libxl/libxl_test_timedereg.c b/tools/libxl/libxl_test_timedereg.c
deleted file mode 100644 (file)
index a567db6..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * timedereg test case for the libxl event system
- *
- * To run this test:
- *    ./test_timedereg
- * Success:
- *    program takes a few seconds, prints some debugging output and exits 0
- * Failure:
- *    crash
- *
- * set up [0]-group timeouts 0 1 2
- * wait for timeout 1 to occur
- * deregister 0 and 2.  1 is supposed to be deregistered already
- * register [1]-group 0 1 2
- * deregister 1 (should be a no-op)
- * wait for [1]-group 0 1 2 in turn
- * on final callback assert that all have been deregistered
- */
-
-#include "libxl_internal.h"
-
-#include "libxl_test_timedereg.h"
-
-#define NTIMES 3
-static const int ms[2][NTIMES] = { { 2000,1000,2000 }, { 1000,2000,3000 } };
-static libxl__ev_time et[2][NTIMES];
-static libxl__ao *tao;
-static int seq;
-
-static void occurs(libxl__egc *egc, libxl__ev_time *ev,
-                   const struct timeval *requested_abs, int rc);
-
-static void regs(libxl__ao *ao, int j)
-{
-    AO_GC;
-    int rc, i;
-    LOG(DEBUG,"regs(%d)", j);
-    for (i=0; i<NTIMES; i++) {
-        rc = libxl__ev_time_register_rel(ao, &et[j][i], occurs, ms[j][i]);
-        assert(!rc);
-    }    
-}
-
-int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
-{
-    int i;
-    AO_CREATE(ctx, 0, ao_how);
-
-    tao = ao;
-
-    for (i=0; i<NTIMES; i++) {
-        libxl__ev_time_init(&et[0][i]);
-        libxl__ev_time_init(&et[1][i]);
-    }
-
-    regs(ao, 0);
-
-    return AO_INPROGRESS;
-}
-
-static void occurs(libxl__egc *egc, libxl__ev_time *ev,
-                   const struct timeval *requested_abs, int rc)
-{
-    EGC_GC;
-    int i;
-
-    int off = ev - &et[0][0];
-    LOG(DEBUG,"occurs[%d][%d] seq=%d rc=%d", off/NTIMES, off%NTIMES, seq, rc);
-
-    assert(rc == ERROR_TIMEDOUT);
-
-    switch (seq) {
-    case 0:
-        assert(ev == &et[0][1]);
-        libxl__ev_time_deregister(gc, &et[0][0]);
-        libxl__ev_time_deregister(gc, &et[0][2]);
-        regs(tao, 1);
-        libxl__ev_time_deregister(gc, &et[0][1]);
-        break;
-
-    case 1:
-    case 2:
-        assert(ev == &et[1][seq-1]);
-        break;
-        
-    case 3:
-        assert(ev == &et[1][2]);
-        for (i=0; i<NTIMES; i++) {
-            assert(!libxl__ev_time_isregistered(&et[0][i]));
-            assert(!libxl__ev_time_isregistered(&et[1][i]));
-        }
-        libxl__ao_complete(egc, tao, 0);
-        return;
-
-    default:
-        abort();
-    }
-
-    seq++;
-}
diff --git a/tools/libxl/libxl_test_timedereg.h b/tools/libxl/libxl_test_timedereg.h
deleted file mode 100644 (file)
index 9547dba..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef TEST_TIMEDEREG_H
-#define TEST_TIMEDEREG_H
-
-#include <pthread.h>
-
-int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how)
-    LIBXL_EXTERNAL_CALLERS_ONLY;
-
-#endif /*TEST_TIMEDEREG_H*/
diff --git a/tools/libxl/libxl_tmem.c b/tools/libxl/libxl_tmem.c
deleted file mode 100644 (file)
index a553b39..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2009-2017 Citrix Ltd and other contributors
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-/* TMEM is gone. Leave some stubs here. */
-
-char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long)
-{
-    GC_INIT(ctx);
-    LOGED(ERROR, domid, "Can not get tmem list");
-    GC_FREE;
-    return NULL;
-}
-
-int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid)
-{
-    GC_INIT(ctx);
-    LOGED(ERROR, domid, "Can not freeze tmem pools");
-    GC_FREE;
-    return ERROR_FAIL;
-}
-
-int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid)
-{
-    GC_INIT(ctx);
-    LOGED(ERROR, domid, "Can not thaw tmem pools");
-    GC_FREE;
-    return ERROR_FAIL;
-}
-
-int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name, uint32_t set)
-{
-    GC_INIT(ctx);
-    LOGED(ERROR, domid, "Can not set tmem %s", name);
-    GC_FREE;
-    return ERROR_FAIL;
-}
-
-int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid,
-                           char* uuid, int auth)
-{
-    GC_INIT(ctx);
-    LOGED(ERROR, domid, "Can not set tmem shared auth");
-    GC_FREE;
-    return ERROR_FAIL;
-}
-
-int libxl_tmem_freeable(libxl_ctx *ctx)
-{
-    GC_INIT(ctx);
-    LOGE(ERROR, "Can not get tmem freeable memory");
-    GC_FREE;
-    return ERROR_FAIL;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
deleted file mode 100644 (file)
index 9d3f05f..0000000
+++ /dev/null
@@ -1,1224 +0,0 @@
-# -*- python -*-
-#
-# Builtin libxl types
-#
-
-namespace("libxl_")
-
-libxl_defbool = Builtin("defbool", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, copy_fn=None,
-                        check_default_fn="libxl__defbool_is_default")
-libxl_domid = Builtin("domid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__uint32_parse_json",
-                      json_parse_type = "JSON_INTEGER", autogenerate_json = False, copy_fn=None)
-libxl_devid = Builtin("devid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__int_parse_json",
-                      json_parse_type = "JSON_INTEGER", autogenerate_json = False, signed = True, init_val="-1",
-                      copy_fn=None)
-libxl_uuid = Builtin("uuid", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl_uuid_is_nil",
-                     copy_fn="libxl_uuid_copy")
-libxl_mac = Builtin("mac", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl__mac_is_default",
-                    copy_fn="libxl_mac_copy")
-libxl_bitmap = Builtin("bitmap", json_parse_type="JSON_ARRAY", dispose_fn="libxl_bitmap_dispose", passby=PASS_BY_REFERENCE,
-                       check_default_fn="libxl_bitmap_is_empty", copy_fn="libxl_bitmap_copy_alloc")
-libxl_cpuid_policy_list = Builtin("cpuid_policy_list", dispose_fn="libxl_cpuid_dispose", passby=PASS_BY_REFERENCE,
-                                  json_parse_type="JSON_ARRAY", check_default_fn="libxl__cpuid_policy_is_empty",
-                                  copy_fn="libxl_cpuid_policy_list_copy")
-
-libxl_string_list = Builtin("string_list", dispose_fn="libxl_string_list_dispose", passby=PASS_BY_REFERENCE,
-                            json_parse_type="JSON_ARRAY", check_default_fn="libxl__string_list_is_empty",
-                            copy_fn="libxl_string_list_copy")
-libxl_key_value_list = Builtin("key_value_list", dispose_fn="libxl_key_value_list_dispose", passby=PASS_BY_REFERENCE,
-                               json_parse_type="JSON_MAP", check_default_fn="libxl__key_value_list_is_empty",
-                               copy_fn="libxl_key_value_list_copy")
-libxl_hwcap = Builtin("hwcap", passby=PASS_BY_REFERENCE, json_parse_type="JSON_ARRAY",
-                      check_default_fn="libxl__hwcap_is_default", copy_fn="libxl_hwcap_copy")
-libxl_ms_vm_genid = Builtin("ms_vm_genid", passby=PASS_BY_REFERENCE, check_default_fn="libxl_ms_vm_genid_is_zero",
-                            copy_fn="libxl_ms_vm_genid_copy")
-
-#
-# Specific integer types
-#
-
-MemKB = UInt(64, init_val = "LIBXL_MEMKB_DEFAULT", json_gen_fn = "libxl__uint64_gen_json")
-
-#
-# Constants / Enumerations
-#
-
-libxl_error = Enumeration("error", [
-    (-1, "NONSPECIFIC"),
-    (-2, "VERSION"),
-    (-3, "FAIL"),
-    (-4, "NI"),
-    (-5, "NOMEM"),
-    (-6, "INVAL"),
-    (-7, "BADFAIL"),
-    (-8, "GUEST_TIMEDOUT"),
-    (-9, "TIMEDOUT"),
-    (-10, "NOPARAVIRT"),
-    (-11, "NOT_READY"),
-    (-12, "OSEVENT_REG_FAIL"),
-    (-13, "BUFFERFULL"),
-    (-14, "UNKNOWN_CHILD"),
-    (-15, "LOCK_FAIL"),
-    (-16, "JSON_CONFIG_EMPTY"),
-    (-17, "DEVICE_EXISTS"),
-    (-18, "CHECKPOINT_DEVOPS_DOES_NOT_MATCH"),
-    (-19, "CHECKPOINT_DEVICE_NOT_SUPPORTED"),
-    (-20, "VNUMA_CONFIG_INVALID"),
-    (-21, "DOMAIN_NOTFOUND"),
-    (-22, "ABORTED"),
-    (-23, "NOTFOUND"),
-    (-24, "DOMAIN_DESTROYED"), # Target domain ceased to exist during op
-    (-25, "FEATURE_REMOVED"), # For functionality that has been removed
-    (-26, "PROTOCOL_ERROR_QMP"),
-    (-27, "UNKNOWN_QMP_ERROR"),
-    (-28, "QMP_GENERIC_ERROR"), # unspecified qmp error
-    (-29, "QMP_COMMAND_NOT_FOUND"), # the requested command has not been found
-    (-30, "QMP_DEVICE_NOT_ACTIVE"), # a device has failed to be become active
-    (-31, "QMP_DEVICE_NOT_FOUND"), # the requested device has not been found
-    (-32, "QEMU_API"), # QEMU's replies don't contains expected members
-    ], value_namespace = "")
-
-libxl_domain_type = Enumeration("domain_type", [
-    (-1, "INVALID"),
-    (1, "HVM"),
-    (2, "PV"),
-    (3, "PVH"),
-    ], init_val = "LIBXL_DOMAIN_TYPE_INVALID")
-
-libxl_rdm_reserve_strategy = Enumeration("rdm_reserve_strategy", [
-    (0, "ignore"),
-    (1, "host"),
-    ])
-
-libxl_rdm_reserve_policy = Enumeration("rdm_reserve_policy", [
-    (-1, "invalid"),
-    (0, "strict"),
-    (1, "relaxed"),
-    ], init_val = "LIBXL_RDM_RESERVE_POLICY_INVALID")
-
-libxl_channel_connection = Enumeration("channel_connection", [
-    (0, "UNKNOWN"),
-    (1, "PTY"),
-    (2, "SOCKET"), # a listening Unix domain socket
-    ])
-
-libxl_device_model_version = Enumeration("device_model_version", [
-    (0, "UNKNOWN"),
-    (1, "QEMU_XEN_TRADITIONAL"), # Historical qemu-xen device model (qemu-dm)
-    (2, "QEMU_XEN"),             # Upstream based qemu-xen device model
-    ])
-
-libxl_console_type = Enumeration("console_type", [
-    (0, "UNKNOWN"),
-    (1, "SERIAL"),
-    (2, "PV"),
-    (3, "VUART"),
-    ])
-
-libxl_disk_format = Enumeration("disk_format", [
-    (0, "UNKNOWN"),
-    (1, "QCOW"),
-    (2, "QCOW2"),
-    (3, "VHD"),
-    (4, "RAW"),
-    (5, "EMPTY"),
-    (6, "QED"),
-    ])
-
-libxl_disk_backend = Enumeration("disk_backend", [
-    (0, "UNKNOWN"),
-    (1, "PHY"),
-    (2, "TAP"),
-    (3, "QDISK"),
-    ])
-
-libxl_nic_type = Enumeration("nic_type", [
-    (0, "UNKNOWN"),
-    (1, "VIF_IOEMU"),
-    (2, "VIF"),
-    ])
-
-libxl_action_on_shutdown = Enumeration("action_on_shutdown", [
-    (1, "DESTROY"),
-
-    (2, "RESTART"),
-    (3, "RESTART_RENAME"),
-
-    (4, "PRESERVE"),
-
-    (5, "COREDUMP_DESTROY"),
-    (6, "COREDUMP_RESTART"),
-
-    (7, "SOFT_RESET"),
-    ], init_val = "LIBXL_ACTION_ON_SHUTDOWN_DESTROY")
-
-libxl_trigger = Enumeration("trigger", [
-    (0, "UNKNOWN"),
-    (1, "POWER"),
-    (2, "SLEEP"),
-    (3, "NMI"),
-    (4, "INIT"),
-    (5, "RESET"),
-    (6, "S3RESUME"),
-    ])
-
-libxl_tsc_mode = Enumeration("tsc_mode", [
-    (0, "default"),
-    (1, "always_emulate"),
-    (2, "native"),
-    (3, "native_paravirt"),
-    ])
-
-libxl_gfx_passthru_kind = Enumeration("gfx_passthru_kind", [
-    (0, "default"),
-    (1, "igd"),
-    ])
-
-# Consistent with the values defined for HVM_PARAM_TIMER_MODE.
-libxl_timer_mode = Enumeration("timer_mode", [
-    (-1, "unknown"),
-    (0, "delay_for_missed_ticks"),
-    (1, "no_delay_for_missed_ticks"),
-    (2, "no_missed_ticks_pending"),
-    (3, "one_missed_tick_pending"),
-    ], init_val = "LIBXL_TIMER_MODE_DEFAULT",
-       check_default_fn = "libxl__timer_mode_is_default")
-
-libxl_bios_type = Enumeration("bios_type", [
-    (0, "unknown"),
-    (1, "rombios"),
-    (2, "seabios"),
-    (3, "ovmf"),
-    ])
-
-# Consistent with values defined in domctl.h
-# Except unknown which we have made up
-libxl_scheduler = Enumeration("scheduler", [
-    (0, "unknown"),
-    (4, "sedf"),
-    (5, "credit"),
-    (6, "credit2"),
-    (7, "arinc653"),
-    (8, "rtds"),
-    (9, "null"),
-    ])
-
-# Consistent with SHUTDOWN_* in sched.h (apart from UNKNOWN)
-libxl_shutdown_reason = Enumeration("shutdown_reason", [
-    (-1, "unknown"),
-    (0, "poweroff"),
-    (1, "reboot"),
-    (2, "suspend"),
-    (3, "crash"),
-    (4, "watchdog"),
-    (5, "soft_reset"),
-    ], init_val = "LIBXL_SHUTDOWN_REASON_UNKNOWN")
-
-libxl_vga_interface_type = Enumeration("vga_interface_type", [
-    (0, "UNKNOWN"),
-    (1, "CIRRUS"),
-    (2, "STD"),
-    (3, "NONE"),
-    (4, "QXL"),
-    ], init_val = "LIBXL_VGA_INTERFACE_TYPE_UNKNOWN")
-
-libxl_vendor_device = Enumeration("vendor_device", [
-    (0, "NONE"),
-    (1, "XENSERVER"),
-    ])
-
-libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [
-    (0, "base"),
-    (1, "freq"),
-    (2, "time_ref_count"),
-    (3, "reference_tsc"),
-    (4, "hcall_remote_tlb_flush"),
-    (5, "apic_assist"),
-    (6, "crash_ctl"),
-    (7, "synic"),
-    (8, "stimer"),
-    (9, "hcall_ipi"),
-    ])
-
-libxl_hdtype = Enumeration("hdtype", [
-    (1, "IDE"),
-    (2, "AHCI"),
-    ], init_val = "LIBXL_HDTYPE_IDE")
-
-# Consistent with the values defined for migration_stream.
-libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
-    (0, "NONE"),
-    (1, "REMUS"),
-    (2, "COLO"),
-    ])
-
-libxl_vuart_type = Enumeration("vuart_type", [
-    (0, "unknown"),
-    (1, "sbsa_uart"),
-    ])
-
-libxl_vkb_backend = Enumeration("vkb_backend", [
-    (0, "UNKNOWN"),
-    (1, "QEMU"),
-    (2, "LINUX")
-    ])
-
-libxl_passthrough = Enumeration("passthrough", [
-    (0, "default"),
-    (1, "disabled"),
-    (2, "enabled"), # becomes {sync,share}_pt once defaults are evaluated
-    (3, "sync_pt"),
-    (4, "share_pt"),
-    ])
-
-#
-# Complex libxl types
-#
-
-libxl_ioport_range = Struct("ioport_range", [
-    ("first", uint32),
-    ("number", uint32),
-    ])
-
-libxl_iomem_range = Struct("iomem_range", [
-    # start host frame number to be mapped to the guest
-    ("start", uint64),
-    # number of frames to be mapped
-    ("number", uint64),
-    # guest frame number used as a start for the mapping
-    ("gfn", uint64, {'init_val': "LIBXL_INVALID_GFN"}),
-    ])
-
-libxl_vga_interface_info = Struct("vga_interface_info", [
-    ("kind",    libxl_vga_interface_type),
-    ])
-
-libxl_vnc_info = Struct("vnc_info", [
-    ("enable",        libxl_defbool),
-    # "address:port" that should be listened on
-    ("listen",        string),
-    ("passwd",        string),
-    ("display",       integer),
-    # If set then try to find an unused port
-    ("findunused",    libxl_defbool),
-    ])
-
-libxl_spice_info = Struct("spice_info", [
-    ("enable",      libxl_defbool),
-    # At least one of spice port or spicetls_post must be given
-    ("port",        integer),
-    ("tls_port",    integer),
-    # Interface to bind to
-    ("host",        string),
-    # enable client connection with no password
-    ("disable_ticketing", libxl_defbool),
-    ("passwd",      string),
-    ("agent_mouse", libxl_defbool),
-    ("vdagent",     libxl_defbool),
-    ("clipboard_sharing", libxl_defbool),
-    ("usbredirection", integer),
-    ("image_compression", string),
-    ("streaming_video", string),
-    ])
-
-libxl_sdl_info = Struct("sdl_info", [
-    ("enable",        libxl_defbool),
-    ("opengl",        libxl_defbool),
-    ("display",       string),
-    ("xauthority",    string),
-    ])
-
-libxl_dominfo = Struct("dominfo",[
-    ("uuid",        libxl_uuid),
-    ("domid",       libxl_domid),
-    ("ssidref",     uint32),
-    ("ssid_label",  string),
-    ("running",     bool),
-    ("blocked",     bool),
-    ("paused",      bool),
-    ("shutdown",    bool),
-    ("dying",       bool),
-    ("never_stop",  bool),
-
-    # Valid iff ->shutdown is true.
-    #
-    # Otherwise set to a value guaranteed not to clash with any valid
-    # LIBXL_SHUTDOWN_REASON_* constant.
-    ("shutdown_reason", libxl_shutdown_reason),
-    ("outstanding_memkb",  MemKB),
-    ("current_memkb",   MemKB),
-    ("shared_memkb", MemKB),
-    ("paged_memkb", MemKB),
-    ("max_memkb",   MemKB),
-    ("cpu_time",    uint64),
-    ("vcpu_max_id", uint32),
-    ("vcpu_online", uint32),
-    ("cpupool",     uint32),
-    ("domain_type", libxl_domain_type),
-    ], dir=DIR_OUT)
-
-libxl_cpupoolinfo = Struct("cpupoolinfo", [
-    ("poolid",      uint32),
-    ("pool_name",   string),
-    ("sched",       libxl_scheduler),
-    ("n_dom",       uint32),
-    ("cpumap",      libxl_bitmap)
-    ], dir=DIR_OUT)
-
-libxl_channelinfo = Struct("channelinfo", [
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("devid", libxl_devid),
-    ("state", integer),
-    ("evtch", integer),
-    ("rref", integer),
-    ("u", KeyedUnion(None, libxl_channel_connection, "connection",
-           [("unknown", None),
-            ("pty", Struct(None, [("path", string),])),
-            ("socket", None),
-           ])),
-    ], dir=DIR_OUT)
-
-libxl_vminfo = Struct("vminfo", [
-    ("uuid", libxl_uuid),
-    ("domid", libxl_domid),
-    ], dir=DIR_OUT)
-
-libxl_version_info = Struct("version_info", [
-    ("xen_version_major", integer),
-    ("xen_version_minor", integer),
-    ("xen_version_extra", string),
-    ("compiler",          string),
-    ("compile_by",        string),
-    ("compile_domain",    string),
-    ("compile_date",      string),
-    ("capabilities",      string),
-    ("changeset",         string),
-    ("virt_start",        uint64),
-    ("pagesize",          integer),
-    ("commandline",       string),
-    ("build_id",          string),
-    ], dir=DIR_OUT)
-
-libxl_domain_create_info = Struct("domain_create_info",[
-    ("type",         libxl_domain_type),
-    ("hap",          libxl_defbool),
-    ("oos",          libxl_defbool),
-    ("ssidref",      uint32),
-    ("ssid_label",   string),
-    ("name",         string),
-    ("domid",        libxl_domid),
-    ("uuid",         libxl_uuid),
-    ("xsdata",       libxl_key_value_list),
-    ("platformdata", libxl_key_value_list),
-    ("poolid",       uint32),
-    ("pool_name",    string),
-    ("run_hotplug_scripts",libxl_defbool),
-    ("driver_domain",libxl_defbool),
-    ("passthrough",  libxl_passthrough),
-    ("xend_suspend_evtchn_compat",libxl_defbool),
-    ], dir=DIR_IN)
-
-libxl_domain_restore_params = Struct("domain_restore_params", [
-    ("checkpointed_stream", integer),
-    ("stream_version", uint32, {'init_val': '1'}),
-    ("colo_proxy_script", string),
-    ("userspace_colo_proxy", libxl_defbool),
-    ])
-
-libxl_sched_params = Struct("sched_params",[
-    ("vcpuid",       integer, {'init_val': 'LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT'}),
-    ("weight",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
-    ("cap",          integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
-    ("period",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
-    ("extratime",    integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
-    ("budget",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
-    ])
-
-libxl_vcpu_sched_params = Struct("vcpu_sched_params",[
-    ("sched",        libxl_scheduler),
-    ("vcpus",        Array(libxl_sched_params, "num_vcpus")),
-    ])
-
-libxl_domain_sched_params = Struct("domain_sched_params",[
-    ("sched",        libxl_scheduler),
-    ("weight",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}),
-    ("cap",          integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}),
-    ("period",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}),
-    ("budget",       integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}),
-    ("extratime",    integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}),
-
-    # The following three parameters ('slice' and 'latency') are deprecated,
-    # and will have no effect if used, since the SEDF scheduler has been removed.
-    # Note that 'period' and 'extratime' was an SDF parameter too, but it is still effective
-    # as they are now used (together with 'budget') by the RTDS scheduler.
-    ("slice",        integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT'}),
-    ("latency",      integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT'}),
-    ])
-
-libxl_vnode_info = Struct("vnode_info", [
-    ("memkb", MemKB),
-    ("distances", Array(uint32, "num_distances")), # distances from this node to other nodes
-    ("pnode", uint32), # physical node of this node
-    ("vcpus", libxl_bitmap), # vcpus in this node
-    ])
-
-libxl_gic_version = Enumeration("gic_version", [
-    (0, "DEFAULT"),
-    (0x20, "v2"),
-    (0x30, "v3")
-    ], init_val = "LIBXL_GIC_VERSION_DEFAULT")
-
-libxl_tee_type = Enumeration("tee_type", [
-    (0, "none"),
-    (1, "optee")
-    ], init_val = "LIBXL_TEE_TYPE_NONE")
-
-libxl_rdm_reserve = Struct("rdm_reserve", [
-    ("strategy",    libxl_rdm_reserve_strategy),
-    ("policy",      libxl_rdm_reserve_policy),
-    ])
-
-# Consistent with the values defined for HVM_PARAM_ALTP2M
-libxl_altp2m_mode = Enumeration("altp2m_mode", [
-    (0, "disabled"),
-    (1, "mixed"),
-    (2, "external"),
-    (3, "limited"),
-    ], init_val = "LIBXL_ALTP2M_MODE_DISABLED")
-
-libxl_domain_build_info = Struct("domain_build_info",[
-    ("max_vcpus",       integer),
-    ("avail_vcpus",     libxl_bitmap),
-    ("cpumap",          libxl_bitmap),
-    ("nodemap",         libxl_bitmap),
-    ("vcpu_hard_affinity", Array(libxl_bitmap, "num_vcpu_hard_affinity")),
-    ("vcpu_soft_affinity", Array(libxl_bitmap, "num_vcpu_soft_affinity")),
-    ("numa_placement",  libxl_defbool),
-    ("tsc_mode",        libxl_tsc_mode),
-    ("max_memkb",       MemKB),
-    ("target_memkb",    MemKB),
-    ("video_memkb",     MemKB),
-    ("shadow_memkb",    MemKB),
-    ("iommu_memkb",     MemKB),
-    ("rtc_timeoffset",  uint32),
-    ("exec_ssidref",    uint32),
-    ("exec_ssid_label", string),
-    ("localtime",       libxl_defbool),
-    ("disable_migrate", libxl_defbool),
-    ("cpuid",           libxl_cpuid_policy_list),
-    ("blkdev_start",    string),
-
-    ("vnuma_nodes", Array(libxl_vnode_info, "num_vnuma_nodes")),
-
-    ("max_grant_frames",    uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
-    ("max_maptrack_frames", uint32, {'init_val': 'LIBXL_MAX_GRANT_DEFAULT'}),
-    
-    ("device_model_version", libxl_device_model_version),
-    ("device_model_stubdomain", libxl_defbool),
-    ("stubdomain_memkb",   MemKB),
-    ("stubdomain_kernel",  string),
-    ("stubdomain_ramdisk", string),
-    # if you set device_model you must set device_model_version too
-    ("device_model",     string),
-    ("device_model_ssidref", uint32),
-    ("device_model_ssid_label", string),
-    ("device_model_user", string),
-
-    # extra parameters pass directly to qemu, NULL terminated
-    ("extra",            libxl_string_list),
-    # extra parameters pass directly to qemu for PV guest, NULL terminated
-    ("extra_pv",         libxl_string_list),
-    # extra parameters pass directly to qemu for HVM guest, NULL terminated
-    ("extra_hvm",        libxl_string_list),
-    #  parameters for all type of scheduler
-    ("sched_params",     libxl_domain_sched_params),
-
-    ("ioports",          Array(libxl_ioport_range, "num_ioports")),
-    ("irqs",             Array(uint32, "num_irqs")),
-    ("iomem",            Array(libxl_iomem_range, "num_iomem")),
-    ("claim_mode",          libxl_defbool),
-    ("event_channels",   uint32),
-    ("kernel",           string),
-    ("cmdline",          string),
-    ("ramdisk",          string),
-    # Given the complexity of verifying the validity of a device tree,
-    # libxl doesn't do any security check on it. It's the responsibility
-    # of the caller to provide only trusted device tree.
-    # Note that the partial device tree should avoid to use the phandle
-    # 65000 which is reserved by the toolstack.
-    ("device_tree",      string),
-    ("acpi",             libxl_defbool),
-    ("bootloader",       string),
-    ("bootloader_args",  libxl_string_list),
-    ("timer_mode",       libxl_timer_mode),
-    ("nested_hvm",       libxl_defbool),
-    ("apic",             libxl_defbool),
-    ("dm_restrict",      libxl_defbool),
-    ("tee",              libxl_tee_type),
-    ("u", KeyedUnion(None, libxl_domain_type, "type",
-                [("hvm", Struct(None, [("firmware",         string),
-                                       ("bios",             libxl_bios_type),
-                                       ("pae",              libxl_defbool),
-                                       ("apic",             libxl_defbool, {'deprecated_by': 'apic'}),
-                                       # The following acpi field is deprecated.
-                                       # Please use the unified acpi field above
-                                       # which works for both x86 and ARM.
-                                       ("acpi",             libxl_defbool),
-                                       ("acpi_s3",          libxl_defbool),
-                                       ("acpi_s4",          libxl_defbool),
-                                       ("acpi_laptop_slate",libxl_defbool),
-                                       ("nx",               libxl_defbool),
-                                       ("viridian",         libxl_defbool),
-                                       ("viridian_enable",  libxl_bitmap),
-                                       ("viridian_disable", libxl_bitmap),
-                                       ("timeoffset",       string),
-                                       ("hpet",             libxl_defbool),
-                                       ("vpt_align",        libxl_defbool),
-                                       ("mmio_hole_memkb",  MemKB),
-                                       ("timer_mode",       libxl_timer_mode, {'deprecated_by': 'timer_mode'}),
-                                       ("nested_hvm",       libxl_defbool, {'deprecated_by': 'nested_hvm'}),
-                                       # The u.hvm.altp2m field is used solely
-                                       # for x86 HVM guests and is maintained
-                                       # for legacy purposes.
-                                       ("altp2m",           libxl_defbool),
-                                       ("system_firmware",  string),
-                                       ("smbios_firmware",  string),
-                                       ("acpi_firmware",    string),
-                                       ("hdtype",           libxl_hdtype),
-                                       ("nographic",        libxl_defbool),
-                                       ("vga",              libxl_vga_interface_info),
-                                       ("vnc",              libxl_vnc_info),
-                                       # keyboard layout, default is en-us keyboard
-                                       ("keymap",           string),
-                                       ("sdl",              libxl_sdl_info),
-                                       ("spice",            libxl_spice_info),
-                                       
-                                       ("gfx_passthru",     libxl_defbool),
-                                       ("gfx_passthru_kind", libxl_gfx_passthru_kind),
-                                       
-                                       ("serial",           string),
-                                       ("boot",             string),
-                                       ("usb",              libxl_defbool),
-                                       ("usbversion",       integer),
-                                       # usbdevice:
-                                       # - "tablet" for absolute mouse,
-                                       # - "mouse" for PS/2 protocol relative mouse
-                                       ("usbdevice",        string),
-                                       ("vkb_device",       libxl_defbool),
-                                       ("soundhw",          string),
-                                       ("xen_platform_pci", libxl_defbool),
-                                       ("usbdevice_list",   libxl_string_list),
-                                       ("vendor_device",    libxl_vendor_device),
-                                       # See libxl_ms_vm_genid_generate()
-                                       ("ms_vm_genid",      libxl_ms_vm_genid),
-                                       ("serial_list",      libxl_string_list),
-                                       ("rdm", libxl_rdm_reserve),
-                                       ("rdm_mem_boundary_memkb", MemKB),
-                                       ("mca_caps",         uint64),
-                                       ])),
-                 ("pv", Struct(None, [("kernel", string, {'deprecated_by': 'kernel'}),
-                                      ("slack_memkb", MemKB),
-                                      ("bootloader", string, {'deprecated_by': 'bootloader'}),
-                                      ("bootloader_args", libxl_string_list, {'deprecated_by': 'bootloader_args'}),
-                                      ("cmdline", string, {'deprecated_by': 'cmdline'}),
-                                      ("ramdisk", string, {'deprecated_by': 'ramdisk'}),
-                                      ("features", string, {'const': True}),
-                                      # Use host's E820 for PCI passthrough.
-                                      ("e820_host", libxl_defbool),
-                                      ])),
-                 ("pvh", Struct(None, [("pvshim", libxl_defbool),
-                                       ("pvshim_path", string),
-                                       ("pvshim_cmdline", string),
-                                       ("pvshim_extra", string), # eg "loglvl=all guest_loglvl=all apic_verbosity=debug e820-verbose"
-                                       ])),
-                 ("invalid", None),
-                 ], keyvar_init_val = "LIBXL_DOMAIN_TYPE_INVALID")),
-
-
-    ("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
-                               ("vuart", libxl_vuart_type),
-                              ])),
-    # Alternate p2m is not bound to any architecture or guest type, as it is
-    # supported by x86 HVM and ARM support is planned.
-    ("altp2m", libxl_altp2m_mode),
-
-    ], dir=DIR_IN,
-       copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
-)
-
-libxl_device_vfb = Struct("device_vfb", [
-    ("backend_domid", libxl_domid),
-    ("backend_domname",string),
-    ("devid",         libxl_devid),
-    ("vnc",           libxl_vnc_info),
-    ("sdl",           libxl_sdl_info),
-    # set keyboard layout, default is en-us keyboard
-    ("keymap",        string),
-    ])
-
-libxl_device_vkb = Struct("device_vkb", [
-    ("backend_domid", libxl_domid),
-    ("backend_domname", string),
-    ("devid", libxl_devid),
-    ("backend_type", libxl_vkb_backend),
-    ("unique_id", string),
-    ("feature_disable_keyboard", bool),
-    ("feature_disable_pointer", bool),
-    ("feature_abs_pointer", bool),
-    ("feature_raw_pointer", bool),
-    ("feature_multi_touch", bool),
-    ("width", uint32),
-    ("height", uint32),
-    ("multi_touch_width", uint32),
-    ("multi_touch_height", uint32),
-    ("multi_touch_num_contacts", uint32)
-    ])
-
-libxl_device_disk = Struct("device_disk", [
-    ("backend_domid", libxl_domid),
-    ("backend_domname", string),
-    ("pdev_path", string),
-    ("vdev", string),
-    ("backend", libxl_disk_backend),
-    ("format", libxl_disk_format),
-    ("script", string),
-    ("removable", integer),
-    ("readwrite", integer),
-    ("is_cdrom", integer),
-    ("direct_io_safe", bool),
-    ("discard_enable", libxl_defbool),
-    # Note that the COLO configuration settings should be considered unstable.
-    # They may change incompatibly in future versions of Xen.
-    ("colo_enable", libxl_defbool),
-    ("colo_restore_enable", libxl_defbool),
-    ("colo_host", string),
-    ("colo_port", integer),
-    ("colo_export", string),
-    ("active_disk", string),
-    ("hidden_disk", string)
-    ])
-
-libxl_device_nic = Struct("device_nic", [
-    ("backend_domid", libxl_domid),
-    ("backend_domname", string),
-    ("devid", libxl_devid),
-    ("mtu", integer),
-    ("model", string),
-    ("mac", libxl_mac),
-    ("ip", string),
-    ("bridge", string),
-    ("ifname", string),
-    ("script", string),
-    ("nictype", libxl_nic_type),
-    ("rate_bytes_per_interval", uint64),
-    ("rate_interval_usecs", uint32),
-    ("gatewaydev", string),
-    # Note that the COLO configuration settings should be considered unstable.
-    # They may change incompatibly in future versions of Xen.
-    ("coloft_forwarddev", string),
-    ("colo_sock_mirror_id", string),
-    ("colo_sock_mirror_ip", string),
-    ("colo_sock_mirror_port", string),
-    ("colo_sock_compare_pri_in_id", string),
-    ("colo_sock_compare_pri_in_ip", string),
-    ("colo_sock_compare_pri_in_port", string),
-    ("colo_sock_compare_sec_in_id", string),
-    ("colo_sock_compare_sec_in_ip", string),
-    ("colo_sock_compare_sec_in_port", string),
-    ("colo_sock_compare_notify_id", string),
-    ("colo_sock_compare_notify_ip", string),
-    ("colo_sock_compare_notify_port", string),
-    ("colo_sock_redirector0_id", string),
-    ("colo_sock_redirector0_ip", string),
-    ("colo_sock_redirector0_port", string),
-    ("colo_sock_redirector1_id", string),
-    ("colo_sock_redirector1_ip", string),
-    ("colo_sock_redirector1_port", string),
-    ("colo_sock_redirector2_id", string),
-    ("colo_sock_redirector2_ip", string),
-    ("colo_sock_redirector2_port", string),
-    ("colo_filter_mirror_queue", string),
-    ("colo_filter_mirror_outdev", string),
-    ("colo_filter_redirector0_queue", string),
-    ("colo_filter_redirector0_indev", string),
-    ("colo_filter_redirector0_outdev", string),
-    ("colo_filter_redirector1_queue", string),
-    ("colo_filter_redirector1_indev", string),
-    ("colo_filter_redirector1_outdev", string),
-    ("colo_compare_pri_in", string),
-    ("colo_compare_sec_in", string),
-    ("colo_compare_out", string),
-    ("colo_compare_notify_dev", string),
-    ("colo_sock_sec_redirector0_id", string),
-    ("colo_sock_sec_redirector0_ip", string),
-    ("colo_sock_sec_redirector0_port", string),
-    ("colo_sock_sec_redirector1_id", string),
-    ("colo_sock_sec_redirector1_ip", string),
-    ("colo_sock_sec_redirector1_port", string),
-    ("colo_filter_sec_redirector0_queue", string),
-    ("colo_filter_sec_redirector0_indev", string),
-    ("colo_filter_sec_redirector0_outdev", string),
-    ("colo_filter_sec_redirector1_queue", string),
-    ("colo_filter_sec_redirector1_indev", string),
-    ("colo_filter_sec_redirector1_outdev", string),
-    ("colo_filter_sec_rewriter0_queue", string),
-    ("colo_checkpoint_host", string),
-    ("colo_checkpoint_port", string)
-    ])
-
-libxl_device_pci = Struct("device_pci", [
-    ("func",      uint8),
-    ("dev",       uint8),
-    ("bus",       uint8),
-    ("domain",    integer),
-    ("vdevfn",    uint32),
-    ("vfunc_mask", uint32),
-    ("msitranslate", bool),
-    ("power_mgmt", bool),
-    ("permissive", bool),
-    ("seize", bool),
-    ("rdm_policy",      libxl_rdm_reserve_policy),
-    ])
-
-libxl_device_rdm = Struct("device_rdm", [
-    ("start", uint64),
-    ("size", uint64),
-    ("policy", libxl_rdm_reserve_policy),
-    ])
-
-libxl_usbctrl_type = Enumeration("usbctrl_type", [
-    (0, "AUTO"),
-    (1, "PV"),
-    (2, "DEVICEMODEL"),
-    (3, "QUSB"),
-    ])
-
-libxl_usbdev_type = Enumeration("usbdev_type", [
-    (1, "hostdev"),
-    ])
-
-libxl_device_usbctrl = Struct("device_usbctrl", [
-    ("type", libxl_usbctrl_type),
-    ("devid", libxl_devid),
-    ("version", integer),
-    ("ports", integer),
-    ("backend_domid", libxl_domid),
-    ("backend_domname", string),
-   ])
-
-libxl_device_usbdev = Struct("device_usbdev", [
-    ("ctrl", libxl_devid),
-    ("port", integer),
-    ("u", KeyedUnion(None, libxl_usbdev_type, "type",
-           [("hostdev", Struct(None, [
-                 ("hostbus",   uint8),
-                 ("hostaddr",  uint8)])),
-           ])),
-    ])
-
-libxl_device_dtdev = Struct("device_dtdev", [
-    ("path", string),
-    ])
-
-libxl_device_vtpm = Struct("device_vtpm", [
-    ("backend_domid",    libxl_domid),
-    ("backend_domname",  string),
-    ("devid",            libxl_devid),
-    ("uuid",             libxl_uuid),
-])
-
-libxl_device_p9 = Struct("device_p9", [
-    ("backend_domid",    libxl_domid),
-    ("backend_domname",  string),
-    ("tag",              string),
-    ("path",             string),
-    ("security_model",   string),
-    ("devid",            libxl_devid),
-])
-
-libxl_device_pvcallsif = Struct("device_pvcallsif", [
-    ("backend_domid",    libxl_domid),
-    ("backend_domname",  string),
-    ("devid",            libxl_devid),
-])
-
-libxl_device_channel = Struct("device_channel", [
-    ("backend_domid", libxl_domid),
-    ("backend_domname", string),
-    ("devid", libxl_devid),
-    ("name", string),
-    ("u", KeyedUnion(None, libxl_channel_connection, "connection",
-           [("unknown", None),
-            ("pty", None),
-            ("socket", Struct(None, [("path", string)])),
-           ])),
-])
-
-libxl_connector_param = Struct("connector_param", [
-    ("unique_id", string),
-    ("width", uint32),
-    ("height", uint32)
-    ])
-
-libxl_device_vdispl = Struct("device_vdispl", [
-    ("backend_domid", libxl_domid),
-    ("backend_domname", string),
-    ("devid", libxl_devid),
-    ("be_alloc", bool),
-    ("connectors", Array(libxl_connector_param, "num_connectors"))
-    ])
-
-libxl_vsnd_pcm_format = Enumeration("vsnd_pcm_format", [
-    (1,  "S8"),
-    (2,  "U8"),
-    (3,  "S16_LE"),
-    (4,  "S16_BE"),
-    (5,  "U16_LE"),
-    (6,  "U16_BE"),
-    (7,  "S24_LE"),
-    (8,  "S24_BE"),
-    (9,  "U24_LE"),
-    (10, "U24_BE"),
-    (11, "S32_LE"),
-    (12, "S32_BE"),
-    (13, "U32_LE"),
-    (14, "U32_BE"),
-    (15, "F32_LE"),
-    (16, "F32_BE"),
-    (17, "F64_LE"),
-    (18, "F64_BE"),
-    (19, "IEC958_SUBFRAME_LE"),
-    (20, "IEC958_SUBFRAME_BE"),
-    (21, "MU_LAW"),
-    (22, "A_LAW"),
-    (23, "IMA_ADPCM"),
-    (24, "MPEG"),
-    (25, "GSM")
-    ])
-
-libxl_vsnd_params = Struct("vsnd_params", [
-    ("sample_rates", Array(uint32, "num_sample_rates")),
-    ("sample_formats", Array(libxl_vsnd_pcm_format, "num_sample_formats")),
-    ("channels_min", uint32),
-    ("channels_max", uint32),
-    ("buffer_size", uint32)
-    ])
-
-libxl_vsnd_stream_type = Enumeration("vsnd_stream_type", [
-    (1, "P"),
-    (2, "C")
-    ])
-
-libxl_vsnd_stream = Struct("vsnd_stream", [
-    ("unique_id", string),
-    ("type", libxl_vsnd_stream_type),
-    ("params", libxl_vsnd_params)
-    ])
-
-libxl_vsnd_pcm = Struct("vsnd_pcm", [
-    ("name", string),
-    ("params", libxl_vsnd_params),
-    ("streams", Array(libxl_vsnd_stream, "num_vsnd_streams"))
-    ])
-
-libxl_device_vsnd = Struct("device_vsnd", [
-    ("backend_domid", libxl_domid),
-    ("backend_domname", string),
-    ("devid", libxl_devid),
-    ("short_name", string),
-    ("long_name", string),
-    ("params", libxl_vsnd_params),
-    ("pcms", Array(libxl_vsnd_pcm, "num_vsnd_pcms"))
-    ])
-
-libxl_domain_config = Struct("domain_config", [
-    ("c_info", libxl_domain_create_info),
-    ("b_info", libxl_domain_build_info),
-
-    ("disks", Array(libxl_device_disk, "num_disks")),
-    ("nics", Array(libxl_device_nic, "num_nics")),
-    ("pcidevs", Array(libxl_device_pci, "num_pcidevs")),
-    ("rdms", Array(libxl_device_rdm, "num_rdms")),
-    ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")),
-    ("vfbs", Array(libxl_device_vfb, "num_vfbs")),
-    ("vkbs", Array(libxl_device_vkb, "num_vkbs")),
-    ("vtpms", Array(libxl_device_vtpm, "num_vtpms")),
-    ("p9s", Array(libxl_device_p9, "num_p9s")),
-    ("pvcallsifs", Array(libxl_device_pvcallsif, "num_pvcallsifs")),
-    ("vdispls", Array(libxl_device_vdispl, "num_vdispls")),
-    ("vsnds", Array(libxl_device_vsnd, "num_vsnds")),
-    # a channel manifests as a console with a name,
-    # see docs/misc/channels.txt
-    ("channels", Array(libxl_device_channel, "num_channels")),
-    ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")),
-    ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")),
-
-    ("on_poweroff", libxl_action_on_shutdown),
-    ("on_reboot", libxl_action_on_shutdown),
-    ("on_watchdog", libxl_action_on_shutdown),
-    ("on_crash", libxl_action_on_shutdown),
-    ("on_soft_reset", libxl_action_on_shutdown),
-    ], dir=DIR_IN)
-
-libxl_diskinfo = Struct("diskinfo", [
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("devid", libxl_devid),
-    ("state", integer),
-    ("evtch", integer),
-    ("rref", integer),
-    ], dir=DIR_OUT)
-
-libxl_nicinfo = Struct("nicinfo", [
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("devid", libxl_devid),
-    ("state", integer),
-    ("evtch", integer),
-    ("rref_tx", integer),
-    ("rref_rx", integer),
-    ], dir=DIR_OUT)
-
-libxl_vtpminfo = Struct("vtpminfo", [
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("devid", libxl_devid),
-    ("state", integer),
-    ("evtch", integer),
-    ("rref", integer),
-    ("uuid", libxl_uuid),
-    ], dir=DIR_OUT)
-
-libxl_usbctrlinfo = Struct("usbctrlinfo", [
-    ("type", libxl_usbctrl_type),
-    ("devid", libxl_devid),
-    ("version", integer),
-    ("ports", integer),
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("state", integer),
-    ("evtch", integer),
-    ("ref_urb", integer),
-    ("ref_conn", integer),
-    ], dir=DIR_OUT)
-
-libxl_vcpuinfo = Struct("vcpuinfo", [
-    ("vcpuid", uint32),
-    ("cpu", uint32),
-    ("online", bool),
-    ("blocked", bool),
-    ("running", bool),
-    ("vcpu_time", uint64), # total vcpu time ran (ns)
-    ("cpumap", libxl_bitmap), # current hard cpu affinity
-    ("cpumap_soft", libxl_bitmap), # current soft cpu affinity
-    ], dir=DIR_OUT)
-
-libxl_physinfo = Struct("physinfo", [
-    ("threads_per_core", uint32),
-    ("cores_per_socket", uint32),
-
-    ("max_cpu_id", uint32),
-    ("nr_cpus", uint32),
-    ("cpu_khz", uint32),
-
-    ("total_pages", uint64),
-    ("free_pages", uint64),
-    ("scrub_pages", uint64),
-    ("outstanding_pages", uint64),
-    ("sharing_freed_pages", uint64),
-    ("sharing_used_frames", uint64),
-    ("max_possible_mfn", uint64),
-
-    ("nr_nodes", uint32),
-    ("hw_cap", libxl_hwcap),
-
-    ("cap_hvm", bool),
-    ("cap_pv", bool),
-    ("cap_hvm_directio", bool), # No longer HVM specific
-    ("cap_hap", bool),
-    ("cap_shadow", bool),
-    ("cap_iommu_hap_pt_share", bool),
-    ], dir=DIR_OUT)
-
-libxl_connectorinfo = Struct("connectorinfo", [
-    ("unique_id", string),
-    ("width", uint32),
-    ("height", uint32),
-    ("req_evtch", integer),
-    ("req_rref", integer),
-    ("evt_evtch", integer),
-    ("evt_rref", integer),
-    ], dir=DIR_OUT)
-
-libxl_vdisplinfo = Struct("vdisplinfo", [
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("devid", libxl_devid),
-    ("state", integer),
-    ("be_alloc", bool),
-    ("connectors", Array(libxl_connectorinfo, "num_connectors"))
-    ], dir=DIR_OUT)
-
-libxl_streaminfo = Struct("streaminfo", [
-    ("req_evtch", integer),
-    ("req_rref", integer)
-    ])
-
-libxl_pcminfo = Struct("pcminfo", [
-    ("streams", Array(libxl_streaminfo, "num_vsnd_streams"))
-    ])
-
-libxl_vsndinfo = Struct("vsndinfo", [
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("devid", libxl_devid),
-    ("state", integer),
-    ("pcms", Array(libxl_pcminfo, "num_vsnd_pcms"))
-    ])
-
-libxl_vkbinfo = Struct("vkbinfo", [
-    ("backend", string),
-    ("backend_id", uint32),
-    ("frontend", string),
-    ("frontend_id", uint32),
-    ("devid", libxl_devid),
-    ("state", integer),
-    ("evtch", integer),
-    ("rref", integer)
-    ], dir=DIR_OUT)
-
-# NUMA node characteristics: size and free are how much memory it has, and how
-# much of it is free, respectively. dists is an array of distances from this
-# node to each other node.
-libxl_numainfo = Struct("numainfo", [
-    ("size", uint64),
-    ("free", uint64),
-    ("dists", Array(uint32, "num_dists")),
-    ], dir=DIR_OUT)
-
-libxl_cputopology = Struct("cputopology", [
-    ("core", uint32),
-    ("socket", uint32),
-    ("node", uint32),
-    ], dir=DIR_OUT)
-
-libxl_pcitopology = Struct("pcitopology", [
-    ("seg", uint16),
-    ("bus", uint8),
-    ("devfn", uint8),
-    ("node", uint32),
-    ], dir=DIR_OUT)
-
-libxl_sched_credit_params = Struct("sched_credit_params", [
-    ("tslice_ms", integer),
-    ("ratelimit_us", integer),
-    ("vcpu_migr_delay_us", integer),
-    ], dispose_fn=None)
-
-libxl_sched_credit2_params = Struct("sched_credit2_params", [
-    ("ratelimit_us", integer),
-    ], dispose_fn=None)
-
-libxl_domain_remus_info = Struct("domain_remus_info",[
-    ("interval",             integer),
-    ("allow_unsafe",         libxl_defbool),
-    ("blackhole",            libxl_defbool),
-    ("compression",          libxl_defbool),
-    ("netbuf",               libxl_defbool),
-    ("netbufscript",         string),
-    ("diskbuf",              libxl_defbool),
-    ("colo",                 libxl_defbool),
-    ("userspace_colo_proxy", libxl_defbool)
-    ])
-
-libxl_event_type = Enumeration("event_type", [
-    (1, "DOMAIN_SHUTDOWN"),
-    (2, "DOMAIN_DEATH"),
-    (3, "DISK_EJECT"),
-    (4, "OPERATION_COMPLETE"),
-    (5, "DOMAIN_CREATE_CONSOLE_AVAILABLE"),
-    ])
-
-libxl_ev_user = UInt(64)
-
-libxl_ev_link = Builtin("ev_link", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, private=True)
-
-libxl_event = Struct("event",[
-    ("link",     libxl_ev_link),
-     # for use by libxl; caller may use this once the event has been
-     #   returned by libxl_event_{check,wait}
-    ("domid",    libxl_domid),
-    ("domuuid",  libxl_uuid),
-    ("for_user", libxl_ev_user),
-    ("u", KeyedUnion(None, libxl_event_type, "type",
-          [("domain_shutdown", Struct(None, [
-                                             ("shutdown_reason", uint8),
-                                      ])),
-           ("domain_death", None),
-           ("disk_eject", Struct(None, [
-                                        ("vdev", string),
-                                        ("disk", libxl_device_disk),
-                                 ])),
-           ("operation_complete", Struct(None, [
-                                        ("rc", integer),
-                                 ])),
-           ("domain_create_console_available", None),
-           ]))])
-
-libxl_psr_cmt_type = Enumeration("psr_cmt_type", [
-    (1, "CACHE_OCCUPANCY"),
-    (2, "TOTAL_MEM_COUNT"),
-    (3, "LOCAL_MEM_COUNT"),
-    ])
-
-libxl_psr_cbm_type = Enumeration("psr_cbm_type", [
-    (0, "UNKNOWN"),
-    (1, "L3_CBM"),
-    (2, "L3_CBM_CODE"),
-    (3, "L3_CBM_DATA"),
-    (4, "L2_CBM"),
-    (5, "MBA_THRTL"),
-    ])
-
-libxl_psr_cat_info = Struct("psr_cat_info", [
-    ("id", uint32),
-    ("cos_max", uint32),
-    ("cbm_len", uint32),
-    ("cdp_enabled", bool),
-    ])
-
-libxl_psr_feat_type = Enumeration("psr_feat_type", [
-    (1, "CAT"),
-    (2, "MBA"),
-    ])
-
-libxl_psr_hw_info = Struct("psr_hw_info", [
-    ("id", uint32),
-    ("u", KeyedUnion(None, libxl_psr_feat_type, "type",
-          [("cat", Struct(None, [
-                                    ("cos_max",     uint32),
-                                    ("cbm_len",     uint32),
-                                    ("cdp_enabled", bool),
-                               ])),
-           ("mba", Struct(None, [
-                                    ("cos_max",     uint32),
-                                    ("thrtl_max",   uint32),
-                                    ("linear",      bool),
-                               ])),
-          ]))
-    ], dir=DIR_OUT)
diff --git a/tools/libxl/libxl_types_internal.idl b/tools/libxl/libxl_types_internal.idl
deleted file mode 100644 (file)
index 3593e21..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-namespace("libxl__")
-hidden(True)
-
-libxl_domid = Builtin("domid", namespace="libxl_", json_gen_fn = "yajl_gen_integer",
-                     json_parse_fn = "libxl__uint32_parse_json", json_parse_type = "JSON_INTEGER",
-                     autogenerate_json = False, copy_fn = None)
-
-libxl__qmp_message_type = Enumeration("qmp_message_type", [
-    (1, "QMP"),
-    (2, "return"),
-    (3, "error"),
-    (4, "event"),
-    (5, "invalid"),
-    ])
-
-# Consider adding to QEMU_BACKEND in libxl_internal.h
-libxl__device_kind = Enumeration("device_kind", [
-    (0, "NONE"),
-    (1, "VIF"),
-    (2, "VBD"),
-    (3, "QDISK"),
-    (4, "PCI"),
-    (5, "VFB"),
-    (6, "VKBD"),
-    (7, "CONSOLE"),
-    (8, "VTPM"),
-    (9, "VUSB"),
-    (10, "QUSB"),
-    (11, "9PFS"),
-    (12, "VDISPL"),
-    (13, "VUART"),
-    (14, "PVCALLS"),
-    (15, "VSND"),
-    (16, "VINPUT"),
-    ])
-
-libxl__console_backend = Enumeration("console_backend", [
-    (1, "XENCONSOLED"),
-    (2, "IOEMU"),
-    ])
-
-libxl__device_console = Struct("device_console", [
-    ("backend_domid", libxl_domid),
-    ("devid", integer),
-    ("consback", libxl__console_backend),
-    ("output", string),
-    # A regular console has no name.
-    # A console with a name is called a 'channel', see docs/misc/channels.txt
-    ("name", string),
-    ("connection", string),
-    ("path", string),
-    ])
-
-libxl__device_action = Enumeration("device_action", [
-    (1, "ADD"),
-    (2, "REMOVE"),
-    ])
diff --git a/tools/libxl/libxl_usb.c b/tools/libxl/libxl_usb.c
deleted file mode 100644 (file)
index 171bb04..0000000
+++ /dev/null
@@ -1,2158 +0,0 @@
-/*
- * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
- * Author Chunyan Liu <cyliu@suse.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-#include <inttypes.h>
-#include <xen/io/usbif.h>
-
-#define USBBACK_INFO_PATH "/libxl/usbback"
-
-#define USBHUB_CLASS_CODE 9
-
-static int usbback_is_loaded(libxl__gc *gc)
-{
-    int r;
-    struct stat st;
-
-    r = lstat(SYSFS_USBBACK_DRIVER, &st);
-
-    if (r == 0)
-        return 1;
-    if (r < 0 && errno == ENOENT)
-        return 0;
-    LOGE(ERROR, "Accessing %s", SYSFS_USBBACK_DRIVER);
-    return ERROR_FAIL;
-}
-
-static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid,
-                                            libxl_device_usbctrl *usbctrl,
-                                            bool hotplug)
-{
-    int rc;
-    libxl_domain_type domtype = libxl__domain_type(gc, domid);
-
-    if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) {
-        if (domtype != LIBXL_DOMAIN_TYPE_HVM) {
-            rc = usbback_is_loaded(gc);
-            if (rc < 0)
-                goto out;
-            usbctrl->type = rc ? LIBXL_USBCTRL_TYPE_PV
-                               : LIBXL_USBCTRL_TYPE_QUSB;
-        } else {
-            /* FIXME: See if we can detect PV frontend */
-            usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL;
-        }
-    }
-
-    switch (usbctrl->type) {
-    case LIBXL_USBCTRL_TYPE_PV:
-    case LIBXL_USBCTRL_TYPE_QUSB:
-        if (!usbctrl->version)
-            usbctrl->version = 2;
-        if (usbctrl->version < 1 || usbctrl->version > 2) {
-            LOG(ERROR,
-                "USB version for paravirtualized devices must be 1 or 2");
-            rc = ERROR_INVAL;
-            goto out;
-        }
-        if (!usbctrl->ports)
-            usbctrl->ports = 8;
-        if (usbctrl->ports < 1 || usbctrl->ports > USBIF_MAX_PORTNR) {
-            LOG(ERROR, "Number of ports for USB controller is limited to %u",
-                USBIF_MAX_PORTNR);
-            rc = ERROR_INVAL;
-            goto out;
-        }
-        break;
-    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
-        if (!usbctrl->version)
-            usbctrl->version = 2;
-        switch (usbctrl->version) {
-        case 1:
-            /* uhci controller in qemu has fixed number of ports. */
-            if (usbctrl->ports && usbctrl->ports != 2) {
-                LOG(ERROR,
-                    "Number of ports for USB controller of version 1 is always 2");
-                rc = ERROR_INVAL;
-                goto out;
-            }
-            usbctrl->ports = 2;
-            break;
-        case 2:
-            /* ehci controller in qemu has fixed number of ports. */
-            if (usbctrl->ports && usbctrl->ports != 6) {
-                LOG(ERROR,
-                    "Number of ports for USB controller of version 2 is always 6");
-                rc = ERROR_INVAL;
-                goto out;
-            }
-            usbctrl->ports = 6;
-            break;
-        case 3:
-            if (!usbctrl->ports)
-                usbctrl->ports = 8;
-            /* xhci controller in qemu supports up to 15 ports. */
-            if (usbctrl->ports > 15) {
-                LOG(ERROR,
-                    "Number of ports for USB controller of version 3 is limited to 15");
-                rc = ERROR_INVAL;
-                goto out;
-            }
-            break;
-        default:
-            LOG(ERROR, "Illegal USB version");
-            rc = ERROR_INVAL;
-            goto out;
-        }
-        break;
-    default:
-        break;
-    }
-
-    rc = libxl__resolve_domid(gc, usbctrl->backend_domname,
-                              &usbctrl->backend_domid);
-
-out:
-    return rc;
-}
-
-static int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid,
-                                      libxl_device_usbctrl *usbctrl,
-                                      libxl__device *device)
-{
-    device->backend_devid   = usbctrl->devid;
-    device->backend_domid   = usbctrl->backend_domid;
-    switch (usbctrl->type) {
-    case LIBXL_USBCTRL_TYPE_PV:
-        device->backend_kind = LIBXL__DEVICE_KIND_VUSB;
-        break;
-    case LIBXL_USBCTRL_TYPE_QUSB:
-        device->backend_kind = LIBXL__DEVICE_KIND_QUSB;
-        break;
-    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
-        device->backend_kind = LIBXL__DEVICE_KIND_NONE;
-        break;
-    default:
-        abort(); /* can't really happen. */
-    }
-    device->devid           = usbctrl->devid;
-    device->domid           = domid;
-    device->kind            = LIBXL__DEVICE_KIND_VUSB;
-
-    return 0;
-}
-
-static const char *vusb_be_from_xs_libxl_type(libxl__gc *gc,
-                                              const char *libxl_path,
-                                              libxl_usbctrl_type type)
-{
-    const char *be_path = NULL, *tmp;
-    int r;
-
-    if (type == LIBXL_USBCTRL_TYPE_AUTO) {
-        r = libxl__xs_read_checked(gc, XBT_NULL,
-                                   GCSPRINTF("%s/type", libxl_path), &tmp);
-        if (r || libxl_usbctrl_type_from_string(tmp, &type))
-            goto out;
-    }
-
-    if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
-        be_path = libxl_path;
-        goto out;
-    }
-
-    r = libxl__xs_read_checked(gc, XBT_NULL,
-                               GCSPRINTF("%s/backend", libxl_path),
-                               &be_path);
-    if (r)
-        be_path = NULL;
-
-out:
-    return be_path;
-}
-
-/* Add usbctrl information to xenstore.
- *
- * Adding a usb controller will add a new 'qusb' or 'vusb' device in xenstore,
- * and add corresponding frontend, backend information to it. According to
- * "update_json", decide whether to update json config file.
- */
-static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t domid,
-                                              libxl_device_usbctrl *usbctrl,
-                                              bool update_json)
-{
-    libxl__device *device;
-    flexarray_t *front = NULL;
-    flexarray_t *back;
-    xs_transaction_t t = XBT_NULL;
-    int i, rc;
-    libxl_domain_config d_config;
-    libxl_device_usbctrl usbctrl_saved;
-    libxl__flock *lock = NULL;
-
-    libxl_domain_config_init(&d_config);
-    libxl_device_usbctrl_init(&usbctrl_saved);
-    libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl);
-
-    GCNEW(device);
-    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
-    if (rc) goto out;
-
-    back = flexarray_make(gc, 12, 1);
-
-    if (device->backend_kind != LIBXL__DEVICE_KIND_NONE) {
-        front = flexarray_make(gc, 4, 1);
-
-        flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
-        flexarray_append_pair(back, "online", "1");
-        flexarray_append_pair(back, "state",
-                              GCSPRINTF("%d", XenbusStateInitialising));
-        flexarray_append_pair(front, "backend-id",
-                              GCSPRINTF("%d", usbctrl->backend_domid));
-        flexarray_append_pair(front, "state",
-                              GCSPRINTF("%d", XenbusStateInitialising));
-    }
-
-    flexarray_append_pair(back, "type",
-                          (char *)libxl_usbctrl_type_to_string(usbctrl->type));
-    flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version));
-    flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports));
-    flexarray_append_pair(back, "port", "");
-    for (i = 0; i < usbctrl->ports; i++)
-        flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), "");
-
-    if (update_json) {
-        lock = libxl__lock_domain_userdata(gc, domid);
-        if (!lock) {
-            rc = ERROR_LOCK_FAIL;
-            goto out;
-        }
-
-        rc = libxl__get_domain_configuration(gc, domid, &d_config);
-        if (rc) goto out;
-
-        device_add_domain_config(gc, &d_config, &libxl__usbctrl_devtype,
-                                 &usbctrl_saved);
-
-        rc = libxl__dm_check_start(gc, &d_config, domid);
-        if (rc) goto out;
-
-        if (usbctrl->type == LIBXL_USBCTRL_TYPE_QUSB) {
-            if (!libxl__query_qemu_backend(gc, domid, usbctrl->backend_domid,
-                                           "qusb", false)) {
-                LOGD(ERROR, domid, "backend type not supported by device model");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-        }
-    }
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        rc = libxl__device_exists(gc, t, device);
-        if (rc < 0) goto out;
-        if (rc == 1) {
-            /* already exists in xenstore */
-            LOGD(ERROR, domid, "device already exists in xenstore");
-            rc = ERROR_DEVICE_EXISTS;
-            goto out;
-        }
-
-        if (update_json) {
-            rc = libxl__set_domain_configuration(gc, domid, &d_config);
-            if (rc) goto out;
-        }
-
-        libxl__device_generic_add(gc, t, device,
-                                  libxl__xs_kvs_of_flexarray(gc, back),
-                                  libxl__xs_kvs_of_flexarray(gc, front),
-                                  NULL);
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-    if (lock) libxl__unlock_file(lock);
-    libxl_device_usbctrl_dispose(&usbctrl_saved);
-    libxl_domain_config_dispose(&d_config);
-    return rc;
-}
-
-static const char *vusb_be_from_xs_libxl(libxl__gc *gc, const char *libxl_path)
-{
-    return vusb_be_from_xs_libxl_type(gc, libxl_path, LIBXL_USBCTRL_TYPE_AUTO);
-}
-
-static void libxl__device_usbctrl_del_xenstore(libxl__gc *gc, uint32_t domid,
-                                               libxl_device_usbctrl *usbctrl)
-{
-    const char *libxl_path, *be_path;
-    xs_transaction_t t = XBT_NULL;
-    int rc;
-
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
-                                                 LIBXL__DEVICE_KIND_VUSB);
-    be_path = vusb_be_from_xs_libxl_type(gc, libxl_path, usbctrl->type);
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        libxl__xs_path_cleanup(gc, t, be_path);
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    return;
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-}
-
-static char *pvusb_get_device_type(libxl_usbctrl_type type)
-{
-    switch (type) {
-    case LIBXL_USBCTRL_TYPE_PV:
-        return "vusb";
-    case LIBXL_USBCTRL_TYPE_QUSB:
-        return "qusb";
-    default:
-        return NULL;
-    }
-}
-
-/* Send qmp commands to create a usb controller in qemu.
- *
- * Depending on the speed (usbctrl->version) we create:
- * - piix3-usb-uhci (version=1), always 2 ports
- * - usb-ehci       (version=2), always 6 ports
- * - nec-usb-xhci   (version=3), up to 15 ports
- */
-static int libxl__device_usbctrl_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
-                                         libxl_device_usbctrl *usbctrl)
-{
-    EGC_GC;
-    libxl__json_object *qmp_args = NULL;
-
-    switch (usbctrl->version) {
-    case 1:
-        libxl__qmp_param_add_string(gc, &qmp_args,
-                                    "driver", "piix3-usb-uhci");
-        break;
-    case 2:
-        libxl__qmp_param_add_string(gc, &qmp_args,
-                                    "driver", "usb-ehci");
-        break;
-    case 3:
-        libxl__qmp_param_add_string(gc, &qmp_args,
-                                    "driver", "nec-usb-xhci");
-        libxl__qmp_param_add_string(gc, &qmp_args, "p2",
-                                    GCSPRINTF("%d", usbctrl->ports));
-        libxl__qmp_param_add_string(gc, &qmp_args, "p3",
-                                    GCSPRINTF("%d", usbctrl->ports));
-        break;
-    default:
-        abort(); /* Should not be possible. */
-    }
-
-    libxl__qmp_param_add_string(gc, &qmp_args, "id",
-                                GCSPRINTF("xenusb-%d", usbctrl->devid));
-
-    return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
-}
-
-/* Send qmp commands to delete a usb controller in qemu.  */
-static int libxl__device_usbctrl_del_hvm(libxl__egc *egc,
-                                         libxl__ev_qmp *qmp,
-                                         int devid)
-{
-    EGC_GC;
-    libxl__json_object *qmp_args = NULL;
-
-    libxl__qmp_param_add_string(gc, &qmp_args,
-                                "id", GCSPRINTF("xenusb-%d", devid));
-
-    return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
-}
-
-/* Send qmp commands to create a usb device in qemu. */
-static int libxl__device_usbdev_add_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
-                                        libxl_device_usbdev *usbdev)
-{
-    EGC_GC;
-    libxl__json_object *qmp_args = NULL;
-
-    libxl__qmp_param_add_string(gc, &qmp_args, "id",
-        GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
-                  usbdev->u.hostdev.hostaddr));
-    libxl__qmp_param_add_string(gc, &qmp_args, "driver", "usb-host");
-    libxl__qmp_param_add_string(gc, &qmp_args, "bus",
-        GCSPRINTF("xenusb-%d.0", usbdev->ctrl));
-    libxl__qmp_param_add_string(gc, &qmp_args, "port",
-        GCSPRINTF("%d", usbdev->port));
-    libxl__qmp_param_add_string(gc, &qmp_args, "hostbus",
-        GCSPRINTF("%d", usbdev->u.hostdev.hostbus));
-    libxl__qmp_param_add_string(gc, &qmp_args, "hostaddr",
-        GCSPRINTF("%d", usbdev->u.hostdev.hostaddr));
-
-    return libxl__ev_qmp_send(egc, qmp, "device_add", qmp_args);
-}
-
-/* Send qmp commands to delete a usb device in qemu. */
-static int libxl__device_usbdev_del_hvm(libxl__egc *egc, libxl__ev_qmp *qmp,
-                                        libxl_device_usbdev *usbdev)
-{
-    EGC_GC;
-    libxl__json_object *qmp_args = NULL;
-
-    libxl__qmp_param_add_string(gc, &qmp_args, "id",
-        GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus,
-                  usbdev->u.hostdev.hostaddr));
-
-    return libxl__ev_qmp_send(egc, qmp, "device_del", qmp_args);
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(usbctrl)
-
-static void device_usbctrl_add_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
-static void device_usbctrl_add_done(libxl__egc *egc,
-    libxl__ao_device *aodev, int rc);
-
-/* AO operation to add a usb controller.
- *
- * Generally, it does:
- * 1) fill in necessary usb controler information with default value
- * 2) write usb controller frontend/backend info to xenstore, update json
- *    config file if necessary.
- * 3) wait for device connection. PVUSB frontend and backend driver will
- *    probe xenstore paths and build connection between frontend and backend.
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->callback, aodev->update_json, ...
- */
-static void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
-                                      libxl_device_usbctrl *usbctrl,
-                                      libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    libxl__device *device;
-    int rc;
-
-    /* Store *usbctrl to be used by callbacks */
-    aodev->device_config = usbctrl;
-    aodev->device_type = &libxl__usbctrl_devtype;
-
-    rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
-                                          aodev->update_json);
-    if (rc < 0) goto out;
-
-    rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
-    if (rc) goto out;
-
-    rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
-                                            aodev->update_json);
-    if (rc) goto out;
-
-    GCNEW(device);
-    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
-    if (rc) goto outrm;
-    aodev->dev = device;
-
-    if (device->backend_kind == LIBXL__DEVICE_KIND_NONE) {
-        libxl__ev_qmp *const qmp = &aodev->qmp;
-
-        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
-                                         device_usbctrl_add_timeout,
-                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
-        if (rc) goto outrm;
-
-        qmp->ao = ao;
-        qmp->domid = domid;
-        qmp->payload_fd = -1;
-        qmp->callback = device_usbctrl_add_qmp_cb;
-        rc = libxl__device_usbctrl_add_hvm(egc, qmp, usbctrl);
-        if (rc) goto outrm;
-        return;
-    }
-
-    aodev->action = LIBXL__DEVICE_ACTION_ADD;
-    libxl__wait_device_connection(egc, aodev);
-    return;
-
-outrm:
-    libxl__device_usbctrl_del_xenstore(gc, domid, usbctrl);
-out:
-    device_usbctrl_add_done(egc, aodev, rc);
-}
-
-static void device_usbctrl_add_timeout(libxl__egc *egc, libxl__ev_time *ev,
-                                       const struct timeval *requested_abs,
-                                       int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
-    if (rc == ERROR_TIMEDOUT)
-        LOGD(ERROR, aodev->dev->domid, "Adding usbctrl to QEMU timed out");
-    device_usbctrl_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbctrl_add_qmp_cb(libxl__egc *egc,
-                                      libxl__ev_qmp *qmp,
-                                      const libxl__json_object *r,
-                                      int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
-    libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
-    if (rc)
-        libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
-
-    device_usbctrl_add_done(egc, aodev, rc);
-}
-
-static void device_usbctrl_add_done(libxl__egc *egc,
-                                    libxl__ao_device *aodev,
-                                    int rc)
-{
-    EGC_GC;
-    libxl__ev_qmp_dispose(gc, &aodev->qmp);
-    libxl__ev_time_deregister(gc, &aodev->timeout);
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-LIBXL_DEFINE_DEVICE_ADD(usbctrl)
-static LIBXL_DEFINE_DEVICES_ADD(usbctrl)
-LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(usbctrl)
-
-static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t domid,
-                                                 libxl_devid usbctrl,
-                                                 libxl_device_usbdev **usbdevs,
-                                                 int *num);
-
-static void libxl__device_usbdev_remove(libxl__egc *egc,
-    uint32_t domid, libxl_device_usbdev *usbdev, libxl__ao_device *aodev);
-
-static void device_usbctrl_usbdevs_removed(libxl__egc *,
-    libxl__multidev *, int rc);
-static void device_usbctrl_remove_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *resp, int rc);
-static void device_usbctrl_remove_done(libxl__egc *egc,
-    libxl__ao_device *, int rc);
-
-typedef struct {
-    libxl__multidev multidev;
-    libxl__ao_device *aodev;
-} usbctrl_remove_state;
-
-/* AO function to remove a usb controller.
- *
- * Generally, it does:
- * 1) check if the usb controller exists or not
- * 2) remove all usb devices under controller
- * 3) remove usb controller information from xenstore
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->dev, aodev->callback, ...
- */
-void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
-                                           libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    libxl_device_usbdev *usbdevs = NULL;
-    int num_usbdev = 0;
-    int i, rc;
-    uint32_t domid = aodev->dev->domid;
-    int usbctrl_devid = aodev->dev->devid;
-    libxl_device_usbctrl *usbctrl;
-    usbctrl_remove_state *ucrs;
-
-    GCNEW(ucrs);
-    ucrs->aodev = aodev;
-    ucrs->multidev.callback = device_usbctrl_usbdevs_removed;
-    libxl__multidev_begin(ao, &ucrs->multidev);
-
-    GCNEW(usbctrl);
-    libxl_device_usbctrl_init(usbctrl);
-    rc = libxl_devid_to_device_usbctrl(CTX, domid, usbctrl_devid,
-                                       usbctrl);
-    if (rc) goto out;
-
-    /* Store *usbctrl to be used by callbacks */
-    aodev->device_config = usbctrl;
-    aodev->device_type = &libxl__usbctrl_devtype;
-
-    /* Remove usb devices first */
-    rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid,
-                                               &usbdevs, &num_usbdev);
-    if (rc) goto out;
-
-    for (i = 0; i < num_usbdev; i++) {
-        libxl__ao_device *usbdev_aodev =
-            libxl__multidev_prepare(&ucrs->multidev);
-        usbdev_aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-        libxl__device_usbdev_remove(egc, domid, &usbdevs[i], usbdev_aodev);
-    }
-
-out:
-    libxl__multidev_prepared(egc, &ucrs->multidev, rc); /* must be last */
-}
-
-static void device_usbctrl_usbdevs_removed(libxl__egc *egc,
-                                           libxl__multidev *multidev,
-                                           int rc)
-{
-    usbctrl_remove_state *ucrs =
-        CONTAINER_OF(multidev, *ucrs, multidev);
-    libxl__ao_device *aodev = ucrs->aodev;
-    STATE_AO_GC(aodev->ao);
-    libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
-    if (rc) goto out;
-
-    /* Remove usbctrl */
-    if (usbctrl->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
-        libxl__ev_qmp *const qmp = &aodev->qmp;
-
-        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
-                                         device_usbctrl_remove_timeout,
-                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
-        if (rc) goto out;
-
-        qmp->ao = ao;
-        qmp->domid = aodev->dev->domid;
-        qmp->callback = device_usbctrl_remove_qmp_cb;
-        qmp->payload_fd = -1;
-        rc = libxl__device_usbctrl_del_hvm(egc, qmp, aodev->dev->devid);
-        if (rc) goto out;
-        return;
-    }
-
-    libxl_device_usbctrl_dispose(usbctrl);
-
-    /* Remove usbctrl */
-    libxl__initiate_device_generic_remove(egc, aodev); /* must be last */
-    return;
-out:
-    device_usbctrl_remove_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbctrl_remove_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
-    if (rc == ERROR_TIMEDOUT)
-        LOGD(ERROR, aodev->dev->domid,
-             "Removing usbctrl from QEMU timed out");
-    device_usbctrl_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbctrl_remove_qmp_cb(libxl__egc *egc,
-                                         libxl__ev_qmp *qmp,
-                                         const libxl__json_object *resp,
-                                         int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
-    libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
-    if (!rc)
-        libxl__device_usbctrl_del_xenstore(gc, aodev->dev->domid, usbctrl);
-
-    device_usbctrl_remove_done(egc, aodev, rc);
-}
-
-static void device_usbctrl_remove_done(libxl__egc *egc,
-                                       libxl__ao_device *aodev,
-                                       int rc)
-{
-    EGC_GC;
-    libxl_device_usbctrl *const usbctrl = aodev->device_config;
-
-    libxl_device_usbctrl_dispose(usbctrl);
-    libxl__ev_qmp_dispose(gc, &aodev->qmp);
-    libxl__ev_time_deregister(gc, &aodev->timeout);
-
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-static int libxl__usbctrl_from_xenstore(libxl__gc *gc,
-                                        const char *libxl_path,
-                                        libxl_devid devid,
-                                        libxl_device_usbctrl *usbctrl_r)
-{
-    int rc;
-    const char *tmp;
-    const char *be_path;
-
-#define READ_SUBPATH(path, subpath) ({                                  \
-        rc = libxl__xs_read_checked(gc, XBT_NULL,                      \
-                                     GCSPRINTF("%s/" subpath, path),    \
-                                     &tmp);                             \
-        if (rc) goto out;                                              \
-        (char *)tmp;                                                    \
-    })
-
-#define READ_SUBPATH_INT(path, subpath) ({                              \
-        rc = libxl__xs_read_checked(gc, XBT_NULL,                      \
-                                     GCSPRINTF("%s/" subpath, path),    \
-                                     &tmp);                             \
-        if (rc) goto out;                                              \
-        tmp ? atoi(tmp) : -1;                                           \
-    })
-
-    usbctrl_r->devid = devid;
-    libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
-                                   &usbctrl_r->type);
-    if (usbctrl_r->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
-        be_path = libxl_path;
-        rc = libxl__get_domid(gc, &usbctrl_r->backend_domid);
-    } else {
-        be_path = READ_SUBPATH(libxl_path, "backend");
-        if (!be_path) goto out;
-        rc = libxl__backendpath_parse_domid(gc, be_path,
-                                             &usbctrl_r->backend_domid);
-    }
-    if (rc) goto out;
-    usbctrl_r->version = READ_SUBPATH_INT(be_path, "usb-ver");
-    usbctrl_r->ports = READ_SUBPATH_INT(be_path, "num-ports");
-
-#undef READ_SUBPATH
-#undef READ_SUBPATH_INT
-out:
-    if (rc)
-        libxl_device_usbctrl_dispose(usbctrl_r);
-    return rc;
-}
-
-int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
-                                 const libxl_device_usbctrl *usbctrl,
-                                 libxl_usbctrlinfo *usbctrlinfo)
-{
-    GC_INIT(ctx);
-    const char *fe_path, *be_path, *tmp;
-    const char *libxl_path;
-    int rc;
-
-    usbctrlinfo->devid = usbctrl->devid;
-
-#define READ_SUBPATH(path, subpath) ({                                  \
-        rc = libxl__xs_read_mandatory(gc, XBT_NULL,                     \
-                                      GCSPRINTF("%s/" subpath, path),   \
-                                      &tmp);                            \
-        if (rc) goto out;                                               \
-        (char *)tmp;                                                    \
-    })
-
-#define READ_SUBPATH_INT(path, subpath) ({                              \
-        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \
-                                    GCSPRINTF("%s/" subpath, path),     \
-                                    &tmp);                              \
-        if (rc) goto out;                                               \
-        tmp ? atoi(tmp) : -1;                                           \
-    })
-
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl->devid,
-                                                 LIBXL__DEVICE_KIND_VUSB);
-    libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"),
-                                   &usbctrlinfo->type);
-
-    if (usbctrlinfo->type != LIBXL_USBCTRL_TYPE_DEVICEMODEL) {
-        fe_path = libxl__domain_device_frontend_path(gc, domid, usbctrl->devid,
-                                                     LIBXL__DEVICE_KIND_VUSB);
-        be_path = READ_SUBPATH(libxl_path, "backend");
-        usbctrlinfo->backend = libxl__strdup(NOGC, be_path);
-        rc = libxl__backendpath_parse_domid(gc, be_path,
-                                            &usbctrlinfo->backend_id);
-        if (rc) goto out;
-        usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state");
-        usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel");
-        usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
-        usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
-        usbctrlinfo->frontend = libxl__strdup(NOGC, fe_path);
-        usbctrlinfo->frontend_id = domid;
-        usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports");
-        usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver");
-    } else {
-        usbctrlinfo->ports = READ_SUBPATH_INT(libxl_path, "num-ports");
-        usbctrlinfo->version = READ_SUBPATH_INT(libxl_path, "usb-ver");
-        rc = libxl__get_domid(gc, &usbctrlinfo->backend_id);
-        if (rc) goto out;
-    }
-
-#undef READ_SUBPATH
-#undef READ_SUBPATH_INT
-
-    rc = 0;
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-
-static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr)
-{
-    DIR *dir;
-    char *busid = NULL;
-    struct dirent *de;
-
-    /* invalid hostbus or hostaddr */
-    if (bus < 1 || addr < 1)
-        return NULL;
-
-    dir = opendir(SYSFS_USB_DEV);
-    if (!dir) {
-        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
-        return NULL;
-    }
-
-    for (;;) {
-        char *filename;
-        void *buf;
-        int busnum = -1;
-        int devnum = -1;
-
-        errno = 0;
-        de = readdir(dir);
-        if (!de && errno) {
-            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
-            break;
-        }
-        if (!de)
-            break;
-
-        if (!strcmp(de->d_name, ".") ||
-            !strcmp(de->d_name, ".."))
-            continue;
-
-        filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", de->d_name);
-        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
-            devnum = atoi(buf);
-
-        filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", de->d_name);
-        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
-            busnum = atoi(buf);
-
-        if (bus == busnum && addr == devnum) {
-            busid = libxl__strdup(gc, de->d_name);
-            break;
-        }
-    }
-
-    closedir(dir);
-    return busid;
-}
-
-static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid,
-                                     uint8_t *bus, uint8_t *addr)
-{
-    char *filename;
-    void *buf;
-
-    filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", busid);
-    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
-        *bus = atoi(buf);
-    else
-        return ERROR_FAIL;
-
-    filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", busid);
-    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
-        *addr = atoi(buf);
-    else
-        return ERROR_FAIL;
-
-    return 0;
-}
-
-static int get_assigned_devices(libxl__gc *gc,
-                                libxl_device_usbdev **list, int *num)
-{
-    char **domlist;
-    unsigned int ndom = 0;
-    int i, j, k;
-    int rc;
-
-    *list = NULL;
-    *num = 0;
-
-    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom);
-    for (i = 0; i < ndom; i++) {
-        char *libxl_vusbs_path;
-        char **usbctrls;
-        unsigned int nc = 0;
-        uint32_t domid = atoi(domlist[i]);
-
-        libxl_vusbs_path = GCSPRINTF("%s/device/%s",
-                                     libxl__xs_libxl_path(gc, domid),
-                                     libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
-        usbctrls = libxl__xs_directory(gc, XBT_NULL,
-                                       libxl_vusbs_path, &nc);
-
-        for (j = 0; j < nc; j++) {
-            libxl_device_usbdev *tmp = NULL;
-            int nd = 0;
-
-            rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
-                                                       atoi(usbctrls[j]),
-                                                       &tmp, &nd);
-            if (rc) goto out;
-
-            if (!nd) continue;
-
-            GCREALLOC_ARRAY(*list, *num + nd);
-            for (k = 0; k < nd; k++) {
-                libxl_device_usbdev_copy(CTX, *list + *num, tmp + k);
-                (*num)++;
-            }
-        }
-    }
-
-    return 0;
-
-out:
-    LOG(ERROR, "fail to get assigned devices");
-    return rc;
-}
-
-static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num,
-                               libxl_device_usbdev *usbdev)
-{
-    int i;
-
-    for (i = 0; i < num; i++) {
-        if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus &&
-            usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr)
-            return true;
-    }
-
-    return false;
-}
-
-/* check if USB device type is assignable */
-static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev *usbdev)
-{
-    int classcode;
-    char *filename;
-    void *buf = NULL;
-    char *busid = NULL;
-
-    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
-                                    usbdev->u.hostdev.hostaddr);
-    if (!busid) return false;
-
-    filename = GCSPRINTF(SYSFS_USB_DEV "/%s/bDeviceClass", busid);
-    if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
-        return false;
-
-    classcode = atoi(buf);
-    return classcode != USBHUB_CLASS_CODE;
-}
-
-/* get usb devices under certain usb controller */
-static int
-libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc,
-                                      uint32_t domid,
-                                      libxl_devid usbctrl,
-                                      libxl_device_usbdev **usbdevs,
-                                      int *num)
-{
-    const char *libxl_path, *be_path, *num_devs;
-    int n, i, rc;
-
-    *usbdevs = NULL;
-    *num = 0;
-
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, usbctrl,
-                                                 LIBXL__DEVICE_KIND_VUSB);
-
-    be_path = vusb_be_from_xs_libxl(gc, libxl_path);
-    if (!be_path) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/num-ports", be_path),
-                                &num_devs);
-    if (rc) goto out;
-
-    n = num_devs ? atoi(num_devs) : 0;
-
-    for (i = 0; i < n; i++) {
-        const char *busid;
-        libxl_device_usbdev *usbdev;
-
-        rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                    GCSPRINTF("%s/port/%d", be_path, i + 1),
-                                    &busid);
-        if (rc) goto out;
-
-        if (busid && strcmp(busid, "")) {
-            GCREALLOC_ARRAY(*usbdevs, *num + 1);
-            usbdev = *usbdevs + *num;
-            (*num)++;
-            libxl_device_usbdev_init(usbdev);
-            usbdev->ctrl = usbctrl;
-            usbdev->port = i + 1;
-            usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
-            rc = usbdev_busaddr_from_busid(gc, busid,
-                                           &usbdev->u.hostdev.hostbus,
-                                           &usbdev->u.hostdev.hostaddr);
-            if (rc) goto out;
-        }
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-/* get all usb devices of the domain */
-libxl_device_usbdev *
-libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num)
-{
-    GC_INIT(ctx);
-    libxl_device_usbdev *usbdevs = NULL;
-    const char *libxl_vusbs_path;
-    char **usbctrls;
-    unsigned int nc = 0;
-    int i, j;
-
-    *num = 0;
-
-    libxl_vusbs_path = GCSPRINTF("%s/device/%s",
-                                 libxl__xs_libxl_path(gc, domid),
-                                 libxl__device_kind_to_string(
-                                 LIBXL__DEVICE_KIND_VUSB));
-    usbctrls = libxl__xs_directory(gc, XBT_NULL, libxl_vusbs_path, &nc);
-
-    for (i = 0; i < nc; i++) {
-        int rc, nd = 0;
-        libxl_device_usbdev *tmp = NULL;
-
-        rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
-                                                  atoi(usbctrls[i]),
-                                                  &tmp, &nd);
-        if (rc || !nd) continue;
-
-        usbdevs = libxl__realloc(NOGC, usbdevs,
-                                 sizeof(*usbdevs) * (*num + nd));
-        for (j = 0; j < nd; j++) {
-            libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j);
-            (*num)++;
-        }
-    }
-
-    GC_FREE;
-    return usbdevs;
-}
-
-static char *vusb_get_port_path(libxl__gc *gc, uint32_t domid,
-                                libxl_usbctrl_type type, int ctrl, int port)
-{
-    char *path;
-
-    if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL)
-        path = GCSPRINTF("%s/device/%s", libxl__xs_libxl_path(gc, domid),
-                         libxl__device_kind_to_string(LIBXL__DEVICE_KIND_VUSB));
-    else
-        path = GCSPRINTF("%s/backend/%s/%d",
-                         libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
-                         pvusb_get_device_type(type), domid);
-
-    return GCSPRINTF("%s/%d/port/%d", path, ctrl, port);
-}
-
-/* find first unused controller:port and give that to usb device */
-static int
-libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid,
-                                         libxl_device_usbdev *usbdev)
-{
-    libxl_device_usbctrl *usbctrls = NULL;
-    int numctrl = 0;
-    int i, j, rc;
-
-    usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl);
-    if (!numctrl || !usbctrls) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    for (i = 0; i < numctrl; i++) {
-        for (j = 0; j < usbctrls[i].ports; j++) {
-            const char *path, *tmp;
-
-            path = vusb_get_port_path(gc, domid, usbctrls[i].type,
-                                      usbctrls[i].devid, j + 1);
-            rc = libxl__xs_read_checked(gc, XBT_NULL, path, &tmp);
-            if (rc) goto out;
-
-            if (tmp && !strcmp(tmp, "")) {
-                usbdev->ctrl = usbctrls[i].devid;
-                usbdev->port = j + 1;
-                rc = 0;
-                goto out;
-            }
-        }
-    }
-
-    /* no available controller:port */
-    rc = ERROR_FAIL;
-
-out:
-    libxl_device_usbctrl_list_free(usbctrls, numctrl);
-    return rc;
-}
-
-/* Fill in usb information with default value.
- *
- * Generally, it does:
- * 1) if "controller" is not specified:
- *    - if "port" is not specified, try to find an available controller:port,
- *      if found, use that; otherwise, create a new controller, use this
- *      controller and its first port
- *    - if "port" is specified, report error.
- * 2) if "controller" is specified, but port is not specified:
- *    try to find an available port under this controller, if found, use
- *    that, otherwise, report error.
- * 3) if both "controller" and "port" are specified:
- *    check the controller:port is available, if not, report error.
- */
-static int libxl__device_usbdev_setdefault(libxl__gc *gc,
-                                           uint32_t domid,
-                                           libxl_device_usbdev *usbdev,
-                                           bool update_json)
-{
-    int rc;
-
-    if (!usbdev->type)
-        usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
-
-    if (usbdev->ctrl == -1) {
-        if (usbdev->port) {
-            LOGD(ERROR, domid,
-                 "USB controller must be specified if you specify port");
-            return ERROR_INVAL;
-        }
-
-        rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev);
-        /* If no existing controller to host this usb device, add a new one */
-        if (rc) {
-            libxl_device_usbctrl *usbctrl;
-
-            GCNEW(usbctrl);
-            libxl_device_usbctrl_init(usbctrl);
-            rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl,
-                                                  update_json);
-            if (rc < 0) goto out;
-
-            rc = libxl__device_usbctrl_update_devid(gc, domid, usbctrl);
-            if (rc) goto out;
-
-            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
-                                                    update_json);
-            if (rc) goto out;
-
-            usbdev->ctrl = usbctrl->devid;
-            usbdev->port = 1;
-        }
-    } else {
-        /* A controller was specified; look it up */
-        const char *libxl_path, *be_path, *tmp;
-
-        libxl_path = libxl__domain_device_libxl_path(gc, domid, usbdev->ctrl,
-                                                        LIBXL__DEVICE_KIND_VUSB);
-
-        be_path = vusb_be_from_xs_libxl(gc, libxl_path);
-        if (!be_path) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        if (usbdev->port) {
-            /* A specific port was requested; see if it's available */
-            rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                        GCSPRINTF("%s/port/%d",
-                                                  be_path, usbdev->port),
-                                        &tmp);
-            if (rc) goto out;
-
-            if (tmp && strcmp(tmp, "")) {
-                LOGD(ERROR, domid, "The controller port isn't available");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-        } else {
-            /* No port was requested. Choose free port. */
-            int i, ports;
-
-            rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                        GCSPRINTF("%s/num-ports", be_path), &tmp);
-            if (rc) goto out;
-
-            ports = tmp ? atoi(tmp) : 0;
-
-            for (i = 0; i < ports; i++) {
-                rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                            GCSPRINTF("%s/port/%d", be_path, i + 1),
-                                            &tmp);
-                if (rc) goto out;
-
-                if (tmp && !strcmp(tmp, "")) {
-                    usbdev->port = i + 1;
-                    break;
-                }
-            }
-
-            if (!usbdev->port) {
-                LOGD(ERROR, domid, "No available port under specified controller");
-                rc = ERROR_FAIL;
-                goto out;
-            }
-        }
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-/* Add usb information to xenstore
- *
- * Adding a usb device won't create new 'qusb'/'vusb' device, but only write
- * the device busid to the controller:port in xenstore.
- */
-static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid,
-                                             libxl_device_usbdev *usbdev,
-                                             libxl_usbctrl_type type,
-                                             bool update_json)
-{
-    char *be_path, *busid;
-    int rc;
-    xs_transaction_t t = XBT_NULL;
-    libxl_domain_config d_config;
-    libxl_device_usbdev usbdev_saved;
-    libxl__flock *lock = NULL;
-
-    libxl_domain_config_init(&d_config);
-    libxl_device_usbdev_init(&usbdev_saved);
-    libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev);
-
-    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
-                                    usbdev->u.hostdev.hostaddr);
-    if (!busid) {
-        LOGD(DEBUG, domid, "Fail to get busid of usb device");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (update_json) {
-        lock = libxl__lock_domain_userdata(gc, domid);
-        if (!lock) {
-            rc = ERROR_LOCK_FAIL;
-            goto out;
-        }
-
-        rc = libxl__get_domain_configuration(gc, domid, &d_config);
-        if (rc) goto out;
-
-        device_add_domain_config(gc, &d_config, &libxl__usbdev_devtype,
-                                         &usbdev_saved);
-
-        rc = libxl__dm_check_start(gc, &d_config, domid);
-        if (rc) goto out;
-    }
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        if (update_json) {
-            rc = libxl__set_domain_configuration(gc, domid, &d_config);
-            if (rc) goto out;
-        }
-
-        be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl,
-                                     usbdev->port);
-
-        LOGD(DEBUG, domid, "Adding usb device %s to xenstore: controller %d, port %d",
-            busid, usbdev->ctrl, usbdev->port);
-
-        rc = libxl__xs_write_checked(gc, t, be_path, busid);
-        if (rc) goto out;
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc < 0) goto out;
-    }
-
-    rc = 0;
-
-out:
-    if (lock) libxl__unlock_file(lock);
-    libxl_device_usbdev_dispose(&usbdev_saved);
-    libxl_domain_config_dispose(&d_config);
-    return rc;
-}
-
-static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t domid,
-                                                libxl_device_usbdev *usbdev,
-                                                libxl_usbctrl_type type)
-{
-    char *be_path;
-
-    be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl, usbdev->port);
-
-    LOGD(DEBUG, domid, "Removing usb device from xenstore: controller %d, port %d",
-         usbdev->ctrl, usbdev->port);
-
-    return libxl__xs_write_checked(gc, XBT_NULL, be_path, "");
-}
-
-static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid,
-                                        libxl_device_usbdev *usbdev,
-                                        libxl_usbctrl_type type)
-{
-    return libxl__xs_read(gc, XBT_NULL,
-                          vusb_get_port_path(gc, domid, type, usbdev->ctrl,
-                                             usbdev->port));
-}
-
-/* get original driver path of usb interface, stored in @drvpath */
-static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char **drvpath)
-{
-    char *spath, *dp = NULL;
-
-    spath = GCSPRINTF(SYSFS_USB_DEV "/%s/driver", intf);
-
-    /* Find the canonical path to the driver. */
-    dp = libxl__zalloc(gc, PATH_MAX);
-    dp = realpath(spath, dp);
-    if (!dp && errno != ENOENT) {
-        LOGE(ERROR, "get realpath failed: '%s'", spath);
-        return ERROR_FAIL;
-    }
-
-    *drvpath = dp;
-
-    return 0;
-}
-
-static int unbind_usbintf(libxl__gc *gc, const char *intf)
-{
-    char *path;
-    int fd = -1;
-    int rc;
-
-    path = GCSPRINTF(SYSFS_USB_DEV "/%s/driver/unbind", intf);
-
-    fd = open(path, O_WRONLY);
-    if (fd < 0) {
-        LOGE(ERROR, "open file failed: '%s'", path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (libxl_write_exactly(CTX, fd, intf, strlen(intf), path, intf)) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    if (fd >= 0) close(fd);
-    return rc;
-}
-
-static int bind_usbintf(libxl__gc *gc, const char *intf, const char *drvpath)
-{
-    char *bind_path, *intf_path;
-    struct stat st;
-    int fd = -1;
-    int rc, r;
-
-    intf_path = GCSPRINTF("%s/%s", drvpath, intf);
-
-    /* check through lstat, if intf already exists under drvpath,
-     * it's already bound, return directly; if it doesn't exist,
-     * continue to do bind work; otherwise, return error.
-     */
-    r = lstat(intf_path, &st);
-    if (r == 0)
-        return 0;
-    if (r < 0 && errno != ENOENT)
-        return ERROR_FAIL;
-
-    bind_path = GCSPRINTF("%s/bind", drvpath);
-
-    fd = open(bind_path, O_WRONLY);
-    if (fd < 0) {
-        LOGE(ERROR, "open file failed: '%s'", bind_path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    if (libxl_write_exactly(CTX, fd, intf, strlen(intf), bind_path, intf)) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    if (fd >= 0) close(fd);
-    return rc;
-}
-
-/* Is usb interface bound to usbback? */
-static int usbintf_is_assigned(libxl__gc *gc, char *intf)
-{
-    char *spath;
-    int r;
-    struct stat st;
-
-    spath = GCSPRINTF(SYSFS_USBBACK_DRIVER "/%s", intf);
-    r = lstat(spath, &st);
-
-    if (r == 0)
-        return 1;
-    if (r < 0 && errno == ENOENT)
-        return 0;
-    LOGE(ERROR, "Accessing %s", spath);
-    return -1;
-}
-
-static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid,
-                                     char ***intfs, int *num)
-{
-    DIR *dir;
-    char *buf;
-    struct dirent *de;
-    int rc;
-
-    *intfs = NULL;
-    *num = 0;
-
-    buf = GCSPRINTF("%s:", busid);
-
-    dir = opendir(SYSFS_USB_DEV);
-    if (!dir) {
-        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
-        return ERROR_FAIL;
-    }
-
-    for (;;) {
-        errno = 0;
-        de = readdir(dir);
-
-        if (!de && errno) {
-            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        if (!de)
-            break;
-
-        if (!strcmp(de->d_name, ".") ||
-            !strcmp(de->d_name, ".."))
-            continue;
-
-        if (!strncmp(de->d_name, buf, strlen(buf))) {
-            GCREALLOC_ARRAY(*intfs, *num + 1);
-            (*intfs)[*num] = libxl__strdup(gc, de->d_name);
-            (*num)++;
-        }
-    }
-
-    rc = 0;
-
-out:
-    closedir(dir);
-    return rc;
-}
-
-/* Encode usb interface so that it could be written to xenstore as a key.
- *
- * Since xenstore key cannot include '.' or ':', we'll change '.' to '_',
- * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1.
- * This will be used to save original driver of USB device to xenstore.
- */
-static char *usb_interface_xenstore_encode(libxl__gc *gc, const char *busid)
-{
-    char *str = libxl__strdup(gc, busid);
-    int i, len = strlen(str);
-
-    for (i = 0; i < len; i++) {
-        if (str[i] == '.') str[i] = '_';
-        if (str[i] == ':') str[i] = '@';
-    }
-    return str;
-}
-
-/* Unbind USB device from "usbback" driver.
- *
- * If there are many interfaces under USB device, check each interface,
- * unbind from "usbback" driver.
- */
-static int usbback_dev_unassign(libxl__gc *gc, const char *busid)
-{
-    char **intfs = NULL;
-    int i, num = 0;
-    int rc;
-
-    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
-    if (rc) goto out;
-
-    for (i = 0; i < num; i++) {
-        char *intf = intfs[i];
-
-        /* check if the USB interface is already bound to "usbback" */
-        if (usbintf_is_assigned(gc, intf) > 0) {
-            /* unbind interface from usbback driver */
-            rc = unbind_usbintf(gc, intf);
-            if (rc) {
-                LOGE(ERROR, "Couldn't unbind %s from usbback", intf);
-                goto out;
-            }
-        }
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-/* rebind USB device to original driver.
- *
- * If there are many interfaces under USB device, for reach interface,
- * read driver_path from xenstore (if there is) and rebind to its
- * original driver, then remove driver_path information from xenstore.
- */
-static int usbdev_rebind(libxl__gc *gc, const char *busid)
-{
-    char **intfs = NULL;
-    char *usbdev_encode = NULL;
-    char *path = NULL;
-    int i, num = 0;
-    int rc;
-
-    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
-    if (rc) goto out;
-
-    usbdev_encode = usb_interface_xenstore_encode(gc, busid);
-
-    for (i = 0; i < num; i++) {
-        char *intf = intfs[i];
-        char *usbintf_encode = NULL;
-        const char *drvpath;
-
-        /* rebind USB interface to its originial driver */
-        usbintf_encode = usb_interface_xenstore_encode(gc, intf);
-        path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
-                         usbdev_encode, usbintf_encode);
-        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath);
-        if (rc) goto out;
-
-        if (drvpath) {
-            rc = bind_usbintf(gc, intf, drvpath);
-            if (rc) {
-                LOGE(ERROR, "Couldn't rebind %s to %s", intf, drvpath);
-                goto out;
-            }
-        }
-    }
-
-out:
-    path = GCSPRINTF(USBBACK_INFO_PATH "/%s", usbdev_encode);
-    libxl__xs_rm_checked(gc, XBT_NULL, path);
-    return rc;
-}
-
-
-/* Bind USB device to "usbback" driver.
- *
- * If there are many interfaces under USB device, check each interface,
- * unbind from original driver and bind to "usbback" driver.
- */
-static int usbback_dev_assign(libxl__gc *gc, const char *busid)
-{
-    char **intfs = NULL;
-    int num = 0, i;
-    int rc;
-    char *usbdev_encode = NULL;
-
-    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
-    if (rc) return rc;
-
-    usbdev_encode = usb_interface_xenstore_encode(gc, busid);
-
-    for (i = 0; i < num; i++) {
-        char *intf = intfs[i];
-        char *drvpath = NULL;
-
-        /* already assigned to usbback */
-        if (usbintf_is_assigned(gc, intf) > 0)
-            continue;
-
-        rc = usbintf_get_drvpath(gc, intf, &drvpath);
-        if (rc) goto out;
-
-        if (drvpath) {
-            /* write driver path to xenstore for later rebinding */
-            char *usbintf_encode = NULL;
-            char *path;
-
-            usbintf_encode = usb_interface_xenstore_encode(gc, intf);
-            path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path",
-                             usbdev_encode, usbintf_encode);
-            rc = libxl__xs_write_checked(gc, XBT_NULL, path, drvpath);
-            if (rc) goto out;
-
-            /* unbind interface from original driver */
-            rc = unbind_usbintf(gc, intf);
-            if (rc) goto out;
-        }
-
-        /* bind interface to usbback */
-        rc = bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER);
-        if (rc) {
-            LOG(ERROR, "Couldn't bind %s to %s", intf, SYSFS_USBBACK_DRIVER);
-            goto out;
-        }
-    }
-
-    return 0;
-
-out:
-    /* some interfaces might be bound to usbback, unbind it and
-     * rebind it to its original driver
-     */
-    usbback_dev_unassign(gc, busid);
-    usbdev_rebind(gc, busid);
-    return rc;
-}
-
-static void device_usbdev_add_qmp_cb(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
-static void device_usbdev_add_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbdev_add_done(libxl__egc *egc,
-    libxl__ao_device *aodev, int rc);
-
-/* AO operation to add a usb device.
- *
- * Generally, it does:
- * 1) check if the usb device type is assignable
- * 2) check if the usb device is already assigned to a domain
- * 3) add 'busid' of the usb device to xenstore contoller/port/.
- *    (PVUSB driver watches the xenstore changes and will detect that.)
- * 4) unbind usb device from original driver and bind to usbback.
- *    If usb device has many interfaces, then:
- *    - unbind each interface from its original driver and bind to usbback.
- *    - store the original driver to xenstore for later rebinding when
- *      detaching the device.
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->callback, aodev->update_json, ...
- */
-static void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid,
-                                     libxl_device_usbdev *usbdev,
-                                     libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    int rc;
-    libxl_device_usbdev *assigned;
-    int num_assigned;
-    libxl_device_usbctrl usbctrl;
-    char *busid;
-    bool has_callback = false;
-
-    libxl_device_usbctrl_init(&usbctrl);
-
-    /* Store *usbdev to be used by callbacks */
-    aodev->device_config = usbdev;
-    aodev->device_type = &libxl__usbdev_devtype;
-
-    /* Currently only support adding USB device from Dom0 backend.
-     * So, if USB controller is specified, check its backend domain,
-     * if it's not Dom0, report error.
-     */
-    if (usbdev->ctrl != -1) {
-        rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl,
-                                           &usbctrl);
-        if (rc) goto out;
-
-        if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
-            LOGD(ERROR, domid,
-                 "Don't support adding USB device from non-Dom0 backend");
-            rc = ERROR_INVAL;
-            goto out;
-        }
-        libxl_device_usbctrl_dispose(&usbctrl);
-    }
-
-    /* check usb device is assignable type */
-    if (!is_usbdev_assignable(gc, usbdev)) {
-        LOGD(ERROR, domid, "USB device is not assignable.");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    /* check usb device is already assigned */
-    rc = get_assigned_devices(gc, &assigned, &num_assigned);
-    if (rc) {
-        LOGD(ERROR, domid, "cannot determine if device is assigned,"
-                           " refusing to continue");
-        goto out;
-    }
-
-    if (is_usbdev_in_array(assigned, num_assigned, usbdev)) {
-        LOGD(ERROR, domid, "USB device already attached to a domain");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    /* fill default values, e.g, if usbdev->ctrl and usbdev->port
-     * not specified, choose available controller:port and fill in. */
-    rc = libxl__device_usbdev_setdefault(gc, domid, usbdev,
-                                         aodev->update_json);
-    if (rc) goto out;
-
-    rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
-    if (rc) goto out;
-
-    /* do actual adding usb device operation */
-    switch (usbctrl.type) {
-    case LIBXL_USBCTRL_TYPE_PV:
-        busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
-                                        usbdev->u.hostdev.hostaddr);
-        if (!busid) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
-                                               LIBXL_USBCTRL_TYPE_PV,
-                                               aodev->update_json);
-        if (rc) goto out;
-
-        rc = usbback_dev_assign(gc, busid);
-        if (rc) {
-            libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
-                                                 LIBXL_USBCTRL_TYPE_PV);
-            goto out;
-        }
-        break;
-    case LIBXL_USBCTRL_TYPE_QUSB:
-        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
-                                               LIBXL_USBCTRL_TYPE_QUSB,
-                                               aodev->update_json);
-        if (rc) goto out;
-
-        break;
-    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
-        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
-                                               LIBXL_USBCTRL_TYPE_DEVICEMODEL,
-                                               aodev->update_json);
-        if (rc) goto out;
-
-        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
-                                         device_usbdev_add_timeout,
-                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
-        if (rc) goto out;
-
-        aodev->qmp.ao = ao;
-        aodev->qmp.domid = domid;
-        aodev->qmp.callback = device_usbdev_add_qmp_cb;
-        aodev->qmp.payload_fd = -1;
-        rc = libxl__device_usbdev_add_hvm(egc, &aodev->qmp, usbdev);
-        if (rc) {
-            libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
-                                             LIBXL_USBCTRL_TYPE_DEVICEMODEL);
-            goto out;
-        }
-        has_callback = true;
-        break;
-    default:
-        LOGD(ERROR, domid, "Unsupported usb controller type");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    libxl_device_usbctrl_dispose(&usbctrl);
-    /* Only call _done if no callback have been setup */
-    if (!has_callback)
-        device_usbdev_add_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_add_timeout(libxl__egc *egc,
-                                      libxl__ev_time *ev,
-                                      const struct timeval *requested_abs,
-                                      int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
-    if (rc == ERROR_TIMEDOUT)
-        LOGD(ERROR, aodev->qmp.domid,
-             "Adding usbdev to QEMU timed out");
-    device_usbdev_add_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbdev_add_qmp_cb(libxl__egc *egc,
-                                     libxl__ev_qmp *qmp,
-                                     const libxl__json_object *r,
-                                     int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
-    libxl_device_usbdev *const usbdev = aodev->device_config;
-
-    if (rc)
-        libxl__device_usbdev_remove_xenstore(gc, qmp->domid,
-            usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL);
-    device_usbdev_add_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_add_done(libxl__egc *egc,
-                                   libxl__ao_device *aodev,
-                                   int rc)
-{
-    EGC_GC;
-
-    libxl__ev_time_deregister(gc, &aodev->timeout);
-    libxl__ev_qmp_dispose(gc, &aodev->qmp);
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-LIBXL_DEFINE_DEVICE_ADD(usbdev)
-static LIBXL_DEFINE_DEVICES_ADD(usbdev)
-
-static void device_usbdev_remove_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
-static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
-    libxl__ev_qmp *qmp, const libxl__json_object *r, int rc);
-static void device_usbdev_remove_done(libxl__egc *egc,
-    libxl__ao_device *aodev, int rc);
-
-/* Operation to remove usb device.
- *
- * Generally, it does:
- * 1) check if the usb device is assigned to the domain
- * 2) remove the usb device from xenstore controller/port.
- * 3) unbind usb device from usbback and rebind to its original driver.
- *    If usb device has many interfaces, do it to each interface.
- *
- * Before calling this function, aodev should be properly filled:
- * aodev->ao, aodev->callback, ...
- */
-static void libxl__device_usbdev_remove(libxl__egc *egc, uint32_t domid,
-                                        libxl_device_usbdev *usbdev,
-                                        libxl__ao_device *aodev)
-{
-    STATE_AO_GC(aodev->ao);
-    int rc;
-    char *busid;
-    libxl_device_usbctrl usbctrl;
-    bool has_callback = false;
-
-    /* Store *usbdev to be used by callbacks */
-    aodev->device_config = usbdev;
-    aodev->device_type = &libxl__usbdev_devtype;
-
-    libxl_device_usbctrl_init(&usbctrl);
-
-    if (usbdev->ctrl < 0 || usbdev->port < 1) {
-        LOGD(ERROR, domid, "Invalid USB device");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl_devid_to_device_usbctrl(CTX, domid, usbdev->ctrl, &usbctrl);
-    if (rc) goto out;
-
-    if (usbctrl.backend_domid != LIBXL_TOOLSTACK_DOMID) {
-        LOGD(ERROR, domid,
-             "Don't support removing USB device from non-Dom0 backend");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    /* do actual removing usb device operation */
-    switch (usbctrl.type) {
-    case LIBXL_USBCTRL_TYPE_PV:
-        busid = usbdev_busid_from_ctrlport(gc, domid, usbdev, usbctrl.type);
-        if (!busid) {
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        /* Things are done in order of:
-         *   unbind USB device from usbback,
-         *   remove USB device from xenstore,
-         *   rebind USB device to original driver.
-         * It is to balance simplicity with robustness in case of failure:
-         * - We unbind all interfaces before rebinding any interfaces, so
-         *   that we never get into a situation where some interfaces are
-         *   assigned to usbback and some are assigned to the original drivers.
-         * - We also unbind the interfaces before removing the pvusb xenstore
-         *   nodes, so that if the unbind fails in the middle, the device still
-         *   shows up in xl usb-list, and the user can re-try removing it.
-         */
-        rc = usbback_dev_unassign(gc, busid);
-        if (rc) {
-            LOGD(ERROR, domid, "Error removing device from guest."
-                 " Try running usbdev-detach again.");
-            goto out;
-        }
-
-        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
-                                                  LIBXL_USBCTRL_TYPE_PV);
-        if (rc) {
-            LOGD(ERROR, domid, "Error removing device from guest."
-                 " Try running usbdev-detach again.");
-            goto out;
-        }
-
-        rc = usbdev_rebind(gc, busid);
-        if (rc) {
-            LOGD(ERROR, domid, "USB device removed from guest, but couldn't"
-                 " re-bind to domain 0. Try removing and re-inserting"
-                 " the USB device or reloading the driver modules.");
-            goto out;
-        }
-
-        break;
-    case LIBXL_USBCTRL_TYPE_QUSB:
-        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
-                                                  LIBXL_USBCTRL_TYPE_QUSB);
-        if (rc) goto out;
-
-        break;
-    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
-        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev,
-                                              LIBXL_USBCTRL_TYPE_DEVICEMODEL);
-        if (rc) goto out;
-
-        rc = libxl__ev_time_register_rel(ao, &aodev->timeout,
-                                         device_usbdev_remove_timeout,
-                                         LIBXL_QMP_CMD_TIMEOUT * 1000);
-        if (rc) goto out;
-
-        aodev->qmp.ao = ao;
-        aodev->qmp.domid = domid;
-        aodev->qmp.callback = device_usbdev_remove_qmp_cb;
-        aodev->qmp.payload_fd = -1;
-        rc = libxl__device_usbdev_del_hvm(egc, &aodev->qmp, usbdev);
-        if (rc) {
-            libxl__device_usbdev_add_xenstore(gc, domid, usbdev,
-                                              LIBXL_USBCTRL_TYPE_DEVICEMODEL,
-                                              false);
-            goto out;
-        }
-        has_callback = true;
-        break;
-    default:
-        LOGD(ERROR, domid, "Unsupported usb controller type");
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    libxl_device_usbctrl_dispose(&usbctrl);
-    /* Only call _done if no callback have been setup */
-    if (!has_callback)
-        device_usbdev_remove_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_remove_timeout(libxl__egc *egc,
-    libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout);
-
-    if (rc == ERROR_TIMEDOUT)
-        LOGD(ERROR, aodev->qmp.domid,
-             "Removing usbdev from QEMU timed out");
-    device_usbdev_remove_qmp_cb(egc, &aodev->qmp, NULL, rc);
-}
-
-static void device_usbdev_remove_qmp_cb(libxl__egc *egc,
-                                        libxl__ev_qmp *qmp,
-                                        const libxl__json_object *r,
-                                        int rc)
-{
-    EGC_GC;
-    libxl__ao_device *aodev = CONTAINER_OF(qmp, *aodev, qmp);
-    libxl_device_usbdev *const usbdev = aodev->device_config;
-
-    if (rc) {
-        libxl__device_usbdev_add_xenstore(gc, qmp->domid, usbdev,
-                                          LIBXL_USBCTRL_TYPE_DEVICEMODEL,
-                                          false);
-    }
-
-    device_usbdev_remove_done(egc, aodev, rc); /* must be last */
-}
-
-static void device_usbdev_remove_done(libxl__egc *egc,
-                                      libxl__ao_device *aodev,
-                                      int rc)
-{
-    EGC_GC;
-
-    libxl__ev_time_deregister(gc, &aodev->timeout);
-    libxl__ev_qmp_dispose(gc, &aodev->qmp);
-    aodev->rc = rc;
-    aodev->callback(egc, aodev);
-}
-
-int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
-                               libxl_device_usbdev *usbdev,
-                               const libxl_asyncop_how *ao_how)
-
-{
-    AO_CREATE(ctx, domid, ao_how);
-    libxl__ao_device *aodev;
-
-    GCNEW(aodev);
-    libxl__prepare_ao_device(ao, aodev);
-    aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
-    aodev->callback = device_addrm_aocomplete;
-    libxl__device_usbdev_remove(egc, domid, usbdev, aodev);
-
-    return AO_INPROGRESS;
-}
-
-int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx,
-                                    uint32_t domid,
-                                    int ctrl,
-                                    int port,
-                                    libxl_device_usbdev *usbdev)
-{
-    GC_INIT(ctx);
-    const char *libxl_path, *be_path, *busid;
-    int rc;
-
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, ctrl,
-                 LIBXL__DEVICE_KIND_VUSB);
-    be_path = vusb_be_from_xs_libxl(gc, libxl_path);
-    if (!be_path) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                           GCSPRINTF("%s/port/%d", be_path, port),
-                           &busid);
-    if (rc) goto out;
-
-    if (!busid || !strcmp(busid, "")) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    usbdev->ctrl = ctrl;
-    usbdev->port = port;
-    usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
-    rc = usbdev_busaddr_from_busid(gc, busid,
-                                   &usbdev->u.hostdev.hostbus,
-                                   &usbdev->u.hostdev.hostaddr);
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-static int libxl_device_usbctrl_compare(const libxl_device_usbctrl *d1,
-                                        const libxl_device_usbctrl *d2)
-{
-    return COMPARE_USBCTRL(d1, d2);
-}
-
-static int libxl_device_usbctrl_dm_needed(void *e, unsigned domid)
-{
-    libxl_device_usbctrl *elem = e;
-
-    return elem->type == LIBXL_USBCTRL_TYPE_QUSB &&
-           elem->backend_domid == domid;
-}
-
-static int libxl_device_usbdev_compare(const libxl_device_usbdev *d1,
-                                       const libxl_device_usbdev *d2)
-{
-    return COMPARE_USB(d1, d2);
-}
-
-void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr)
-{
-   int i;
-
-   for (i = 0; i < nr; i++)
-       libxl_device_usbdev_dispose(&list[i]);
-   free(list);
-}
-
-#define libxl__device_usbctrl_update_devid NULL
-
-LIBXL_DEFINE_DEVID_TO_DEVICE(usbctrl)
-LIBXL_DEFINE_DEVICE_LIST(usbctrl)
-DEFINE_DEVICE_TYPE_STRUCT(usbctrl, VUSB,
-    .from_xenstore = (device_from_xenstore_fn_t)libxl__usbctrl_from_xenstore,
-    .dm_needed = libxl_device_usbctrl_dm_needed
-);
-
-#define libxl__device_from_usbdev NULL
-#define libxl__device_usbdev_update_devid NULL
-
-DEFINE_DEVICE_TYPE_STRUCT(usbdev, VUSB);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c
deleted file mode 100644 (file)
index b039143..0000000
+++ /dev/null
@@ -1,1272 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include <ctype.h>
-
-#include "libxl_internal.h"
-#include "_paths.h"
-
-#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
-const
-#endif
-char *libxl_basename(const char *name)
-{
-    const char *filename;
-    if (name == NULL)
-        return strdup(".");
-    if (name[0] == '\0')
-        return strdup(".");
-
-    filename = strrchr(name, '/');
-    if (filename)
-        return strdup(filename+1);
-    return strdup(name);
-}
-
-unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus)
-{
-    /* 256 pages (1MB) per vcpu,
-       plus 1 page per MiB of RAM for the P2M map,
-       plus 1 page per MiB of RAM to shadow the resident processes.
-       This is higher than the minimum that Xen would allocate if no value
-       were given (but the Xen minimum is for safety, not performance).
-     */
-    return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024));
-}
-
-char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid)
-{
-    unsigned int len;
-    char path[strlen("/local/domain") + 12];
-    char *s;
-
-    snprintf(path, sizeof(path), "/local/domain/%d/name", domid);
-    s = xs_read(ctx->xsh, XBT_NULL, path, &len);
-    return s;
-}
-
-char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid)
-{
-    char *s = libxl_domid_to_name(CTX, domid);
-    libxl__ptr_add(gc, s);
-    return s;
-}
-
-int libxl_name_to_domid(libxl_ctx *ctx, const char *name,
-                        uint32_t *domid)
-{
-    int i, nb_domains;
-    char *domname;
-    libxl_dominfo *dominfo;
-    int ret = ERROR_INVAL;
-
-    dominfo = libxl_list_domain(ctx, &nb_domains);
-    if (!dominfo)
-        return ERROR_NOMEM;
-
-    for (i = 0; i < nb_domains; i++) {
-        domname = libxl_domid_to_name(ctx, dominfo[i].domid);
-        if (!domname)
-            continue;
-        if (strcmp(domname, name) == 0) {
-            *domid = dominfo[i].domid;
-            ret = 0;
-            free(domname);
-            break;
-        }
-        free(domname);
-    }
-    libxl_dominfo_list_free(dominfo, nb_domains);
-    return ret;
-}
-
-int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name,
-                                    uint32_t *domid)
-{
-    int i, rv;
-    for (i=0; name[i]; i++) {
-        if (!CTYPE(isdigit, name[i])) {
-            goto nondigit_found;
-        }
-    }
-    *domid = strtoul(name, NULL, 10);
-    return 0;
-
- nondigit_found:
-    /* this could also check for uuids */
-    rv = libxl_name_to_domid(ctx, name, domid);
-    return rv;
-}
-
-static int qualifier_to_id(const char *p, uint32_t *id_r)
-{
-    int i, alldigit;
-
-    alldigit = 1;
-    for (i = 0; p[i]; i++) {
-        if (!isdigit((uint8_t)p[i])) {
-            alldigit = 0;
-            break;
-        }
-    }
-
-    if (i > 0 && alldigit) {
-        *id_r = strtoul(p, NULL, 10);
-        return 0;
-    } else {
-        /* check here if it's a uuid and do proper conversion */
-    }
-    return 1;
-}
-
-int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
-                                         uint32_t *poolid_r,
-                                         int *was_name_r)
-{
-    int was_name;
-
-    was_name = qualifier_to_id(p, poolid_r);
-    if (was_name_r) *was_name_r = was_name;
-    return was_name ? libxl_name_to_cpupoolid(ctx, p, poolid_r) : 0;
-}
-
-char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid)
-{
-    unsigned int len;
-    char path[strlen("/local/pool") + 12];
-    char *s;
-
-    snprintf(path, sizeof(path), "/local/pool/%d/name", poolid);
-    s = xs_read(ctx->xsh, XBT_NULL, path, &len);
-    if (!s && (poolid == 0))
-        return strdup("Pool-0");
-    return s;
-}
-
-/* This is a bit horrid but without xs_exists it seems like the only way. */
-int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid)
-{
-    int ret;
-    char *s = libxl_cpupoolid_to_name(ctx, poolid);
-
-    ret = (s != NULL);
-    free(s);
-    return ret;
-}
-
-char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid)
-{
-    char *s = libxl_cpupoolid_to_name(CTX, poolid);
-    libxl__ptr_add(gc, s);
-    return s;
-}
-
-int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name,
-                        uint32_t *poolid)
-{
-    int i, nb_pools;
-    char *poolname;
-    libxl_cpupoolinfo *poolinfo;
-    int ret = ERROR_INVAL;
-
-    poolinfo = libxl_list_cpupool(ctx, &nb_pools);
-    if (!poolinfo)
-        return ERROR_NOMEM;
-
-    for (i = 0; i < nb_pools; i++) {
-        if (ret && ((poolname = libxl_cpupoolid_to_name(ctx,
-            poolinfo[i].poolid)) != NULL)) {
-            if (strcmp(poolname, name) == 0) {
-                *poolid = poolinfo[i].poolid;
-                ret = 0;
-            }
-            free(poolname);
-        }
-    }
-    libxl_cpupoolinfo_list_free(poolinfo, nb_pools);
-    return ret;
-}
-
-int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid)
-{
-    GC_INIT(ctx);
-    char * stubdom_id_s;
-    int ret;
-
-    stubdom_id_s = libxl__xs_read(gc, XBT_NULL,
-                                  GCSPRINTF("%s/image/device-model-domid",
-                                  libxl__xs_get_dompath(gc, guest_domid)));
-    if (stubdom_id_s)
-        ret = atoi(stubdom_id_s);
-    else
-        ret = 0;
-    GC_FREE;
-    return ret;
-}
-
-int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid)
-{
-    GC_INIT(ctx);
-    char *target, *endptr;
-    uint32_t value;
-    int ret = 0;
-
-    target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/target",
-                            libxl__xs_get_dompath(gc, domid)));
-    if (!target)
-        goto out;
-    value = strtol(target, &endptr, 10);
-    if (*endptr != '\0')
-        goto out;
-    if (target_domid)
-        *target_domid = value;
-    ret = 1;
-out:
-    GC_FREE;
-    return ret;
-}
-
-static int logrename(libxl__gc *gc, const char *old, const char *new)
-{
-    int r;
-
-    r = rename(old, new);
-    if (r) {
-        if (errno == ENOENT) return 0; /* ok */
-
-        LOGE(ERROR, "failed to rotate logfile - "
-                    "could not rename %s to %s", old, new);
-        return ERROR_FAIL;
-    }
-    return 0;
-}
-
-int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name)
-{
-    GC_INIT(ctx);
-    struct stat stat_buf;
-    char *logfile, *logfile_new;
-    int i, rc;
-
-    logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
-    if (stat(logfile, &stat_buf) == 0) {
-        /* file exists, rotate */
-        logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.10", name);
-        unlink(logfile);
-        for (i = 9; i > 0; i--) {
-            logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i);
-            logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i + 1);
-            rc = logrename(gc, logfile, logfile_new);
-            if (rc)
-                goto out;
-        }
-        logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
-        logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.1", name);
-
-        rc = logrename(gc, logfile, logfile_new);
-        if (rc)
-            goto out;
-    } else {
-        if (errno != ENOENT)
-            LOGE(WARN, "problem checking existence of logfile %s, "
-                       "which might have needed to be rotated",
-                 name);
-    }
-    *full_name = strdup(logfile);
-    rc = 0;
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend)
-{
-    char *p;
-    int rc = 0;
-
-    if (!strcmp(s, "phy")) {
-        *backend = LIBXL_DISK_BACKEND_PHY;
-    } else if (!strcmp(s, "file")) {
-        *backend = LIBXL_DISK_BACKEND_TAP;
-    } else if (!strcmp(s, "qdisk")) {
-        *backend = LIBXL_DISK_BACKEND_QDISK;
-    } else if (!strcmp(s, "tap")) {
-        p = strchr(s, ':');
-        if (!p) {
-            rc = ERROR_INVAL;
-            goto out;
-        }
-        p++;
-        if (!strcmp(p, "vhd")) {
-            *backend = LIBXL_DISK_BACKEND_TAP;
-        } else if (!strcmp(p, "qcow")) {
-            *backend = LIBXL_DISK_BACKEND_QDISK;
-        } else if (!strcmp(p, "qcow2")) {
-            *backend = LIBXL_DISK_BACKEND_QDISK;
-        } else if (!strcmp(p, "qed")) {
-            *backend = LIBXL_DISK_BACKEND_QDISK;
-        }
-    }
-out:
-    return rc;
-}
-
-int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
-                             void **data_r, int *datalen_r) {
-    GC_INIT(ctx);
-    FILE *f = 0;
-    uint8_t *data = 0;
-    int datalen = 0;
-    int e;
-    struct stat stab;
-    ssize_t rs;
-
-    f = fopen(filename, "r");
-    if (!f) {
-        if (errno == ENOENT) return ENOENT;
-        LOGE(ERROR, "failed to open %s", filename);
-        goto xe;
-    }
-
-    if (fstat(fileno(f), &stab)) {
-        LOGE(ERROR, "failed to fstat %s", filename);
-        goto xe;
-    }
-
-    if (!S_ISREG(stab.st_mode)) {
-        LOGE(ERROR, "%s is not a plain file", filename);
-        errno = ENOTTY;
-        goto xe;
-    }
-
-    if (stab.st_size > INT_MAX) {
-        LOG(ERROR, "file %s is far too large", filename);
-        errno = EFBIG;
-        goto xe;
-    }
-
-    datalen = stab.st_size;
-
-    if (stab.st_size && data_r) {
-        data = malloc(datalen);
-        if (!data) goto xe;
-
-        rs = fread(data, 1, datalen, f);
-        if (rs != datalen) {
-            if (ferror(f))
-                LOGE(ERROR, "failed to read %s", filename);
-            else if (feof(f))
-                LOG(ERROR, "%s changed size while we were reading it",
-                   filename);
-            else
-                abort();
-            goto xe;
-        }
-    }
-
-    if (fclose(f)) {
-        f = 0;
-        LOGE(ERROR, "failed to close %s", filename);
-        goto xe;
-    }
-
-    if (data_r) *data_r = data;
-    if (datalen_r) *datalen_r = datalen;
-
-    GC_FREE;
-    return 0;
-
- xe:
-    GC_FREE;
-    e = errno;
-    assert(e != ENOENT);
-    if (f) fclose(f);
-    free(data);
-    return e;
-}
-
-int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename,
-                                    void **data_r, int *datalen_r)
-{
-    FILE *f = 0;
-    uint8_t *data = 0;
-    int datalen = 0;
-    int e;
-    struct stat stab;
-    ssize_t rs;
-
-    f = fopen(filename, "r");
-    if (!f) {
-        if (errno == ENOENT) return ENOENT;
-        LOGE(ERROR, "failed to open %s", filename);
-        goto xe;
-    }
-
-    if (fstat(fileno(f), &stab)) {
-        LOGE(ERROR, "failed to fstat %s", filename);
-        goto xe;
-    }
-
-    if (!S_ISREG(stab.st_mode)) {
-        LOGE(ERROR, "%s is not a plain file", filename);
-        errno = ENOTTY;
-        goto xe;
-    }
-
-    if (stab.st_size > INT_MAX) {
-        LOG(ERROR, "file %s is far too large", filename);
-        errno = EFBIG;
-        goto xe;
-    }
-
-    datalen = stab.st_size;
-
-    if (stab.st_size && data_r) {
-        data = libxl__malloc(gc, datalen);
-
-        /* For sysfs file, datalen is always PAGE_SIZE. 'read'
-         * will return the number of bytes of the actual content,
-         * rs <= datalen is expected.
-         */
-        rs = fread(data, 1, datalen, f);
-        if (rs < datalen) {
-            if (ferror(f)) {
-                LOGE(ERROR, "failed to read %s", filename);
-                goto xe;
-            }
-
-            datalen = rs;
-            data = libxl__realloc(gc, data, datalen);
-        }
-    }
-
-    if (fclose(f)) {
-        f = 0;
-        LOGE(ERROR, "failed to close %s", filename);
-        goto xe;
-    }
-
-    if (data_r) *data_r = data;
-    if (datalen_r) *datalen_r = datalen;
-
-    return 0;
-
- xe:
-    e = errno;
-    assert(e != ENOENT);
-    if (f) fclose(f);
-    return e;
-}
-
-
-#define READ_WRITE_EXACTLY(rw, zero_is_eof, constdata)                    \
-                                                                          \
-  int libxl_##rw##_exactly(libxl_ctx *ctx, int fd,                 \
-                           constdata void *data, ssize_t sz,              \
-                           const char *source, const char *what) {        \
-      ssize_t got;                                                        \
-      GC_INIT(ctx);                                                       \
-                                                                          \
-      while (sz > 0) {                                                    \
-          got = rw(fd, data, sz);                                         \
-          if (got == -1) {                                                \
-              if (errno == EINTR) continue;                               \
-              if (!ctx) { GC_FREE; return errno; }                        \
-              LOGE(ERROR, "failed to "#rw" %s%s%s",                       \
-                   what ? what : "", what ? " from " : "", source);       \
-              GC_FREE;                                                    \
-              return errno;                                               \
-          }                                                               \
-          if (got == 0) {                                                 \
-              if (!ctx) { GC_FREE; return  EPROTO; }                      \
-              LOG(ERROR, zero_is_eof                                      \
-                  ? "file/stream truncated reading %s%s%s"                \
-                  : "file/stream write returned 0! writing %s%s%s",       \
-                  what ? what : "", what ? " from " : "", source);        \
-              GC_FREE;                                                    \
-              return EPROTO;                                              \
-          }                                                               \
-          sz -= got;                                                      \
-          data = (char*)data + got;                                       \
-      }                                                                   \
-      GC_FREE;                                                            \
-      return 0;                                                           \
-  }
-
-READ_WRITE_EXACTLY(read, 1, /* */)
-READ_WRITE_EXACTLY(write, 0, const)
-
-int libxl__remove_file(libxl__gc *gc, const char *path)
-{
-    for (;;) {
-        int r = unlink(path);
-        if (!r) return 0;
-        if (errno == ENOENT) return 0;
-        if (errno == EINTR) continue;
-        LOGE(ERROR, "failed to remove file %s", path);
-        return ERROR_FAIL;
-     }
-}
-
-int libxl__remove_file_or_directory(libxl__gc *gc, const char *path)
-{
-    for (;;) {
-        int r = rmdir(path);
-        if (!r) return 0;
-        if (errno == ENOENT) return 0;
-        if (errno == ENOTEMPTY) return libxl__remove_directory(gc, path);
-        if (errno == ENOTDIR) return libxl__remove_file(gc, path);
-        if (errno == EINTR) continue;
-        LOGE(ERROR, "failed to remove %s", path);
-        return ERROR_FAIL;
-     }
-}
-
-int libxl__remove_directory(libxl__gc *gc, const char *dirpath)
-{
-    int rc = 0;
-    DIR *d = 0;
-
-    d = opendir(dirpath);
-    if (!d) {
-        if (errno == ENOENT)
-            goto out;
-
-        LOGE(ERROR, "failed to opendir %s for removal", dirpath);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    struct dirent *de;
-
-    for (;;) {
-        errno = 0;
-        de = readdir(d);
-        if (!de && errno) {
-            LOGE(ERROR, "failed to readdir %s for removal", dirpath);
-            rc = ERROR_FAIL;
-            break;
-        }
-        if (!de)
-            break;
-
-        if (!strcmp(de->d_name, ".") ||
-            !strcmp(de->d_name, ".."))
-            continue;
-
-        const char *subpath = GCSPRINTF("%s/%s", dirpath, de->d_name);
-        if (libxl__remove_file_or_directory(gc, subpath))
-            rc = ERROR_FAIL;
-    }
-
-    for (;;) {
-        int r = rmdir(dirpath);
-        if (!r) break;
-        if (errno == ENOENT) goto out;
-        if (errno == EINTR) continue;
-        LOGE(ERROR, "failed to remove emptied directory %s", dirpath);
-        rc = ERROR_FAIL;
-    }
-
- out:
-    if (d) closedir(d);
-
-    return rc;
-}
-
-int libxl_pipe(libxl_ctx *ctx, int pipes[2])
-{
-    GC_INIT(ctx);
-    int ret = 0;
-    if (pipe(pipes) < 0) {
-        LOG(ERROR, "Failed to create a pipe");
-        ret = -1;
-    }
-    GC_FREE;
-    return ret;
-}
-
-int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits)
-{
-    GC_INIT(ctx);
-    int sz;
-
-    sz = (n_bits + 7) / 8;
-    bitmap->map = libxl__calloc(NOGC, sizeof(*bitmap->map), sz);
-    bitmap->size = sz;
-
-    GC_FREE;
-    return 0;
-}
-
-void libxl_bitmap_init(libxl_bitmap *map)
-{
-    memset(map, '\0', sizeof(*map));
-}
-
-void libxl_bitmap_dispose(libxl_bitmap *map)
-{
-    if (!map)
-        return;
-
-    free(map->map);
-    map->map = NULL;
-    map->size = 0;
-}
-
-void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
-                       const libxl_bitmap *sptr)
-{
-    int sz;
-
-    assert(dptr->size == sptr->size);
-    sz = dptr->size = sptr->size;
-    memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
-}
-
-/* This function copies X bytes from source to destination bitmap,
- * where X is the smaller of the two sizes.
- *
- * If destination's size is larger than source, the extra bytes are
- * untouched.
- */
-void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
-                                    const libxl_bitmap *sptr)
-{
-    int sz;
-
-    sz = dptr->size < sptr->size ? dptr->size : sptr->size;
-    memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
-}
-
-void libxl_bitmap_copy_alloc(libxl_ctx *ctx,
-                             libxl_bitmap *dptr,
-                             const libxl_bitmap *sptr)
-{
-    GC_INIT(ctx);
-
-    dptr->map = libxl__calloc(NOGC, sptr->size, sizeof(*sptr->map));
-    dptr->size = sptr->size;
-    memcpy(dptr->map, sptr->map, sptr->size * sizeof(*sptr->map));
-
-    GC_FREE;
-}
-
-int libxl_bitmap_is_full(const libxl_bitmap *bitmap)
-{
-    int i;
-
-    for (i = 0; i < bitmap->size; i++)
-        if (bitmap->map[i] != (uint8_t)-1)
-            return 0;
-   return 1;
-}
-
-int libxl_bitmap_is_empty(const libxl_bitmap *bitmap)
-{
-    int i;
-
-    for (i = 0; i < bitmap->size; i++)
-        if (bitmap->map[i])
-            return 0;
-    return 1;
-}
-
-int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit)
-{
-    if (bit >= bitmap->size * 8)
-        return 0;
-    return (bitmap->map[bit / 8] & (1 << (bit & 7))) ? 1 : 0;
-}
-
-void libxl_bitmap_set(libxl_bitmap *bitmap, int bit)
-{
-    if (bit >= bitmap->size * 8)
-        return;
-    bitmap->map[bit / 8] |= 1 << (bit & 7);
-}
-
-void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit)
-{
-    if (bit >= bitmap->size * 8)
-        return;
-    bitmap->map[bit / 8] &= ~(1 << (bit & 7));
-}
-
-int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
-                    const libxl_bitmap *map1, const libxl_bitmap *map2)
-{
-    GC_INIT(ctx);
-    int rc;
-    uint32_t i;
-    const libxl_bitmap *large_map;
-    const libxl_bitmap *small_map;
-
-    if (map1->size > map2->size) {
-        large_map = map1;
-        small_map = map2;
-    } else {
-        large_map = map2;
-        small_map = map1;
-    }
-
-    rc = libxl_bitmap_alloc(ctx, or_map, large_map->size * 8);
-    if (rc)
-        goto out;
-
-    /*
-     *  If bitmaps aren't the same size, their union (logical or) will
-     *  be size of larger bit map.  Any bit past the end of the
-     *  smaller bit map, will match the larger one.
-     */
-    for (i = 0; i < small_map->size; i++)
-        or_map->map[i] = (small_map->map[i] | large_map->map[i]);
-
-    for (i = small_map->size; i < large_map->size; i++)
-        or_map->map[i] = large_map->map[i];
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
-                     const libxl_bitmap *map1, const libxl_bitmap *map2)
-{
-    GC_INIT(ctx);
-    int rc;
-    uint32_t i;
-    const libxl_bitmap *large_map;
-    const libxl_bitmap *small_map;
-
-    if (map1->size > map2->size) {
-        large_map = map1;
-        small_map = map2;
-    } else {
-        large_map = map2;
-        small_map = map1;
-    }
-
-    rc = libxl_bitmap_alloc(ctx, and_map, small_map->size * 8);
-    if (rc)
-        goto out;
-
-    /*
-     *  If bitmaps aren't same size, their 'and' will be size of
-     *  smaller bit map
-     */
-    for (i = 0; i < and_map->size; i++)
-        and_map->map[i] = (large_map->map[i] & small_map->map[i]);
-
-out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_bitmap_count_set(const libxl_bitmap *bitmap)
-{
-    int i, nr_set_bits = 0;
-    libxl_for_each_set_bit(i, *bitmap)
-        nr_set_bits++;
-
-    return nr_set_bits;
-}
-
-/* NB. caller is responsible for freeing the memory */
-char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap)
-{
-    GC_INIT(ctx);
-    int i = bitmap->size;
-    char *p = libxl__zalloc(NOGC, bitmap->size * 2 + 3);
-    char *q = p;
-    strncpy(p, "0x", 3);
-    p += 2;
-    while(--i >= 0) {
-        sprintf(p, "%02x", bitmap->map[i]);
-        p += 2;
-    }
-    *p = '\0';
-    GC_FREE;
-    return q;
-}
-
-int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus)
-{
-    GC_INIT(ctx);
-    int rc = 0;
-
-    if (max_cpus < 0) {
-        rc = ERROR_INVAL;
-        LOG(ERROR, "invalid number of cpus provided");
-        goto out;
-    }
-    if (max_cpus == 0)
-        max_cpus = libxl_get_max_cpus(ctx);
-    if (max_cpus < 0) {
-        LOG(ERROR, "failed to retrieve the maximum number of cpus");
-        rc = max_cpus;
-        goto out;
-    }
-    /* This can't fail: no need to check and log */
-    libxl_bitmap_alloc(ctx, cpumap, max_cpus);
-
- out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
-                            int max_nodes)
-{
-    GC_INIT(ctx);
-    int rc = 0;
-
-    if (max_nodes < 0) {
-        rc = ERROR_INVAL;
-        LOG(ERROR, "invalid number of nodes provided");
-        goto out;
-    }
-
-    if (max_nodes == 0)
-        max_nodes = libxl_get_max_nodes(ctx);
-    if (max_nodes < 0) {
-        LOG(ERROR, "failed to retrieve the maximum number of nodes");
-        rc = max_nodes;
-        goto out;
-    }
-    /* This can't fail: no need to check and log */
-    libxl_bitmap_alloc(ctx, nodemap, max_nodes);
-
- out:
-    GC_FREE;
-    return rc;
-}
-
-int libxl__count_physical_sockets(libxl__gc *gc, int *sockets)
-{
-    int rc;
-    libxl_physinfo info;
-
-    libxl_physinfo_init(&info);
-
-    rc = libxl_get_physinfo(CTX, &info);
-    if (rc)
-        return rc;
-
-    *sockets = info.nr_cpus / info.threads_per_core
-                            / info.cores_per_socket;
-
-    libxl_physinfo_dispose(&info);
-    return 0;
-}
-
-int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
-                              int max_sockets)
-{
-    GC_INIT(ctx);
-    int rc = 0;
-
-    if (max_sockets < 0) {
-        rc = ERROR_INVAL;
-        LOG(ERROR, "invalid number of sockets provided");
-        goto out;
-    }
-
-    if (max_sockets == 0) {
-        rc = libxl__count_physical_sockets(gc, &max_sockets);
-        if (rc) {
-            LOGE(ERROR, "failed to get system socket count");
-            goto out;
-        }
-    }
-    /* This can't fail: no need to check and log */
-    libxl_bitmap_alloc(ctx, socketmap, max_sockets);
-
- out:
-    GC_FREE;
-    return rc;
-
-}
-
-int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap)
-{
-    libxl_cputopology *tinfo = NULL;
-    int nr_cpus = 0, i, rc = 0;
-
-    tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
-    if (tinfo == NULL) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl_bitmap_set_none(socketmap);
-    for (i = 0; i < nr_cpus; i++)
-        if (tinfo[i].socket != XEN_INVALID_SOCKET_ID
-            && !libxl_bitmap_test(socketmap, tinfo[i].socket))
-            libxl_bitmap_set(socketmap, tinfo[i].socket);
-
- out:
-    libxl_cputopology_list_free(tinfo, nr_cpus);
-    return rc;
-}
-
-int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
-                            const libxl_bitmap *nodemap,
-                            libxl_bitmap *cpumap)
-{
-    libxl_cputopology *tinfo = NULL;
-    int nr_cpus = 0, i, rc = 0;
-
-    tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
-    if (tinfo == NULL) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl_bitmap_set_none(cpumap);
-    for (i = 0; i < nr_cpus; i++) {
-        if (libxl_bitmap_test(nodemap, tinfo[i].node))
-            libxl_bitmap_set(cpumap, i);
-    }
- out:
-    libxl_cputopology_list_free(tinfo, nr_cpus);
-    return rc;
-}
-
-int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
-                         libxl_bitmap *cpumap)
-{
-    libxl_bitmap nodemap;
-    int rc = 0;
-
-    libxl_bitmap_init(&nodemap);
-
-    rc = libxl_node_bitmap_alloc(ctx, &nodemap, 0);
-    if (rc)
-        goto out;
-
-    libxl_bitmap_set_none(&nodemap);
-    libxl_bitmap_set(&nodemap, node);
-
-    rc = libxl_nodemap_to_cpumap(ctx, &nodemap, cpumap);
-
- out:
-    libxl_bitmap_dispose(&nodemap);
-    return rc;
-}
-
-int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
-                            const libxl_bitmap *cpumap,
-                            libxl_bitmap *nodemap)
-{
-    libxl_cputopology *tinfo = NULL;
-    int nr_cpus = 0, i, rc = 0;
-
-    tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
-    if (tinfo == NULL) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    libxl_bitmap_set_none(nodemap);
-    libxl_for_each_set_bit(i, *cpumap) {
-        if (i >= nr_cpus)
-            break;
-        libxl_bitmap_set(nodemap, tinfo[i].node);
-    }
- out:
-    libxl_cputopology_list_free(tinfo, nr_cpus);
-    return rc;
-}
-
-int libxl_get_max_cpus(libxl_ctx *ctx)
-{
-    int max_cpus = xc_get_max_cpus(ctx->xch);
-
-    return max_cpus < 0 ? ERROR_FAIL : max_cpus;
-}
-
-int libxl_get_online_cpus(libxl_ctx *ctx)
-{
-    int online_cpus = xc_get_online_cpus(ctx->xch);
-
-    return online_cpus < 0 ? ERROR_FAIL : online_cpus;
-}
-
-int libxl_get_max_nodes(libxl_ctx *ctx)
-{
-    int max_nodes = xc_get_max_nodes(ctx->xch);
-
-    return max_nodes < 0 ? ERROR_FAIL : max_nodes;
-}
-
-int libxl__enum_from_string(const libxl_enum_string_table *t,
-                            const char *s, int *e)
-{
-    if (!t) return ERROR_INVAL;
-
-    for( ; t->s; t++) {
-        if (!strcasecmp(t->s, s)) {
-                *e = t->v;
-                return 0;
-        }
-    }
-    return ERROR_FAIL;
-}
-
-void libxl_cputopology_list_free(libxl_cputopology *list, int nr)
-{
-    int i;
-    for (i = 0; i < nr; i++)
-        libxl_cputopology_dispose(&list[i]);
-    free(list);
-}
-
-void libxl_pcitopology_list_free(libxl_pcitopology *list, int nr)
-{
-    int i;
-    for (i = 0; i < nr; i++)
-        libxl_pcitopology_dispose(&list[i]);
-    free(list);
-}
-
-void libxl_numainfo_list_free(libxl_numainfo *list, int nr)
-{
-    int i;
-    for (i = 0; i < nr; i++)
-        libxl_numainfo_dispose(&list[i]);
-    free(list);
-}
-
-void libxl_vcpuinfo_list_free(libxl_vcpuinfo *list, int nr)
-{
-    int i;
-    for (i = 0; i < nr; i++)
-        libxl_vcpuinfo_dispose(&list[i]);
-    free(list);
-}
-
-int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
-                       const char data,
-                       int nfds, const int fds[], const char *what) {
-    struct msghdr msg = { 0 };
-    struct cmsghdr *cmsg;
-    size_t spaceneeded = nfds * sizeof(fds[0]);
-    char control[CMSG_SPACE(spaceneeded)];
-    const size_t datalen = 1;
-    struct iovec iov;
-    int r;
-
-    iov.iov_base = (void*)&data;
-    iov.iov_len  = datalen;
-
-    /* compose the message */
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_control = control;
-    msg.msg_controllen = sizeof(control);
-
-    /* attach open fd */
-    cmsg = CMSG_FIRSTHDR(&msg);
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    cmsg->cmsg_len = CMSG_LEN(spaceneeded);
-    memcpy(CMSG_DATA(cmsg), fds, spaceneeded);
-
-    msg.msg_controllen = cmsg->cmsg_len;
-
-    while (1) {
-        r = sendmsg(carrier, &msg, 0);
-        if (r < 0) {
-            if (errno == EINTR)
-                continue;
-            if (errno == EWOULDBLOCK) {
-                return ERROR_NOT_READY;
-            }
-            LOGE(ERROR, "failed to send fd-carrying message (%s)", what);
-            return ERROR_FAIL;
-        }
-        if (r != datalen) {
-            LOG(ERROR, "sendmsg have written %d instead of %zu",
-                r, datalen);
-            return ERROR_FAIL;
-        }
-        break;
-    };
-
-    return 0;
-}
-
-int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
-                       void *databuf, size_t datalen,
-                       int nfds, int fds[], const char *what)
-{
-    struct msghdr msg = { 0 };
-    struct cmsghdr *cmsg;
-    size_t spaceneeded = nfds * sizeof(fds[0]);
-    char control[CMSG_SPACE(spaceneeded)];
-    struct iovec iov;
-    int r;
-
-    iov.iov_base = databuf;
-    iov.iov_len  = datalen;
-
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_control = control;
-    msg.msg_controllen = sizeof(control);
-
-    for (;;) {
-        r = recvmsg(carrier, &msg, 0);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            if (errno == EWOULDBLOCK) return -1;
-            LOGE(ERROR,"recvmsg failed (%s)", what);
-            return ERROR_FAIL;
-        }
-        if (r == 0) {
-            LOG(ERROR,"recvmsg got EOF (%s)", what);
-            return ERROR_FAIL;
-        }
-        cmsg = CMSG_FIRSTHDR(&msg);
-        if (cmsg->cmsg_len <= CMSG_LEN(0)) {
-            LOG(ERROR,"recvmsg got no control msg"
-                " when expecting fds (%s)", what);
-            return ERROR_FAIL;
-        }
-        if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
-            LOG(ERROR, "recvmsg got unexpected"
-                " cmsg_level %d (!=%d) or _type %d (!=%d) (%s)",
-                cmsg->cmsg_level, SOL_SOCKET,
-                cmsg->cmsg_type, SCM_RIGHTS,
-                what);
-            return ERROR_FAIL;
-        }
-        if (cmsg->cmsg_len != CMSG_LEN(spaceneeded) ||
-            msg.msg_controllen != cmsg->cmsg_len) {
-            LOG(ERROR, "recvmsg got unexpected"
-                " number of fds or extra control data"
-                " (%ld bytes' worth, expected %ld) (%s)",
-                (long)CMSG_LEN(spaceneeded), (long)cmsg->cmsg_len,
-                what);
-            int i, fd;
-            unsigned char *p;
-            for (i=0, p=CMSG_DATA(cmsg);
-                 CMSG_SPACE(i * sizeof(fds[0]));
-                 i++, i+=sizeof(fd)) {
-                memcpy(&fd, p, sizeof(fd));
-                close(fd);
-            }
-            return ERROR_FAIL;
-        }
-        memcpy(fds, CMSG_DATA(cmsg), spaceneeded);
-        return 0;
-    }
-}         
-
-void libxl_dominfo_list_free(libxl_dominfo *list, int nr)
-{
-    int i;
-    for (i = 0; i < nr; i++)
-        libxl_dominfo_dispose(&list[i]);
-    free(list);
-}
-
-void libxl_vminfo_list_free(libxl_vminfo *list, int nr)
-{
-    int i;
-    for (i = 0; i < nr; i++)
-        libxl_vminfo_dispose(&list[i]);
-    free(list);
-}
-
-void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nr)
-{
-    int i;
-    for (i = 0; i < nr; i++)
-        libxl_cpupoolinfo_dispose(&list[i]);
-    free(list);
-}
-
-int libxl_domid_valid_guest(uint32_t domid)
-{
-    /* returns 1 if the value _could_ be a valid guest domid, 0 otherwise
-     * does not check whether the domain actually exists */
-    return domid > 0 && domid < DOMID_FIRST_RESERVED;
-}
-
-void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src)
-{
-    GC_INIT(ctx);
-
-    if (*src)
-        *dst = libxl__strdup(NOGC, *src);
-    else
-        *dst = NULL;
-
-    GC_FREE;
-}
-
-/*
- * Fill @buf with @len random bytes.
- */
-int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len)
-{
-    static const char *dev = "/dev/urandom";
-    int fd;
-    int ret;
-
-    fd = open(dev, O_RDONLY);
-    if (fd < 0) {
-        LOGE(ERROR, "failed to open \"%s\"", dev);
-        return ERROR_FAIL;
-    }
-    ret = libxl_fd_set_cloexec(CTX, fd, 1);
-    if (ret) {
-        close(fd);
-        return ERROR_FAIL;
-    }
-
-    ret = libxl_read_exactly(CTX, fd, buf, len, dev, NULL);
-
-    close(fd);
-
-    return ret;
-}
-
-int libxl__prepare_sockaddr_un(libxl__gc *gc,
-                               struct sockaddr_un *un, const char *path,
-                               const char *what)
-{
-    if (sizeof(un->sun_path) - 1 <= strlen(path)) {
-        LOG(ERROR, "UNIX socket path '%s' is too long for %s", path, what);
-        LOG(DEBUG, "Path must be less than %zu bytes", sizeof(un->sun_path) - 1);
-        return ERROR_INVAL;
-    }
-    memset(un, 0, sizeof(struct sockaddr_un));
-    un->sun_family = AF_UNIX;
-    strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
-    return 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h
deleted file mode 100644 (file)
index 46918ae..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef LIBXL_UTILS_H
-#define LIBXL_UTILS_H
-
-#include "libxl.h"
-
-#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
-const
-#endif
-char *libxl_basename(const char *name); /* returns string from strdup */
-
-unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus);
-  /* deprecated; see LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONFIG in libxl.h */
-int libxl_name_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
-int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid);
-char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid);
-int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
-                                         uint32_t *poolid_r,
-                                         int *was_name_r);
-int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name, uint32_t *poolid);
-char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid);
-int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid);
-int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid);
-int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid);
-int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name);
-int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend);
-
-int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
-                             void **data_r, int *datalen_r);
-  /* Reads the contents of the plain file filename into a mallocd
-   * buffer.  Returns 0 or errno.  Any errors other than ENOENT are logged.
-   * If the file is empty, *data_r and *datalen_r are set to 0.
-   * On error, *data_r and *datalen_r are unchanged.
-   * data_r and/or datalen_r may be 0.
-   */
-
-int libxl_read_exactly(libxl_ctx *ctx, int fd, void *data, ssize_t sz,
-                       const char *filename, const char *what);
-int libxl_write_exactly(libxl_ctx *ctx, int fd, const void *data,
-                        ssize_t sz, const char *filename, const char *what);
-  /* Returns 0 or errno.  If file is truncated on reading, returns
-   * EPROTO and you have no way to tell how much was read.  Errors are
-   * logged using filename (which is only used for logging) and what
-   * (which may be 0). */
-
-int libxl_pipe(libxl_ctx *ctx, int pipes[2]);
-  /* Just like pipe(2), but log errors. */
-
-void libxl_report_child_exitstatus(libxl_ctx *ctx, xentoollog_level,
-                                   const char *what, pid_t pid, int status);
-    /* treats all exit statuses as errors; if that's not what you want,
-     * check status yourself first */
-
-int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
-                            const char *mac, libxl_device_nic *nic);
-int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid, int devid,
-                              libxl_device_nic *nic);
-
-int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, const char *vdev,
-                               libxl_device_disk *disk);
-
-int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
-                               libxl_uuid *uuid, libxl_device_vtpm *vtpm);
-int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
-                               int devid, libxl_device_vtpm *vtpm);
-int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid,
-                                  int devid, libxl_device_usbctrl *usbctrl);
-
-int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
-                              int devid, libxl_device_vkb *vkb);
-
-int libxl_devid_to_device_vdispl(libxl_ctx *ctx, uint32_t domid,
-                                 int devid, libxl_device_vdispl *vdispl);
-
-int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
-                               int devid, libxl_device_vsnd *vsnd);
-
-int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid,
-                                    int ctrl, int port,
-                                    libxl_device_usbdev *usbdev);
-
-int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits);
-    /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be
-     * called by the application when done. */
-void libxl_bitmap_copy_alloc(libxl_ctx *ctx, libxl_bitmap *dptr,
-                             const libxl_bitmap *sptr);
-void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
-                       const libxl_bitmap *sptr);
-int libxl_bitmap_is_full(const libxl_bitmap *bitmap);
-int libxl_bitmap_is_empty(const libxl_bitmap *bitmap);
-int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit);
-void libxl_bitmap_set(libxl_bitmap *bitmap, int bit);
-void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit);
-int libxl_bitmap_count_set(const libxl_bitmap *bitmap);
-int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
-                    const libxl_bitmap *map1,
-                    const libxl_bitmap *map2);
-int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
-                     const libxl_bitmap *map1,
-                     const libxl_bitmap *map2);
-char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap);
-static inline void libxl_bitmap_set_any(libxl_bitmap *bitmap)
-{
-    memset(bitmap->map, -1, bitmap->size);
-}
-static inline void libxl_bitmap_set_none(libxl_bitmap *bitmap)
-{
-    memset(bitmap->map, 0, bitmap->size);
-}
-static inline int libxl_bitmap_cpu_valid(libxl_bitmap *bitmap, int bit)
-{
-    return bit >= 0 && bit < (bitmap->size * 8);
-}
-#define libxl_for_each_bit(var, map) for (var = 0; var < (map).size * 8; var++)
-#define libxl_for_each_set_bit(v, m) for (v = 0; v < (m).size * 8; v++) \
-                                             if (libxl_bitmap_test(&(m), v))
-
-/*
- * Compares two bitmaps bit by bit, up to nr_bits or, if nr_bits is 0, up
- * to the size of the largest bitmap. If sizes does not match, bits past the
- * of a bitmap are considered as being 0, which matches with the semantic and
- * implementation of libxl_bitmap_test I think().
- *
- * So, basically, [0,1,0] and [0,1] are considered equal, while [0,1,1] and
- * [0,1] are different.
- */
-static inline int libxl_bitmap_equal(const libxl_bitmap *ba,
-                                     const libxl_bitmap *bb,
-                                     int nr_bits)
-{
-    int i;
-
-    if (nr_bits == 0)
-        nr_bits = ba->size > bb->size ? ba->size * 8 : bb->size * 8;
-
-    for (i = 0; i < nr_bits; i++) {
-        if (libxl_bitmap_test(ba, i) != libxl_bitmap_test(bb, i))
-            return 0;
-    }
-    return 1;
-}
-
-int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus);
-int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
-                            int max_nodes);
-int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
-                              int max_sockets);
-/* Fill socketmap with the CPU topology information on the system. */
-int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap);
-
-/* Populate cpumap with the cpus spanned by the nodes in nodemap */
-int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
-                            const libxl_bitmap *nodemap,
-                            libxl_bitmap *cpumap);
-/* Populate cpumap with the cpus spanned by node */
-int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
-                         libxl_bitmap *cpumap);
-/* Populate nodemap with the nodes of the cpus in cpumap */
-int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
-                            const libxl_bitmap *cpumap,
-                            libxl_bitmap *nodemap);
-
- static inline uint32_t libxl__sizekb_to_mb(uint32_t s) {
-    return (s + 1023) / 1024;
-}
-
-void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src);
-
-
-#define LIBXL_FILLZERO(object) (memset(&(object), 0, sizeof((object))))
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_uuid.c b/tools/libxl/libxl_uuid.c
deleted file mode 100644 (file)
index dadb79b..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2008,2010 Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-#if defined(__linux__)
-
-int libxl_uuid_is_nil(const libxl_uuid *uuid)
-{
-     return uuid_is_null(uuid->uuid);
-}
-
-void libxl_uuid_generate(libxl_uuid *uuid)
-{
-     uuid_generate(uuid->uuid);
-}
-
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
-{
-     return uuid_parse(in, uuid->uuid);
-}
-
-void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
-                     const libxl_uuid *src)
-{
-     uuid_copy(dst->uuid, src->uuid);
-}
-
-void libxl_uuid_clear(libxl_uuid *uuid)
-{
-     uuid_clear(uuid->uuid);
-}
-
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
-{
-     return uuid_compare(uuid1->uuid, uuid2->uuid);
-}
-
-const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
-{
-    return uuid->uuid;
-}
-
-uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
-{
-    return uuid->uuid;
-}
-
-#elif defined(__FreeBSD__) || defined(__NetBSD__)
-
-int libxl_uuid_is_nil(const libxl_uuid *uuid)
-{
-    uint32_t status;
-    uuid_t nat_uuid;
-
-    uuid_dec_be(uuid->uuid, &nat_uuid);
-
-    return uuid_is_nil(&nat_uuid, &status);
-}
-
-void libxl_uuid_generate(libxl_uuid *uuid)
-{
-    uint32_t status;
-    uuid_t nat_uuid;
-
-    uuid_create(&nat_uuid, &status);
-    assert(status == uuid_s_ok);
-
-    uuid_enc_be(uuid->uuid, &nat_uuid);
-}
-
-#ifdef __FreeBSD__
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
-{
-    uint32_t status;
-    uuid_t nat_uuid;
-
-    uuid_from_string(in, &nat_uuid, &status);
-    if (status != uuid_s_ok)
-        return ERROR_FAIL;
-    uuid_enc_be(uuid->uuid, &nat_uuid);
-
-    return 0;
-}
-#else
-#define LIBXL__UUID_PTRS(uuid) &uuid[0], &uuid[1], &uuid[2], &uuid[3], \
-                               &uuid[4], &uuid[5], &uuid[6], &uuid[7], \
-                               &uuid[8], &uuid[9], &uuid[10],&uuid[11], \
-                               &uuid[12],&uuid[13],&uuid[14],&uuid[15]
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in)
-{
-    if ( sscanf(in, LIBXL_UUID_FMT, LIBXL__UUID_PTRS(uuid->uuid)) != sizeof(uuid->uuid) )
-        return -1;
-    return 0;
-}
-#undef LIBXL__UUID_PTRS
-#endif
-
-void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
-                     const libxl_uuid *src)
-{
-    memcpy(&dst->uuid, &src->uuid, sizeof(dst->uuid));
-}
-
-void libxl_uuid_clear(libxl_uuid *uuid)
-{
-    memset(&uuid->uuid, 0, sizeof(uuid->uuid));
-}
-
-#ifdef __FreeBSD__
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
-{
-    uuid_t nat_uuid1, nat_uuid2;
-
-    uuid_dec_be(uuid1->uuid, &nat_uuid1);
-    uuid_dec_be(uuid2->uuid, &nat_uuid2);
-
-    return uuid_compare(&nat_uuid1, &nat_uuid2, NULL);
-}
-#else
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2)
-{
-     return memcmp(uuid1->uuid, uuid2->uuid, sizeof(uuid1->uuid));
-}
-#endif
-
-const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid)
-{
-
-    return uuid->uuid;
-}
-
-uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid)
-{
-
-    return uuid->uuid;
-}
-#else
-
-#error "Please update libxl_uuid.c for your OS"
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_uuid.h b/tools/libxl/libxl_uuid.h
deleted file mode 100644 (file)
index 17c8b97..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2008,2010 Citrix Ltd.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#ifndef __LIBXL_UUID_H__
-#define __LIBXL_UUID_H__
-
-#define LIBXL_UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
-#define LIBXL_UUID_FMTLEN ((2*16)+4) /* 16 hex bytes plus 4 hypens */
-#define LIBXL__UUID_BYTES(uuid) uuid[0], uuid[1], uuid[2], uuid[3], \
-                                uuid[4], uuid[5], uuid[6], uuid[7], \
-                                uuid[8], uuid[9], uuid[10], uuid[11], \
-                                uuid[12], uuid[13], uuid[14], uuid[15]
-#define LIBXL_UUID_BYTES(arg) LIBXL__UUID_BYTES((arg).uuid)
-
-typedef struct {
-    /* UUID as an octet stream in big-endian byte-order. */
-    unsigned char uuid[16];
-} libxl_uuid;
-
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040700
-#if defined(__linux__)
-
-#include <uuid/uuid.h>
-#include <stdint.h>
-
-#elif defined(__FreeBSD__) || defined(__NetBSD__)
-
-#include <uuid.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-
-#else
-
-#error "Please update libxl_uuid.h for your OS"
-
-#endif
-#endif
-
-int libxl_uuid_is_nil(const libxl_uuid *uuid);
-void libxl_uuid_generate(libxl_uuid *uuid);
-int libxl_uuid_from_string(libxl_uuid *uuid, const char *in);
-void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst,
-                     const libxl_uuid *src);
-#if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500
-static inline void libxl_uuid_copy_0x040400(libxl_uuid *dst,
-                                            const libxl_uuid *src)
-{
-    libxl_uuid_copy(NULL, dst, src);
-}
-#define libxl_uuid_copy libxl_uuid_copy_0x040400
-#endif
-
-void libxl_uuid_clear(libxl_uuid *uuid);
-int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2);
-const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid);
-uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid);
-
-#endif /* __LIBXL_UUID_H__ */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_vdispl.c b/tools/libxl/libxl_vdispl.c
deleted file mode 100644 (file)
index 8ddc894..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2016 EPAM Systems Inc.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_internal.h"
-
-#include <xen/io/displif.h>
-
-static int libxl__device_vdispl_setdefault(libxl__gc *gc, uint32_t domid,
-                                           libxl_device_vdispl *vdispl,
-                                           bool hotplug)
-{
-    return libxl__resolve_domid(gc, vdispl->backend_domname,
-                                &vdispl->backend_domid);
-}
-
-static int libxl__vdispl_from_xenstore(libxl__gc *gc, const char *libxl_path,
-                                       libxl_devid devid,
-                                       libxl_device_vdispl *vdispl)
-{
-    const char *be_path;
-    int rc;
-
-    vdispl->devid = devid;
-    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
-                                  GCSPRINTF("%s/backend", libxl_path),
-                                  &be_path);
-    if (rc) return rc;
-
-    return libxl__backendpath_parse_domid(gc, be_path, &vdispl->backend_domid);
-}
-
-static void libxl__update_config_vdispl(libxl__gc *gc,
-                                        libxl_device_vdispl *dst,
-                                        libxl_device_vdispl *src)
-{
-    dst->devid = src->devid;
-    dst->be_alloc = src->be_alloc;
-}
-
-static int libxl_device_vdispl_compare(const libxl_device_vdispl *d1,
-                                       const libxl_device_vdispl *d2)
-{
-    return COMPARE_DEVID(d1, d2);
-}
-
-static void libxl__device_vdispl_add(libxl__egc *egc, uint32_t domid,
-                                     libxl_device_vdispl *vdispl,
-                                     libxl__ao_device *aodev)
-{
-    libxl__device_add_async(egc, domid, &libxl__vdispl_devtype, vdispl, aodev);
-}
-
-static int libxl__set_xenstore_vdispl(libxl__gc *gc, uint32_t domid,
-                                      libxl_device_vdispl *vdispl,
-                                      flexarray_t *back, flexarray_t *front,
-                                      flexarray_t *ro_front)
-{
-    int i;
-
-    flexarray_append_pair(ro_front, XENDISPL_FIELD_BE_ALLOC,
-                          GCSPRINTF("%d", vdispl->be_alloc));
-
-    for (i = 0; i < vdispl->num_connectors; i++) {
-        flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_RESOLUTION, i),
-                              GCSPRINTF("%d"XENDISPL_RESOLUTION_SEPARATOR"%d", vdispl->connectors[i].width,
-                                                 vdispl->connectors[i].height));
-        flexarray_append_pair(ro_front, GCSPRINTF("%d/"XENDISPL_FIELD_UNIQUE_ID, i),
-                              vdispl->connectors[i].unique_id);
-    }
-
-    return 0;
-}
-
-static int libxl__device_vdispl_getconnectors(libxl_ctx *ctx,
-                                              const char *path,
-                                              libxl_vdisplinfo *info)
-{
-    GC_INIT(ctx);
-    char *connector = NULL;
-    char *connector_path;
-    int i, rc;
-
-    info->num_connectors = 0;
-
-    connector_path = GCSPRINTF("%s/%d", path, info->num_connectors);
-
-    while ((connector = xs_read(ctx->xsh, XBT_NULL, connector_path, NULL)) !=
-           NULL) {
-        free(connector);
-        connector_path = GCSPRINTF("%s/%d", path, ++info->num_connectors);
-    }
-
-    info->connectors = libxl__calloc(NOGC, info->num_connectors,
-                                     sizeof(*info->connectors));
-
-    for (i = 0; i < info->num_connectors; i++) {
-        char *value;
-        char *value_path;
-
-        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_UNIQUE_ID, path, i);
-        info->connectors[i].unique_id = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
-        if (info->connectors[i].unique_id == NULL) { rc = ERROR_FAIL; goto out; }
-
-        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_RESOLUTION, path, i);
-        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
-        if (value == NULL) { rc = ERROR_FAIL; goto out; }
-
-        rc = sscanf(value, "%u"XENDISPL_RESOLUTION_SEPARATOR"%u", &info->connectors[i].width,
-                    &info->connectors[i].height);
-        free(value);
-
-        if (rc != 2) {
-            rc = ERROR_FAIL; goto out;
-        }
-
-        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_RING_REF, path, i);
-        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
-        info->connectors[i].req_rref = value ? strtoul(value, NULL, 10) : -1;
-        free(value);
-
-        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_REQ_CHANNEL, path, i);
-        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
-        info->connectors[i].req_evtch = value ? strtoul(value, NULL, 10) : -1;
-        free(value);
-
-        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_RING_REF, path, i);
-        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
-        info->connectors[i].evt_rref = value ? strtoul(value, NULL, 10) : -1;
-        free(value);
-
-        value_path = GCSPRINTF("%s/%d/"XENDISPL_FIELD_EVT_CHANNEL, path, i);
-        value = xs_read(ctx->xsh, XBT_NULL, value_path, NULL);
-        info->connectors[i].evt_evtch = value ? strtoul(value, NULL, 10) : -1;
-        free(value);
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-int libxl_device_vdispl_getinfo(libxl_ctx *ctx, uint32_t domid,
-                                const libxl_device_vdispl *vdispl,
-                                libxl_vdisplinfo *info)
-{
-    GC_INIT(ctx);
-    char *libxl_path, *devpath;
-    char *val;
-    int rc;
-
-    libxl_vdisplinfo_init(info);
-    info->devid = vdispl->devid;
-
-    devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
-                                                 LIBXL__DEVICE_KIND_VDISPL);
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
-                                                 LIBXL__DEVICE_KIND_VDISPL);
-
-    info->backend = xs_read(ctx->xsh, XBT_NULL,
-                            GCSPRINTF("%s/backend", libxl_path),
-                            NULL);
-    if (!info->backend) { rc = ERROR_FAIL; goto out; }
-
-    rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
-    if (rc) goto out;
-
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
-    info->state = val ? strtoul(val, NULL, 10) : -1;
-
-    info->frontend = xs_read(ctx->xsh, XBT_NULL,
-                             GCSPRINTF("%s/frontend", libxl_path),
-                             NULL);
-    info->frontend_id = domid;
-
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/"XENDISPL_FIELD_BE_ALLOC, devpath));
-    info->be_alloc = val ? strtoul(val, NULL, 10) : 0;
-
-    rc = libxl__device_vdispl_getconnectors(ctx, devpath, info);
-    if (rc) goto out;
-
-    rc = 0;
-
-out:
-     GC_FREE;
-     return rc;
-}
-
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(vdispl)
-static LIBXL_DEFINE_UPDATE_DEVID(vdispl)
-static LIBXL_DEFINE_DEVICES_ADD(vdispl)
-
-LIBXL_DEFINE_DEVID_TO_DEVICE(vdispl)
-LIBXL_DEFINE_DEVICE_ADD(vdispl)
-LIBXL_DEFINE_DEVICE_REMOVE(vdispl)
-LIBXL_DEFINE_DEVICE_LIST(vdispl)
-
-DEFINE_DEVICE_TYPE_STRUCT(vdispl, VDISPL,
-    .update_config = (device_update_config_fn_t)libxl__update_config_vdispl,
-    .from_xenstore = (device_from_xenstore_fn_t)libxl__vdispl_from_xenstore,
-    .set_xenstore_config = (device_set_xenstore_config_fn_t)
-                           libxl__set_xenstore_vdispl
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_vkb.c b/tools/libxl/libxl_vkb.c
deleted file mode 100644 (file)
index 4c44a81..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2016 EPAM Systems Inc.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_internal.h"
-
-#include <xen/io/kbdif.h>
-
-static int libxl__device_vkb_setdefault(libxl__gc *gc, uint32_t domid,
-                                        libxl_device_vkb *vkb, bool hotplug)
-{
-    if (vkb->backend_type == LIBXL_VKB_BACKEND_UNKNOWN) {
-        vkb->backend_type = LIBXL_VKB_BACKEND_QEMU;
-    }
-
-    return libxl__resolve_domid(gc, vkb->backend_domname, &vkb->backend_domid);
-}
-
-static int libxl__device_vkb_dm_needed(void *e, uint32_t domid)
-{
-    libxl_device_vkb *elem = e;
-
-    return elem->backend_type == LIBXL_VKB_BACKEND_QEMU;
-}
-
-static int libxl__set_xenstore_vkb(libxl__gc *gc, uint32_t domid,
-                                   libxl_device_vkb *vkb,
-                                   flexarray_t *back, flexarray_t *front,
-                                   flexarray_t *ro_front)
-{
-    if (vkb->unique_id) {
-        flexarray_append_pair(back, XENKBD_FIELD_UNIQUE_ID, vkb->unique_id);
-    }
-
-    if (vkb->feature_disable_keyboard) {
-        flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_KEYBRD,
-                              GCSPRINTF("%u", vkb->feature_disable_keyboard));
-    }
-
-    if (vkb->feature_disable_pointer) {
-        flexarray_append_pair(back, XENKBD_FIELD_FEAT_DSBL_POINTER,
-                              GCSPRINTF("%u", vkb->feature_disable_pointer));
-    }
-
-    if (vkb->feature_abs_pointer) {
-        flexarray_append_pair(back, XENKBD_FIELD_FEAT_ABS_POINTER,
-                              GCSPRINTF("%u", vkb->feature_abs_pointer));
-    }
-
-    if (vkb->feature_raw_pointer) {
-        flexarray_append_pair(back, XENKBD_FIELD_FEAT_RAW_POINTER,
-                              GCSPRINTF("%u", vkb->feature_raw_pointer));
-    }
-
-    if (vkb->feature_multi_touch) {
-        flexarray_append_pair(back, XENKBD_FIELD_FEAT_MTOUCH,
-                              GCSPRINTF("%u", vkb->feature_multi_touch));
-        flexarray_append_pair(back, XENKBD_FIELD_MT_WIDTH,
-                              GCSPRINTF("%u", vkb->multi_touch_width));
-        flexarray_append_pair(back, XENKBD_FIELD_MT_HEIGHT,
-                              GCSPRINTF("%u", vkb->multi_touch_height));
-        flexarray_append_pair(back, XENKBD_FIELD_MT_NUM_CONTACTS,
-                              GCSPRINTF("%u", vkb->multi_touch_num_contacts));
-    }
-
-    if (vkb->width) {
-        flexarray_append_pair(back, XENKBD_FIELD_WIDTH,
-                              GCSPRINTF("%u", vkb->width));
-    }
-
-    if (vkb->height) {
-        flexarray_append_pair(back, XENKBD_FIELD_HEIGHT,
-                              GCSPRINTF("%u", vkb->height));
-    }
-
-    return 0;
-}
-
-static int libxl__vkb_from_xenstore(libxl__gc *gc, const char *libxl_path,
-                                    libxl_devid devid,
-                                    libxl_device_vkb *vkb)
-{
-    const char *be_path, *fe_path, *tmp;
-    libxl__device dev;
-    int rc;
-
-    vkb->devid = devid;
-
-    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
-                                  GCSPRINTF("%s/backend", libxl_path),
-                                  &be_path);
-    if (rc) goto out;
-
-    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
-                                  GCSPRINTF("%s/frontend", libxl_path),
-                                  &fe_path);
-    if (rc) goto out;
-
-    rc = libxl__backendpath_parse_domid(gc, be_path, &vkb->backend_domid);
-    if (rc) goto out;
-
-    rc = libxl__parse_backend_path(gc, be_path, &dev);
-    if (rc) goto out;
-
-    vkb->backend_type = dev.backend_kind == LIBXL__DEVICE_KIND_VINPUT ?
-                                            LIBXL_VKB_BACKEND_LINUX : LIBXL_VKB_BACKEND_QEMU;
-
-    vkb->unique_id = xs_read(CTX->xsh, XBT_NULL, GCSPRINTF("%s/"XENKBD_FIELD_UNIQUE_ID, be_path), NULL);
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_KEYBRD,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->feature_disable_keyboard = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_DSBL_POINTER,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->feature_disable_pointer = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_ABS_POINTER,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->feature_abs_pointer = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_RAW_POINTER,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->feature_raw_pointer = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_FEAT_MTOUCH,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->feature_multi_touch = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_MT_WIDTH,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->multi_touch_width = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_MT_HEIGHT,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->multi_touch_height = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_MT_NUM_CONTACTS,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->multi_touch_num_contacts = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_WIDTH,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->width = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENKBD_FIELD_HEIGHT,
-                                be_path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        vkb->height = strtoul(tmp, NULL, 0);
-    }
-
-    rc = 0;
-
-out:
-
-    return rc;
-}
-
-static int libxl__device_from_vkb(libxl__gc *gc, uint32_t domid,
-                                  libxl_device_vkb *type, libxl__device *device)
-{
-    device->backend_devid   = type->devid;
-    device->backend_domid   = type->backend_domid;
-    device->backend_kind    = type->backend_type == LIBXL_VKB_BACKEND_LINUX ?
-                              LIBXL__DEVICE_KIND_VINPUT : LIBXL__DEVICE_KIND_VKBD;
-    device->devid           = type->devid;
-    device->domid           = domid;
-    device->kind            = LIBXL__DEVICE_KIND_VKBD;
-
-    return 0;
-}
-
-int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
-                         const libxl_asyncop_how *ao_how)
-{
-    AO_CREATE(ctx, domid, ao_how);
-    int rc;
-
-    rc = libxl__device_add(gc, domid, &libxl__vkb_devtype, vkb);
-    if (rc) {
-        LOGD(ERROR, domid, "Unable to add vkb device");
-        goto out;
-    }
-
-out:
-    libxl__ao_complete(egc, ao, rc);
-    return AO_INPROGRESS;
-}
-
-int libxl_devid_to_device_vkb(libxl_ctx *ctx, uint32_t domid,
-                              int devid, libxl_device_vkb *vkb)
-{
-    GC_INIT(ctx);
-
-    libxl_device_vkb *vkbs = NULL;
-    int n, i;
-    int rc;
-
-    libxl_device_vkb_init(vkb);
-
-    vkbs = libxl__device_list(gc, &libxl__vkb_devtype, domid, &n);
-
-    if (!vkbs) { rc = ERROR_NOTFOUND; goto out; }
-
-    for (i = 0; i < n; ++i) {
-        if (devid == vkbs[i].devid) {
-            libxl_device_vkb_copy(ctx, vkb, &vkbs[i]);
-            rc = 0;
-            goto out;
-        }
-    }
-
-    rc = ERROR_NOTFOUND;
-
-out:
-
-    if (vkbs)
-        libxl__device_list_free(&libxl__vkb_devtype, vkbs, n);
-
-    GC_FREE;
-    return rc;
-}
-
-int libxl_device_vkb_getinfo(libxl_ctx *ctx, uint32_t domid,
-                             const libxl_device_vkb *vkb,
-                             libxl_vkbinfo *info)
-{
-    GC_INIT(ctx);
-    char *libxl_path, *dompath, *devpath;
-    char *val;
-    int rc;
-
-    libxl_vkbinfo_init(info);
-    dompath = libxl__xs_get_dompath(gc, domid);
-    info->devid = vkb->devid;
-
-    devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
-                                                 LIBXL__DEVICE_KIND_VKBD);
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
-                                                 LIBXL__DEVICE_KIND_VKBD);
-
-    info->backend = xs_read(ctx->xsh, XBT_NULL,
-                            GCSPRINTF("%s/backend", libxl_path),
-                            NULL);
-    if (!info->backend) { rc = ERROR_FAIL; goto out; }
-
-    rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
-    if (rc) goto out;
-
-    val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", devpath));
-    info->state = val ? strtoul(val, NULL, 10) : -1;
-
-    info->frontend = xs_read(ctx->xsh, XBT_NULL,
-                             GCSPRINTF("%s/frontend", libxl_path),
-                             NULL);
-    info->frontend_id = domid;
-
-    val = libxl__xs_read(gc, XBT_NULL,
-          GCSPRINTF("%s/"XENKBD_FIELD_EVT_CHANNEL, devpath));
-    info->evtch = val ? strtoul(val, NULL, 10) : -1;
-
-    val = libxl__xs_read(gc, XBT_NULL,
-          GCSPRINTF("%s/"XENKBD_FIELD_RING_GREF, devpath));
-    info->rref = val ? strtoul(val, NULL, 10) : -1;
-
-    rc = 0;
-
-out:
-     GC_FREE;
-     return rc;
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(vkb)
-
-#define libxl__add_vkbs NULL
-#define libxl_device_vkb_compare NULL
-
-LIBXL_DEFINE_DEVICE_LIST(vkb)
-LIBXL_DEFINE_DEVICE_REMOVE(vkb)
-
-DEFINE_DEVICE_TYPE_STRUCT(vkb, VKBD,
-    .skip_attach = 1,
-    .dm_needed = libxl__device_vkb_dm_needed,
-    .set_xenstore_config = (device_set_xenstore_config_fn_t)
-                           libxl__set_xenstore_vkb,
-    .from_xenstore = (device_from_xenstore_fn_t)libxl__vkb_from_xenstore
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_vnuma.c b/tools/libxl/libxl_vnuma.c
deleted file mode 100644 (file)
index c2e144c..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2014      Citrix Ltd.
- * Author Wei Liu <wei.liu2@citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-#include "libxl_osdeps.h" /* must come before any other headers */
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-#include <stdlib.h>
-
-#include <xenctrl_dom.h>
-
-bool libxl__vnuma_configured(const libxl_domain_build_info *b_info)
-{
-    return b_info->num_vnuma_nodes != 0;
-}
-
-/* Sort vmemranges in ascending order with "start" */
-static int compare_vmemrange(const void *a, const void *b)
-{
-    const xen_vmemrange_t *x = a, *y = b;
-    if (x->start < y->start)
-        return -1;
-    if (x->start > y->start)
-        return 1;
-    return 0;
-}
-
-/* Check if a vcpu has an hard (or soft) affinity set in such
- * a way that it does not match the pnode to which the vcpu itself
- * is assigned to.
- */
-static int check_vnuma_affinity(libxl__gc *gc,
-                                 unsigned int vcpu,
-                                 unsigned int pnode,
-                                 unsigned int num_affinity,
-                                 const libxl_bitmap *affinity,
-                                 const char *kind)
-{
-    libxl_bitmap nodemap;
-    int rc = 0;
-
-    libxl_bitmap_init(&nodemap);
-
-    rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0);
-    if (rc) {
-        LOG(ERROR, "Can't allocate nodemap");
-        goto out;
-    }
-
-    rc = libxl_cpumap_to_nodemap(CTX, affinity, &nodemap);
-    if (rc) {
-        LOG(ERROR, "Can't convert Vcpu %d affinity to nodemap", vcpu);
-        goto out;
-    }
-
-    if (libxl_bitmap_count_set(&nodemap) != 1 ||
-        !libxl_bitmap_test(&nodemap, pnode))
-        LOG(WARN, "Vcpu %d %s affinity and vnuma info mismatch", vcpu, kind);
-
-out:
-    libxl_bitmap_dispose(&nodemap);
-    return rc;
-}
-
-/* Check if vNUMA configuration is valid:
- *  1. all pnodes inside vnode_to_pnode array are valid
- *  2. each vcpu belongs to one and only one vnode
- *  3. each vmemrange is valid and doesn't overlap with any other
- *  4. local distance cannot be larger than remote distance
- *
- * Check also, if any hard or soft affinity is specified, whether
- * they match with the vNUMA related bits (namely vcpus to vnodes
- * mappings and vnodes to pnodes association). If that does not
- * hold, however, just print a warning, as that has "only"
- * performance implications.
- */
-int libxl__vnuma_config_check(libxl__gc *gc,
-                              const libxl_domain_build_info *b_info,
-                              const libxl__domain_build_state *state)
-{
-    int nr_nodes = 0, rc = ERROR_VNUMA_CONFIG_INVALID;
-    unsigned int i, j;
-    libxl_numainfo *ninfo = NULL;
-    uint64_t total_memkb = 0;
-    libxl_bitmap cpumap;
-    libxl_vnode_info *v;
-
-    libxl_bitmap_init(&cpumap);
-
-    /* Check pnode specified is valid */
-    ninfo = libxl_get_numainfo(CTX, &nr_nodes);
-    if (!ninfo) {
-        LOG(ERROR, "libxl_get_numainfo failed");
-        goto out;
-    }
-
-    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
-        uint32_t pnode;
-
-        v = &b_info->vnuma_nodes[i];
-        pnode = v->pnode;
-
-        /* The pnode specified is not valid? */
-        if (pnode >= nr_nodes) {
-            LOG(ERROR, "Invalid pnode %"PRIu32" specified", pnode);
-            goto out;
-        }
-
-        total_memkb += v->memkb;
-    }
-
-    if (total_memkb != b_info->max_memkb) {
-        LOG(ERROR, "Amount of memory mismatch (0x%"PRIx64" != 0x%"PRIx64")",
-            total_memkb, b_info->max_memkb);
-        goto out;
-    }
-
-    /* Check vcpu mapping */
-    libxl_cpu_bitmap_alloc(CTX, &cpumap, b_info->max_vcpus);
-    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
-        v = &b_info->vnuma_nodes[i];
-        libxl_for_each_set_bit(j, v->vcpus) {
-            if (!libxl_bitmap_test(&cpumap, j))
-                libxl_bitmap_set(&cpumap, j);
-            else {
-                LOG(ERROR, "Vcpu %d assigned more than once", j);
-                goto out;
-            }
-        }
-    }
-
-    for (i = 0; i < b_info->max_vcpus; i++) {
-        if (!libxl_bitmap_test(&cpumap, i)) {
-            LOG(ERROR, "Vcpu %d is not assigned to any vnode", i);
-            goto out;
-        }
-    }
-
-    /* Check whether vcpu affinity (if any) matches vnuma configuration */
-    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
-        v = &b_info->vnuma_nodes[i];
-        libxl_for_each_set_bit(j, v->vcpus) {
-            if (b_info->num_vcpu_hard_affinity > j)
-                check_vnuma_affinity(gc, j, v->pnode,
-                                     b_info->num_vcpu_hard_affinity,
-                                     &b_info->vcpu_hard_affinity[j],
-                                     "hard");
-            if (b_info->num_vcpu_soft_affinity > j)
-                check_vnuma_affinity(gc, j, v->pnode,
-                                     b_info->num_vcpu_soft_affinity,
-                                     &b_info->vcpu_soft_affinity[j],
-                                     "soft");
-        }
-    }
-
-    /* Check vmemranges */
-    qsort(state->vmemranges, state->num_vmemranges, sizeof(xen_vmemrange_t),
-          compare_vmemrange);
-
-    for (i = 0; i < state->num_vmemranges; i++) {
-        if (state->vmemranges[i].end < state->vmemranges[i].start) {
-                LOG(ERROR, "Vmemrange end < start");
-                goto out;
-        }
-    }
-
-    for (i = 0; i < state->num_vmemranges - 1; i++) {
-        if (state->vmemranges[i].end > state->vmemranges[i+1].start) {
-            LOG(ERROR,
-                "Vmemranges overlapped, 0x%"PRIx64"-0x%"PRIx64", 0x%"PRIx64"-0x%"PRIx64,
-                state->vmemranges[i].start, state->vmemranges[i].end,
-                state->vmemranges[i+1].start, state->vmemranges[i+1].end);
-            goto out;
-        }
-    }
-
-    /* Check vdistances */
-    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
-        uint32_t local_distance, remote_distance;
-
-        v = &b_info->vnuma_nodes[i];
-        local_distance = v->distances[i];
-
-        for (j = 0; j < v->num_distances; j++) {
-            if (i == j) continue;
-            remote_distance = v->distances[j];
-            if (local_distance > remote_distance) {
-                LOG(ERROR,
-                    "Distance from %u to %u smaller than %u's local distance",
-                    i, j, i);
-                goto out;
-            }
-        }
-    }
-
-    rc = 0;
-out:
-    libxl_numainfo_list_free(ninfo, nr_nodes);
-    libxl_bitmap_dispose(&cpumap);
-    return rc;
-}
-
-int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc,
-                                            uint32_t domid,
-                                            libxl_domain_build_info *b_info,
-                                            libxl__domain_build_state *state)
-{
-    int i;
-    uint64_t next;
-    xen_vmemrange_t *v = NULL;
-
-    /* Generate one vmemrange for each virtual node. */
-    GCREALLOC_ARRAY(v, b_info->num_vnuma_nodes);
-    next = 0;
-    for (i = 0; i < b_info->num_vnuma_nodes; i++) {
-        libxl_vnode_info *p = &b_info->vnuma_nodes[i];
-
-        v[i].start = next;
-        v[i].end = next + (p->memkb << 10);
-        v[i].flags = 0;
-        v[i].nid = i;
-
-        next = v[i].end;
-    }
-
-    state->vmemranges = v;
-    state->num_vmemranges = i;
-
-    return 0;
-}
-
-/* Build vmemranges for PV guest */
-int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc,
-                                    uint32_t domid,
-                                    libxl_domain_build_info *b_info,
-                                    libxl__domain_build_state *state)
-{
-    assert(state->vmemranges == NULL);
-    return libxl__arch_vnuma_build_vmemrange(gc, domid, b_info, state);
-}
-
-/* Build vmemranges for HVM guest */
-int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc,
-                                     uint32_t domid,
-                                     libxl_domain_build_info *b_info,
-                                     libxl__domain_build_state *state,
-                                     struct xc_dom_image *dom)
-{
-    uint64_t hole_start, hole_end, next;
-    int nid, nr_vmemrange;
-    xen_vmemrange_t *vmemranges;
-    int rc;
-
-    /* Derive vmemranges from vnode size and memory hole.
-     *
-     * Guest physical address space layout:
-     * [0, hole_start) [hole_start, hole_end) [hole_end, highmem_end)
-     */
-    hole_start = dom->lowmem_end < dom->mmio_start ?
-        dom->lowmem_end : dom->mmio_start;
-    hole_end = (dom->mmio_start + dom->mmio_size) > (1ULL << 32) ?
-        (dom->mmio_start + dom->mmio_size) : (1ULL << 32);
-
-    assert(state->vmemranges == NULL);
-
-    next = 0;
-    nr_vmemrange = 0;
-    vmemranges = NULL;
-    for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
-        libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
-        uint64_t remaining_bytes = p->memkb << 10;
-
-        /* Consider video ram belongs to vnode 0 */
-        if (nid == 0) {
-            if (p->memkb < b_info->video_memkb) {
-                LOGD(ERROR, domid, "vnode 0 too small to contain video ram");
-                rc = ERROR_INVAL;
-                goto out;
-            }
-            remaining_bytes -= (b_info->video_memkb << 10);
-        }
-
-        while (remaining_bytes > 0) {
-            uint64_t count = remaining_bytes;
-
-            if (next >= hole_start && next < hole_end)
-                next = hole_end;
-            if ((next < hole_start) && (next + remaining_bytes >= hole_start))
-                count = hole_start - next;
-
-            GCREALLOC_ARRAY(vmemranges, nr_vmemrange+1);
-            vmemranges[nr_vmemrange].start = next;
-            vmemranges[nr_vmemrange].end = next + count;
-            vmemranges[nr_vmemrange].flags = 0;
-            vmemranges[nr_vmemrange].nid = nid;
-
-            nr_vmemrange++;
-            remaining_bytes -= count;
-            next += count;
-        }
-    }
-
-    state->vmemranges = vmemranges;
-    state->num_vmemranges = nr_vmemrange;
-
-    rc = 0;
-out:
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_vsnd.c b/tools/libxl/libxl_vsnd.c
deleted file mode 100644 (file)
index 0bc5f6d..0000000
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * Copyright (C) 2016 EPAM Systems Inc.
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_internal.h"
-
-#include <xen/io/sndif.h>
-
-static int libxl__device_vsnd_setdefault(libxl__gc *gc, uint32_t domid,
-                                         libxl_device_vsnd *vsnd,
-                                         bool hotplug)
-{
-    return libxl__resolve_domid(gc, vsnd->backend_domname,
-                                &vsnd->backend_domid);
-}
-
-static int libxl__device_from_vsnd(libxl__gc *gc, uint32_t domid,
-                                   libxl_device_vsnd *vsnd,
-                                   libxl__device *device)
-{
-   device->backend_devid   = vsnd->devid;
-   device->backend_domid   = vsnd->backend_domid;
-   device->backend_kind    = LIBXL__DEVICE_KIND_VSND;
-   device->devid           = vsnd->devid;
-   device->domid           = domid;
-   device->kind            = LIBXL__DEVICE_KIND_VSND;
-
-   return 0;
-}
-
-static int libxl__sample_rates_from_string(libxl__gc *gc, const char *str,
-                                           libxl_vsnd_params *params)
-{
-    char *tmp = libxl__strdup(gc, str);
-
-    params->num_sample_rates = 0;
-    params->sample_rates = NULL;
-
-    char *p = strtok(tmp, " ,");
-
-    while (p != NULL) {
-        params->sample_rates = libxl__realloc(NOGC, params->sample_rates,
-                                              sizeof(*params->sample_rates) *
-                                              (params->num_sample_rates + 1));
-        params->sample_rates[params->num_sample_rates++] = strtoul(p, NULL, 0);
-        p = strtok(NULL, " ,");
-    }
-
-    return 0;
-}
-
-static int libxl__sample_formats_from_string(libxl__gc *gc, const char *str,
-                                             libxl_vsnd_params *params)
-{
-    int rc;
-    char *tmp = libxl__strdup(gc, str);
-
-    params->num_sample_formats = 0;
-    params->sample_formats = NULL;
-
-    char *p = strtok(tmp, " ,");
-
-    while (p != NULL) {
-        params->sample_formats = libxl__realloc(NOGC, params->sample_formats,
-                                                sizeof(*params->sample_formats) *
-                                                (params->num_sample_formats + 1));
-
-        libxl_vsnd_pcm_format format;
-
-        rc = libxl_vsnd_pcm_format_from_string(p, &format);
-        if (rc) goto out;
-
-        params->sample_formats[params->num_sample_formats++] = format;
-        p = strtok(NULL, " ,");
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__params_from_xenstore(libxl__gc *gc, const char *path,
-                                       libxl_vsnd_params *params)
-{
-    const char *tmp;
-    int rc;
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_RATES,
-                                          path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        rc = libxl__sample_rates_from_string(gc, tmp, params);
-        if (rc) goto out;
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENSND_FIELD_SAMPLE_FORMATS,
-                                          path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        rc = libxl__sample_formats_from_string(gc, tmp, params);
-        if (rc) goto out;
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                 GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MIN,
-                                           path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        params->channels_min = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                 GCSPRINTF("%s/"XENSND_FIELD_CHANNELS_MAX,
-                                           path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        params->channels_max = strtoul(tmp, NULL, 0);
-    }
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                 GCSPRINTF("%s/"XENSND_FIELD_BUFFER_SIZE,
-                                           path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        params->buffer_size = strtoul(tmp, NULL, 0);
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__stream_from_xenstore(libxl__gc *gc, const char *path,
-                                       libxl_vsnd_stream *stream)
-{
-    const char *tmp;
-    int rc;
-
-    stream->unique_id = xs_read(CTX->xsh, XBT_NULL,
-                         GCSPRINTF("%s/"XENSND_FIELD_STREAM_UNIQUE_ID,
-                         path), NULL);
-
-    rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                GCSPRINTF("%s/"XENSND_FIELD_TYPE,
-                                          path), &tmp);
-    if (rc) goto out;
-
-    if (tmp) {
-        libxl_vsnd_stream_type type;
-
-        rc = libxl_vsnd_stream_type_from_string(tmp, &type);
-        if (rc) goto out;
-
-        stream->type = type;
-    }
-
-    rc = libxl__params_from_xenstore(gc, path, &stream->params);
-    if (rc) goto out;
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__pcm_from_xenstore(libxl__gc *gc, const char *path,
-                                    libxl_vsnd_pcm *pcm)
-{
-    const char *tmp;
-    int rc;
-
-    pcm->name = xs_read(CTX->xsh, XBT_NULL,
-                        GCSPRINTF("%s/"XENSND_FIELD_DEVICE_NAME, path), NULL);
-
-    rc = libxl__params_from_xenstore(gc, path, &pcm->params);
-    if (rc) goto out;
-
-    pcm->streams = NULL;
-    pcm->num_vsnd_streams = 0;
-
-    do {
-        char *stream_path = GCSPRINTF("%s/%d", path, pcm->num_vsnd_streams);
-
-        rc = libxl__xs_read_checked(gc, XBT_NULL, stream_path, &tmp);
-        if (rc) goto out;
-
-        if (tmp) {
-            pcm->streams = libxl__realloc(NOGC, pcm->streams,
-                                          sizeof(*pcm->streams) *
-                                          (++pcm->num_vsnd_streams));
-
-            libxl_vsnd_stream_init(&pcm->streams[pcm->num_vsnd_streams - 1]);
-
-            rc = libxl__stream_from_xenstore(gc, stream_path,
-                                             &pcm->streams[pcm->num_vsnd_streams
-                                             - 1]);
-            if (rc) goto out;
-        }
-    } while (tmp);
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__vsnd_from_xenstore(libxl__gc *gc, const char *libxl_path,
-                                     libxl_devid devid,
-                                     libxl_device_vsnd *vsnd)
-{
-    const char *tmp;
-    const char *fe_path;
-    int rc;
-
-    vsnd->devid = devid;
-    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
-                                  GCSPRINTF("%s/backend", libxl_path),
-                                  &tmp);
-    if (rc) goto out;
-
-    rc = libxl__backendpath_parse_domid(gc, tmp, &vsnd->backend_domid);
-    if (rc) goto out;
-
-    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
-                                  GCSPRINTF("%s/frontend", libxl_path),
-                                  &fe_path);
-    if (rc) goto out;
-
-    vsnd->short_name = xs_read(CTX->xsh, XBT_NULL,
-                               GCSPRINTF("%s/"XENSND_FIELD_VCARD_SHORT_NAME,
-                               fe_path), NULL);
-
-    vsnd->long_name = xs_read(CTX->xsh, XBT_NULL,
-                              GCSPRINTF("%s/"XENSND_FIELD_VCARD_LONG_NAME,
-                              fe_path), NULL);
-
-    rc = libxl__params_from_xenstore(gc, fe_path, &vsnd->params);
-    if (rc) goto out;
-
-    vsnd->pcms = NULL;
-    vsnd->num_vsnd_pcms = 0;
-
-    do {
-        char *pcm_path = GCSPRINTF("%s/%d", fe_path, vsnd->num_vsnd_pcms);
-
-        rc = libxl__xs_read_checked(gc, XBT_NULL, pcm_path, &tmp);
-        if (rc) goto out;
-
-        if (tmp) {
-            vsnd->pcms = libxl__realloc(NOGC, vsnd->pcms, sizeof(*vsnd->pcms) *
-                                        (++vsnd->num_vsnd_pcms));
-
-            libxl_vsnd_pcm_init(&vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
-
-            rc = libxl__pcm_from_xenstore(gc, pcm_path,
-                                          &vsnd->pcms[vsnd->num_vsnd_pcms - 1]);
-            if (rc) goto out;
-        }
-    } while (tmp);
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static void libxl__update_config_vsnd(libxl__gc *gc,
-                                      libxl_device_vsnd *dst,
-                                      libxl_device_vsnd *src)
-{
-    dst->devid = src->devid;
-}
-
-static int libxl_device_vsnd_compare(const libxl_device_vsnd *d1,
-                                     const libxl_device_vsnd *d2)
-{
-    return COMPARE_DEVID(d1, d2);
-}
-
-static void libxl__device_vsnd_add(libxl__egc *egc, uint32_t domid,
-                                   libxl_device_vsnd *vsnd,
-                                   libxl__ao_device *aodev)
-{
-    libxl__device_add_async(egc, domid, &libxl__vsnd_devtype, vsnd, aodev);
-}
-
-static unsigned int libxl__rates_to_str_vsnd(char *str, uint32_t *sample_rates,
-                                             int num_sample_rates)
-{
-    unsigned int len;
-    int i;
-
-    len = 0;
-
-    if (num_sample_rates == 0) goto out;
-
-    for (i = 0; i < num_sample_rates - 1; i++) {
-        if (str) {
-            len += sprintf(&str[len], "%u,", sample_rates[i]);
-        } else {
-            len += snprintf(NULL, 0, "%u,", sample_rates[i]);
-        }
-    }
-
-    if (str) {
-        len += sprintf(&str[len], "%u", sample_rates[i]);
-    } else {
-        len += snprintf(NULL, 0, "%u", sample_rates[i]);
-    }
-
-out:
-    return len;
-}
-
-static unsigned int libxl__formats_to_str_vsnd(char *str,
-                                               libxl_vsnd_pcm_format *sample_formats,
-                                               int num_sample_formats)
-{
-    unsigned int len;
-    int i;
-
-    len = 0;
-
-    if (num_sample_formats == 0) goto out;
-
-    for (i = 0; i < num_sample_formats - 1; i++) {
-        if (str) {
-            len += sprintf(&str[len], "%s,",
-                           libxl_vsnd_pcm_format_to_string(sample_formats[i]));
-        } else {
-            len += snprintf(NULL, 0, "%s,",
-                            libxl_vsnd_pcm_format_to_string(sample_formats[i]));
-        }
-    }
-
-    if (str) {
-        len += sprintf(&str[len], "%s",
-                       libxl_vsnd_pcm_format_to_string(sample_formats[i]));
-    } else {
-        len += snprintf(NULL, 0, "%s",
-                        libxl_vsnd_pcm_format_to_string(sample_formats[i]));
-    }
-
-out:
-    return len;
-}
-
-static int libxl__set_params_vsnd(libxl__gc *gc, char *path,
-                                  libxl_vsnd_params *params, flexarray_t *front)
-{
-    char *buffer;
-    int len;
-    int rc;
-
-    if (params->sample_rates) {
-        /* calculate required string size */
-        len = libxl__rates_to_str_vsnd(NULL, params->sample_rates,
-                                       params->num_sample_rates);
-
-        if (len) {
-            buffer = libxl__malloc(gc, len + 1);
-
-            libxl__rates_to_str_vsnd(buffer, params->sample_rates,
-                                     params->num_sample_rates);
-            rc = flexarray_append_pair(front,
-                                       GCSPRINTF("%s"XENSND_FIELD_SAMPLE_RATES,
-                                                 path), buffer);
-            if (rc) goto out;
-        }
-    }
-
-    if (params->sample_formats) {
-        /* calculate required string size */
-        len = libxl__formats_to_str_vsnd(NULL, params->sample_formats,
-                                         params->num_sample_formats);
-
-        if (len) {
-            buffer = libxl__malloc(gc, len + 1);
-
-            libxl__formats_to_str_vsnd(buffer, params->sample_formats,
-                                     params->num_sample_formats);
-            rc = flexarray_append_pair(front,
-                                       GCSPRINTF("%s"XENSND_FIELD_SAMPLE_FORMATS,
-                                                 path), buffer);
-            if (rc) goto out;
-        }
-    }
-
-    if (params->channels_min) {
-        rc = flexarray_append_pair(front,
-                                   GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MIN, path),
-                                   GCSPRINTF("%u", params->channels_min));
-        if (rc) goto out;
-    }
-
-    if (params->channels_max) {
-        rc = flexarray_append_pair(front,
-                                   GCSPRINTF("%s"XENSND_FIELD_CHANNELS_MAX, path),
-                                   GCSPRINTF("%u", params->channels_max));
-        if (rc) goto out;
-    }
-
-    if (params->buffer_size) {
-        rc = flexarray_append_pair(front,
-                                   GCSPRINTF("%s"XENSND_FIELD_BUFFER_SIZE, path),
-                                   GCSPRINTF("%u", params->buffer_size));
-        if (rc) goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__set_streams_vsnd(libxl__gc *gc, char *path,
-                                   libxl_vsnd_stream *streams,
-                                   int num_streams, flexarray_t *front)
-{
-    int i;
-    int rc;
-
-    for (i = 0; i < num_streams; i++) {
-        rc = flexarray_append_pair(front,
-                 GCSPRINTF("%s%d/"XENSND_FIELD_STREAM_UNIQUE_ID, path, i),
-                 streams[i].unique_id);
-        if (rc) goto out;
-
-        const char *type = libxl_vsnd_stream_type_to_string(streams[i].type);
-
-        if (type) {
-            rc = flexarray_append_pair(front,
-                     GCSPRINTF("%s%d/"XENSND_FIELD_TYPE, path, i),
-                     (char *)type);
-            if (rc) goto out;
-        }
-
-        rc = libxl__set_params_vsnd(gc, GCSPRINTF("%s%d/", path, i),
-                                    &streams[i].params, front);
-        if (rc) goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__set_pcms_vsnd(libxl__gc *gc, libxl_vsnd_pcm *pcms,
-                                int num_pcms, flexarray_t *front)
-{
-    int i;
-    int rc;
-
-    for (i = 0; i < num_pcms; i++) {
-        if (pcms[i].name) {
-            rc = flexarray_append_pair(front,
-                                       GCSPRINTF("%d/"XENSND_FIELD_DEVICE_NAME, i),
-                                       pcms[i].name);
-            if (rc) goto out;
-        }
-
-        char *path = GCSPRINTF("%d/", i);
-
-        rc = libxl__set_params_vsnd(gc, path, &pcms[i].params, front);
-        if (rc) goto out;
-
-        rc = libxl__set_streams_vsnd(gc, path, pcms[i].streams,
-                                     pcms[i].num_vsnd_streams, front);
-        if (rc) goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__set_xenstore_vsnd(libxl__gc *gc, uint32_t domid,
-                                    libxl_device_vsnd *vsnd,
-                                    flexarray_t *back, flexarray_t *front,
-                                    flexarray_t *ro_front)
-{
-    int rc;
-
-    if (vsnd->long_name) {
-        rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_LONG_NAME,
-                                   vsnd->long_name);
-        if (rc) goto out;
-    }
-
-    if (vsnd->short_name) {
-        rc = flexarray_append_pair(front, XENSND_FIELD_VCARD_SHORT_NAME,
-                                   vsnd->short_name);
-        if (rc) goto out;
-    }
-
-    rc = libxl__set_params_vsnd(gc, "", &vsnd->params, front);
-    if (rc) goto out;
-
-    rc = libxl__set_pcms_vsnd(gc, vsnd->pcms, vsnd->num_vsnd_pcms, front);
-    if (rc) goto out;
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__device_stream_getinfo(libxl__gc *gc, const char *path,
-                                        libxl_vsnd_pcm* pcm,
-                                        libxl_pcminfo *info)
-{
-    const char *tmp;
-    int i;
-    int rc;
-
-    info->num_vsnd_streams = pcm->num_vsnd_streams;
-    info->streams = libxl__malloc(NOGC, sizeof(*info->streams) * info->num_vsnd_streams);
-
-    for (i = 0; i < info->num_vsnd_streams; i++)
-    {
-        libxl_streaminfo_init(&info->streams[i]);
-
-        rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                    GCSPRINTF("%s/%d/"XENSND_FIELD_RING_REF,
-                                    path, i), &tmp);
-        if (rc) goto out;
-
-        info->streams[i].req_rref = tmp ? strtoul(tmp, NULL, 10) : -1;
-
-        rc = libxl__xs_read_checked(gc, XBT_NULL,
-                                    GCSPRINTF("%s/%d/"XENSND_FIELD_EVT_CHNL,
-                                    path, i), &tmp);
-        if (rc) goto out;
-
-        info->streams[i].req_evtch = tmp ? strtoul(tmp, NULL, 10) : -1;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-static int libxl__device_pcm_getinfo(libxl__gc *gc, const char *path,
-                                     const libxl_device_vsnd *vsnd,
-                                     libxl_vsndinfo *info)
-{
-    int i;
-    int rc;
-
-    info->num_vsnd_pcms = vsnd->num_vsnd_pcms;
-    info->pcms = libxl__malloc(NOGC, sizeof(*info->pcms) * info->num_vsnd_pcms);
-
-    for (i = 0; i < info->num_vsnd_pcms; i++)
-    {
-        libxl_pcminfo_init(&info->pcms[i]);
-
-        rc = libxl__device_stream_getinfo(gc, GCSPRINTF("%s/%d", path, i),
-                                          &vsnd->pcms[i], &info->pcms[i]);
-        if (rc) goto out;
-    }
-
-    rc = 0;
-
-out:
-    return rc;
-}
-
-int libxl_device_vsnd_getinfo(libxl_ctx *ctx, uint32_t domid,
-                              const libxl_device_vsnd *vsnd,
-                              libxl_vsndinfo *info)
-{
-    GC_INIT(ctx);
-    char *libxl_path, *dompath, *devpath;
-    const char *val;
-    int rc;
-
-    libxl_vsndinfo_init(info);
-    dompath = libxl__xs_get_dompath(gc, domid);
-    info->devid = vsnd->devid;
-
-    devpath = libxl__domain_device_frontend_path(gc, domid, info->devid,
-                                                 LIBXL__DEVICE_KIND_VSND);
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, info->devid,
-                                                 LIBXL__DEVICE_KIND_VSND);
-
-    info->backend = xs_read(ctx->xsh, XBT_NULL,
-                            GCSPRINTF("%s/backend", libxl_path), NULL);
-
-    rc = libxl__backendpath_parse_domid(gc, info->backend, &info->backend_id);
-    if (rc) goto out;
-
-    val = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/state", devpath), NULL);
-
-    info->state = val ? strtoul(val, NULL, 10) : -1;
-
-    info->frontend = xs_read(ctx->xsh, XBT_NULL,
-                             GCSPRINTF("%s/frontend", libxl_path), NULL);
-
-    info->frontend_id = domid;
-
-    rc = libxl__device_pcm_getinfo(gc, devpath, vsnd, info);
-    if (rc) goto out;
-
-    rc = 0;
-
-out:
-     GC_FREE;
-     return rc;
-}
-
-int libxl_devid_to_device_vsnd(libxl_ctx *ctx, uint32_t domid,
-                               int devid, libxl_device_vsnd *vsnd)
-{
-    GC_INIT(ctx);
-
-    libxl_device_vsnd *vsnds = NULL;
-    int n, i;
-    int rc;
-
-    libxl_device_vsnd_init(vsnd);
-
-    vsnds = libxl__device_list(gc, &libxl__vsnd_devtype, domid, &n);
-
-    if (!vsnds) { rc = ERROR_NOTFOUND; goto out; }
-
-    for (i = 0; i < n; ++i) {
-        if (devid == vsnds[i].devid) {
-            libxl_device_vsnd_copy(ctx, vsnd, &vsnds[i]);
-            rc = 0;
-            goto out;
-        }
-    }
-
-    rc = ERROR_NOTFOUND;
-
-out:
-    if (vsnds)
-        libxl__device_list_free(&libxl__vsnd_devtype, vsnds, n);
-
-    GC_FREE;
-    return rc;
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(vsnd)
-static LIBXL_DEFINE_DEVICES_ADD(vsnd)
-
-LIBXL_DEFINE_DEVICE_ADD(vsnd)
-LIBXL_DEFINE_DEVICE_REMOVE(vsnd)
-LIBXL_DEFINE_DEVICE_LIST(vsnd)
-
-DEFINE_DEVICE_TYPE_STRUCT(vsnd, VSND,
-    .update_config = (device_update_config_fn_t) libxl__update_config_vsnd,
-    .from_xenstore = (device_from_xenstore_fn_t) libxl__vsnd_from_xenstore,
-    .set_xenstore_config = (device_set_xenstore_config_fn_t)
-                           libxl__set_xenstore_vsnd
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_vtpm.c b/tools/libxl/libxl_vtpm.c
deleted file mode 100644 (file)
index dd00b26..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2016      SUSE Linux GmbH
- * Author Juergen Gross <jgross@suse.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include "libxl_internal.h"
-
-static int libxl__device_vtpm_setdefault(libxl__gc *gc, uint32_t domid,
-                                         libxl_device_vtpm *vtpm, bool hotplug)
-{
-    int rc;
-    if (libxl_uuid_is_nil(&vtpm->uuid)) {
-        libxl_uuid_generate(&vtpm->uuid);
-    }
-    rc = libxl__resolve_domid(gc, vtpm->backend_domname, &vtpm->backend_domid);
-    return rc;
-}
-
-static void libxl__update_config_vtpm(libxl__gc *gc, libxl_device_vtpm *dst,
-                                      libxl_device_vtpm *src)
-{
-    dst->devid = src->devid;
-    libxl_uuid_copy(CTX, &dst->uuid, &src->uuid);
-}
-
-static int libxl__set_xenstore_vtpm(libxl__gc *gc, uint32_t domid,
-                                    libxl_device_vtpm *vtpm,
-                                    flexarray_t *back, flexarray_t *front,
-                                    flexarray_t *ro_front)
-{
-    flexarray_append_pair(back, "handle", GCSPRINTF("%d", vtpm->devid));
-    flexarray_append_pair(back, "uuid",
-                          GCSPRINTF(LIBXL_UUID_FMT,
-                                    LIBXL_UUID_BYTES(vtpm->uuid)));
-    flexarray_append_pair(back, "resume", "False");
-
-    flexarray_append_pair(front, "handle", GCSPRINTF("%d", vtpm->devid));
-
-    return 0;
-}
-
-static void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid,
-                                   libxl_device_vtpm *vtpm,
-                                   libxl__ao_device *aodev)
-{
-    libxl__device_add_async(egc, domid, &libxl__vtpm_devtype, vtpm, aodev);
-}
-
-static int libxl__vtpm_from_xenstore(libxl__gc *gc, const char *libxl_path,
-                                     libxl_devid devid,
-                                     libxl_device_vtpm *vtpm)
-{
-    int rc;
-    const char *be_path;
-    char *uuid;
-
-    vtpm->devid = devid;
-
-    rc = libxl__xs_read_mandatory(gc, XBT_NULL,
-                                  GCSPRINTF("%s/backend", libxl_path),
-                                  &be_path);
-    if (rc) return rc;
-
-    rc = libxl__backendpath_parse_domid(gc, be_path, &vtpm->backend_domid);
-    if (rc) return rc;
-
-    uuid = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/uuid", be_path));
-    if (uuid) {
-        if(libxl_uuid_from_string(&(vtpm->uuid), uuid)) {
-            LOGD(ERROR, vtpm->backend_domid, "%s/uuid is a malformed uuid?? "
-                               "(%s) Probably a bug!!\n", be_path, uuid);
-            return ERROR_FAIL;
-        }
-    }
-
-    return 0;
-}
-
-int libxl_device_vtpm_getinfo(libxl_ctx *ctx,
-                              uint32_t domid,
-                              const libxl_device_vtpm *vtpm,
-                              libxl_vtpminfo *vtpminfo)
-{
-    GC_INIT(ctx);
-    char *libxl_path, *vtpmpath;
-    char *val;
-    int rc = 0;
-
-    libxl_vtpminfo_init(vtpminfo);
-    vtpminfo->devid = vtpm->devid;
-
-    vtpmpath = libxl__domain_device_frontend_path(gc, domid, vtpminfo->devid,
-                                                  LIBXL__DEVICE_KIND_VTPM);
-    libxl_path = libxl__domain_device_libxl_path(gc, domid, vtpminfo->devid,
-                                                 LIBXL__DEVICE_KIND_VTPM);
-    vtpminfo->backend = xs_read(ctx->xsh, XBT_NULL,
-          GCSPRINTF("%s/backend", libxl_path), NULL);
-    if (!vtpminfo->backend) {
-        goto err;
-    }
-
-    rc = libxl__backendpath_parse_domid(gc, vtpminfo->backend,
-                                        &vtpminfo->backend_id);
-    if (rc) goto exit;
-
-    val = libxl__xs_read(gc, XBT_NULL,
-          GCSPRINTF("%s/state", vtpmpath));
-    vtpminfo->state = val ? strtoul(val, NULL, 10) : -1;
-
-    val = libxl__xs_read(gc, XBT_NULL,
-          GCSPRINTF("%s/event-channel", vtpmpath));
-    vtpminfo->evtch = val ? strtoul(val, NULL, 10) : -1;
-
-    val = libxl__xs_read(gc, XBT_NULL,
-          GCSPRINTF("%s/ring-ref", vtpmpath));
-    vtpminfo->rref = val ? strtoul(val, NULL, 10) : -1;
-
-    vtpminfo->frontend = xs_read(ctx->xsh, XBT_NULL,
-          GCSPRINTF("%s/frontend", libxl_path), NULL);
-    vtpminfo->frontend_id = domid;
-
-    val = libxl__xs_read(gc, XBT_NULL,
-          GCSPRINTF("%s/uuid", libxl_path));
-    if(val == NULL) {
-       LOGD(ERROR, domid, "%s/uuid does not exist!", vtpminfo->backend);
-       goto err;
-    }
-    if(libxl_uuid_from_string(&(vtpminfo->uuid), val)) {
-       LOGD(ERROR, domid,
-             "%s/uuid is a malformed uuid?? (%s) Probably a bug!\n",
-             vtpminfo->backend, val);
-       goto err;
-    }
-
-    goto exit;
-err:
-    rc = ERROR_FAIL;
-exit:
-    GC_FREE;
-    return rc;
-}
-
-int libxl_devid_to_device_vtpm(libxl_ctx *ctx,
-                               uint32_t domid,
-                               int devid,
-                               libxl_device_vtpm *vtpm)
-{
-    GC_INIT(ctx);
-    libxl_device_vtpm *vtpms;
-    int nb, i;
-    int rc;
-
-    vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
-    if (!vtpms)
-        return ERROR_FAIL;
-
-    libxl_device_vtpm_init(vtpm);
-    rc = 1;
-    for (i = 0; i < nb; ++i) {
-        if(devid == vtpms[i].devid) {
-            vtpm->backend_domid = vtpms[i].backend_domid;
-            vtpm->devid = vtpms[i].devid;
-            libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
-            rc = 0;
-            break;
-        }
-    }
-
-    libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
-    GC_FREE;
-    return rc;
-}
-
-static int libxl_device_vtpm_compare(const libxl_device_vtpm *d1,
-                                     const libxl_device_vtpm *d2)
-{
-    return COMPARE_DEVID(d1, d2);
-}
-
-int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
-                            libxl_uuid* uuid, libxl_device_vtpm *vtpm)
-{
-    GC_INIT(ctx);
-    libxl_device_vtpm *vtpms;
-    int nb, i;
-    int rc;
-
-    vtpms = libxl__device_list(gc, &libxl__vtpm_devtype, domid, &nb);
-    if (!vtpms)
-        return ERROR_FAIL;
-
-    memset(vtpm, 0, sizeof (libxl_device_vtpm));
-    rc = 1;
-    for (i = 0; i < nb; ++i) {
-        if(!libxl_uuid_compare(uuid, &vtpms[i].uuid)) {
-            vtpm->backend_domid = vtpms[i].backend_domid;
-            vtpm->devid = vtpms[i].devid;
-            libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
-            rc = 0;
-            break;
-        }
-    }
-
-    libxl__device_list_free(&libxl__vtpm_devtype, vtpms, nb);
-    GC_FREE;
-    return rc;
-}
-
-static void libxl_device_vtpm_update_config(libxl__gc *gc, void *d, void *s)
-{
-    libxl__update_config_vtpm(gc, d, s);
-}
-
-static LIBXL_DEFINE_UPDATE_DEVID(vtpm)
-static LIBXL_DEFINE_DEVICE_FROM_TYPE(vtpm)
-static LIBXL_DEFINE_DEVICES_ADD(vtpm)
-
-LIBXL_DEFINE_DEVICE_ADD(vtpm)
-LIBXL_DEFINE_DEVICE_REMOVE(vtpm)
-LIBXL_DEFINE_DEVICE_LIST(vtpm)
-
-DEFINE_DEVICE_TYPE_STRUCT(vtpm, VTPM,
-    .update_config = libxl_device_vtpm_update_config,
-    .from_xenstore = (device_from_xenstore_fn_t)libxl__vtpm_from_xenstore,
-    .set_xenstore_config = (device_set_xenstore_config_fn_t)
-                           libxl__set_xenstore_vtpm,
-);
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
-
diff --git a/tools/libxl/libxl_x86.c b/tools/libxl/libxl_x86.c
deleted file mode 100644 (file)
index 7d95506..0000000
+++ /dev/null
@@ -1,852 +0,0 @@
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-
-#include <xenctrl_dom.h>
-
-int libxl__arch_domain_prepare_config(libxl__gc *gc,
-                                      libxl_domain_config *d_config,
-                                      struct xen_domctl_createdomain *config)
-{
-    switch(d_config->c_info.type) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
-        break;
-    case LIBXL_DOMAIN_TYPE_PVH:
-        config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
-        break;
-    case LIBXL_DOMAIN_TYPE_PV:
-        config->arch.emulation_flags = 0;
-        break;
-    default:
-        abort();
-    }
-
-    return 0;
-}
-
-int libxl__arch_domain_save_config(libxl__gc *gc,
-                                   libxl_domain_config *d_config,
-                                   libxl__domain_build_state *state,
-                                   const struct xen_domctl_createdomain *config)
-{
-    return 0;
-}
-
-static const char *e820_names(int type)
-{
-    switch (type) {
-        case E820_RAM: return "RAM";
-        case E820_RESERVED: return "Reserved";
-        case E820_ACPI: return "ACPI";
-        case E820_NVS: return "ACPI NVS";
-        case E820_UNUSABLE: return "Unusable";
-        default: break;
-    }
-    return "Unknown";
-}
-
-static int e820_sanitize(libxl__gc *gc, struct e820entry src[],
-                         uint32_t *nr_entries,
-                         unsigned long map_limitkb,
-                         unsigned long balloon_kb)
-{
-    uint64_t delta_kb = 0, start = 0, start_kb = 0, last = 0, ram_end;
-    uint32_t i, idx = 0, nr;
-    struct e820entry e820[E820MAX];
-
-    if (!src || !map_limitkb || !nr_entries)
-        return ERROR_INVAL;
-
-    nr = *nr_entries;
-    if (!nr)
-        return ERROR_INVAL;
-
-    if (nr > E820MAX)
-        return ERROR_NOMEM;
-
-    /* Weed out anything under 1MB */
-    for (i = 0; i < nr; i++) {
-        if (src[i].addr > 0x100000)
-            continue;
-
-        src[i].type = 0;
-        src[i].size = 0;
-        src[i].addr = -1ULL;
-    }
-
-    /* Find the lowest and highest entry in E820, skipping over
-     * undesired entries. */
-    start = -1ULL;
-    last = 0;
-    for (i = 0; i < nr; i++) {
-        if ((src[i].type == E820_RAM) ||
-            (src[i].type == E820_UNUSABLE) ||
-            (src[i].type == 0))
-            continue;
-
-        start = src[i].addr < start ? src[i].addr : start;
-        last = src[i].addr + src[i].size > last ?
-               src[i].addr + src[i].size > last : last;
-    }
-    if (start > 1024)
-        start_kb = start >> 10;
-
-    /* Add the memory RAM region for the guest */
-    e820[idx].addr = 0;
-    e820[idx].size = (uint64_t)map_limitkb << 10;
-    e820[idx].type = E820_RAM;
-
-    /* .. and trim if neccessary */
-    if (start_kb && map_limitkb > start_kb) {
-        delta_kb = map_limitkb - start_kb;
-        if (delta_kb)
-            e820[idx].size -= (uint64_t)(delta_kb << 10);
-    }
-    /* Note: We don't touch balloon_kb here. Will add it at the end. */
-    ram_end = e820[idx].addr + e820[idx].size;
-    idx ++;
-
-    LOG(DEBUG, "Memory: %"PRIu64"kB End of RAM: " \
-        "0x%"PRIx64" (PFN) Delta: %"PRIu64"kB, PCI start: %"PRIu64"kB " \
-        "(0x%"PRIx64" PFN), Balloon %"PRIu64"kB\n", (uint64_t)map_limitkb,
-        ram_end >> 12, delta_kb, start_kb ,start >> 12,
-        (uint64_t)balloon_kb);
-
-
-    /* This whole code below is to guard against if the Intel IGD is passed into
-     * the guest. If we don't pass in IGD, this whole code can be ignored.
-     *
-     * The reason for this code is that Intel boxes fill their E820 with
-     * E820_RAM amongst E820_RESERVED and we can't just ditch those E820_RAM.
-     * That is b/c any "gaps" in the E820 is considered PCI I/O space by
-     * Linux and it would be utilized by the Intel IGD as I/O space while
-     * in reality it was an RAM region.
-     *
-     * What this means is that we have to walk the E820 and for any region
-     * that is RAM and below 4GB and above ram_end, needs to change its type
-     * to E820_UNUSED. We also need to move some of the E820_RAM regions if
-     * the overlap with ram_end. */
-    for (i = 0; i < nr; i++) {
-        uint64_t end = src[i].addr + src[i].size;
-
-        /* We don't care about E820_UNUSABLE, but we need to
-         * change the type to zero b/c the loop after this
-         * sticks E820_UNUSABLE on the guest's E820 but ignores
-         * the ones with type zero. */
-        if ((src[i].type == E820_UNUSABLE) ||
-            /* Any region that is within the "RAM region" can
-             * be safely ditched. */
-            (end < ram_end)) {
-                src[i].type = 0;
-                continue;
-        }
-
-        /* Look only at RAM regions. */
-        if (src[i].type != E820_RAM)
-            continue;
-
-        /* We only care about RAM regions below 4GB. */
-        if (src[i].addr >= (1ULL<<32))
-            continue;
-
-        /* E820_RAM overlaps with our RAM region. Move it */
-        if (src[i].addr < ram_end) {
-            uint64_t delta;
-
-            src[i].type = E820_UNUSABLE;
-            delta = ram_end - src[i].addr;
-            /* The end < ram_end should weed this out */
-            if (src[i].size < delta)
-                src[i].type = 0;
-            else {
-                src[i].size -= delta;
-                src[i].addr = ram_end;
-            }
-            if (src[i].addr + src[i].size != end) {
-                /* We messed up somewhere */
-                src[i].type = 0;
-                LOGE(ERROR, "Computed E820 wrongly. Continuing on.");
-            }
-        }
-        /* Lastly, convert the RAM to UNSUABLE. Look in the Linux kernel
-           at git commit 2f14ddc3a7146ea4cd5a3d1ecd993f85f2e4f948
-            "xen/setup: Inhibit resource API from using System RAM E820
-           gaps as PCI mem gaps" for full explanation. */
-        if (end > ram_end)
-            src[i].type = E820_UNUSABLE;
-    }
-
-    /* Check if there is a region between ram_end and start. */
-    if (start > ram_end) {
-        int add_unusable = 1;
-        for (i = 0; i < nr && add_unusable; i++) {
-            if (src[i].type != E820_UNUSABLE)
-                continue;
-            if (ram_end != src[i].addr)
-                continue;
-            if (start != src[i].addr + src[i].size) {
-                /* there is one, adjust it */
-                src[i].size = start - src[i].addr;
-            }
-            add_unusable = 0;
-        }
-        /* .. and if not present, add it in. This is to guard against
-           the Linux guest assuming that the gap between the end of
-           RAM region and the start of the E820_[ACPI,NVS,RESERVED]
-           is PCI I/O space. Which it certainly is _not_. */
-        if (add_unusable) {
-            e820[idx].type = E820_UNUSABLE;
-            e820[idx].addr = ram_end;
-            e820[idx].size = start - ram_end;
-            idx++;
-        }
-    }
-    /* Almost done: copy them over, ignoring the undesireable ones */
-    for (i = 0; i < nr; i++) {
-        if ((src[i].type == E820_RAM) ||
-            (src[i].type == 0))
-            continue;
-
-        e820[idx].type = src[i].type;
-        e820[idx].addr = src[i].addr;
-        e820[idx].size = src[i].size;
-        idx++;
-    }
-    /* At this point we have the mapped RAM + E820 entries from src. */
-    if (balloon_kb || delta_kb) {
-        /* and if we truncated the RAM region, then add it to the end. */
-        e820[idx].type = E820_RAM;
-        e820[idx].addr = (uint64_t)(1ULL << 32) > last ?
-                         (uint64_t)(1ULL << 32) : last;
-        /* also add the balloon memory to the end. */
-        e820[idx].size = (uint64_t)(delta_kb << 10) +
-                         (uint64_t)(balloon_kb << 10);
-        idx++;
-
-    }
-    nr = idx;
-
-    for (i = 0; i < nr; i++) {
-      LOG(DEBUG, ":\t[%"PRIx64" -> %"PRIx64"] %s", e820[i].addr >> 12,
-          (e820[i].addr + e820[i].size) >> 12, e820_names(e820[i].type));
-    }
-
-    /* Done: copy the sanitized version. */
-    *nr_entries = nr;
-    memcpy(src, e820, nr * sizeof(struct e820entry));
-    return 0;
-}
-
-static int e820_host_sanitize(libxl__gc *gc,
-                              libxl_domain_build_info *b_info,
-                              struct e820entry map[],
-                              uint32_t *nr)
-{
-    int rc;
-
-    rc = xc_get_machine_memory_map(CTX->xch, map, *nr);
-    if (rc < 0)
-        return ERROR_FAIL;
-
-    *nr = rc;
-
-    rc = e820_sanitize(gc, map, nr, b_info->target_memkb,
-                       (b_info->max_memkb - b_info->target_memkb) +
-                       b_info->u.pv.slack_memkb);
-    return rc;
-}
-
-static int libxl__e820_alloc(libxl__gc *gc, uint32_t domid,
-        libxl_domain_config *d_config)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    int rc;
-    uint32_t nr;
-    struct e820entry map[E820MAX];
-    libxl_domain_build_info *b_info;
-
-    if (d_config == NULL || d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV)
-        return ERROR_INVAL;
-
-    b_info = &d_config->b_info;
-    if (!libxl_defbool_val(b_info->u.pv.e820_host))
-        return ERROR_INVAL;
-
-    nr = E820MAX;
-    rc = e820_host_sanitize(gc, b_info, map, &nr);
-    if (rc)
-        return ERROR_FAIL;
-
-    rc = xc_domain_set_memory_map(ctx->xch, domid, map, nr);
-
-    if (rc < 0)
-        return ERROR_FAIL;
-
-    return 0;
-}
-
-static unsigned long timer_mode(const libxl_domain_build_info *info)
-{
-    const libxl_timer_mode mode = info->timer_mode;
-    assert(mode >= LIBXL_TIMER_MODE_DELAY_FOR_MISSED_TICKS &&
-           mode <= LIBXL_TIMER_MODE_ONE_MISSED_TICK_PENDING);
-    return ((unsigned long)mode);
-}
-
-static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid,
-                                     const libxl_domain_build_info *info)
-{
-    libxl_bitmap enlightenments;
-    libxl_viridian_enlightenment v;
-    uint64_t mask = 0;
-
-    libxl_bitmap_init(&enlightenments);
-    libxl_bitmap_alloc(CTX, &enlightenments,
-                       LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH);
-
-    if (libxl_defbool_val(info->u.hvm.viridian)) {
-        /* Enable defaults */
-        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE);
-        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ);
-        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT);
-        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST);
-        libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL);
-    }
-
-    libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) {
-        if (libxl_bitmap_test(&info->u.hvm.viridian_disable, v)) {
-            LOG(ERROR, "%s group both enabled and disabled",
-                libxl_viridian_enlightenment_to_string(v));
-            goto err;
-        }
-        if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
-            libxl_bitmap_set(&enlightenments, v);
-    }
-
-    libxl_for_each_set_bit(v, info->u.hvm.viridian_disable)
-        if (libxl_viridian_enlightenment_to_string(v)) /* check validity */
-            libxl_bitmap_reset(&enlightenments, v);
-
-    /* The base set is a pre-requisite for all others */
-    if (!libxl_bitmap_is_empty(&enlightenments) &&
-        !libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
-        LOG(ERROR, "base group not enabled");
-        goto err;
-    }
-
-    libxl_for_each_set_bit(v, enlightenments)
-        LOG(DETAIL, "%s group enabled", libxl_viridian_enlightenment_to_string(v));
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) {
-        mask |= HVMPV_base_freq;
-
-        if (!libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ))
-            mask |= HVMPV_no_freq;
-    }
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT))
-        mask |= HVMPV_time_ref_count;
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_REFERENCE_TSC))
-        mask |= HVMPV_reference_tsc;
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_REMOTE_TLB_FLUSH))
-        mask |= HVMPV_hcall_remote_tlb_flush;
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST))
-        mask |= HVMPV_apic_assist;
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL))
-        mask |= HVMPV_crash_ctl;
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_SYNIC))
-        mask |= HVMPV_synic;
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_STIMER))
-        mask |= HVMPV_time_ref_count | HVMPV_synic | HVMPV_stimer;
-
-    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_IPI))
-        mask |= HVMPV_hcall_ipi;
-
-    if (mask != 0 &&
-        xc_hvm_param_set(CTX->xch,
-                         domid,
-                         HVM_PARAM_VIRIDIAN,
-                         mask) != 0) {
-        LOGE(ERROR, "Couldn't set viridian feature mask (0x%"PRIx64")", mask);
-        goto err;
-    }
-
-    libxl_bitmap_dispose(&enlightenments);
-    return 0;
-
-err:
-    libxl_bitmap_dispose(&enlightenments);
-    return ERROR_FAIL;
-}
-
-static int hvm_set_conf_params(libxl__gc *gc, uint32_t domid,
-                               const libxl_domain_build_info *info)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    xc_interface *xch = ctx->xch;
-    int ret = ERROR_FAIL;
-    unsigned int altp2m = info->altp2m;
-
-    switch(info->type) {
-    case LIBXL_DOMAIN_TYPE_HVM:
-        /* The config parameter "altp2m" replaces the parameter "altp2mhvm". For
-         * legacy reasons, both parameters are accepted on x86 HVM guests.
-         *
-         * If the legacy field info->u.hvm.altp2m is set, activate altp2m.
-         * Otherwise set altp2m based on the field info->altp2m. */
-        if (info->altp2m == LIBXL_ALTP2M_MODE_DISABLED &&
-            libxl_defbool_val(info->u.hvm.altp2m))
-            altp2m = libxl_defbool_val(info->u.hvm.altp2m);
-
-        if (xc_hvm_param_set(xch, domid, HVM_PARAM_HPET_ENABLED,
-                             libxl_defbool_val(info->u.hvm.hpet))) {
-            LOG(ERROR, "Couldn't set HVM_PARAM_HPET_ENABLED");
-            goto out;
-        }
-        if (xc_hvm_param_set(xch, domid, HVM_PARAM_VPT_ALIGN,
-                             libxl_defbool_val(info->u.hvm.vpt_align))) {
-            LOG(ERROR, "Couldn't set HVM_PARAM_VPT_ALIGN");
-            goto out;
-        }
-        if (info->u.hvm.mca_caps &&
-            xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_MCA_CAP,
-                             info->u.hvm.mca_caps)) {
-            LOG(ERROR, "Couldn't set HVM_PARAM_MCA_CAP");
-            goto out;
-        }
-
-        /* Fallthrough */
-    case LIBXL_DOMAIN_TYPE_PVH:
-        if (xc_hvm_param_set(xch, domid, HVM_PARAM_TIMER_MODE,
-                             timer_mode(info))) {
-            LOG(ERROR, "Couldn't set HVM_PARAM_TIMER_MODE");
-            goto out;
-        }
-        if (xc_hvm_param_set(xch, domid, HVM_PARAM_NESTEDHVM,
-                             libxl_defbool_val(info->nested_hvm))) {
-            LOG(ERROR, "Couldn't set HVM_PARAM_NESTEDHVM");
-            goto out;
-        }
-        if (xc_hvm_param_set(xch, domid, HVM_PARAM_ALTP2M, altp2m)) {
-            LOG(ERROR, "Couldn't set HVM_PARAM_ALTP2M");
-            goto out;
-        }
-        break;
-
-    default:
-        abort();
-    }
-
-    ret = 0;
-
- out:
-    return ret;
-}
-
-int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
-        uint32_t domid)
-{
-    const libxl_domain_build_info *info = &d_config->b_info;
-    int ret = 0;
-    int tsc_mode;
-    uint32_t rtc_timeoffset;
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-
-    if (info->type != LIBXL_DOMAIN_TYPE_PV &&
-        (ret = hvm_set_conf_params(gc, domid, info)) != 0)
-        goto out;
-
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
-        (ret = hvm_set_viridian_features(gc, domid, info)) != 0)
-        goto out;
-
-    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PV)
-        xc_domain_set_memmap_limit(ctx->xch, domid,
-                                   (d_config->b_info.max_memkb +
-                                    d_config->b_info.u.pv.slack_memkb));
-
-    switch (d_config->b_info.tsc_mode) {
-    case LIBXL_TSC_MODE_DEFAULT:
-        tsc_mode = 0;
-        break;
-    case LIBXL_TSC_MODE_ALWAYS_EMULATE:
-        tsc_mode = 1;
-        break;
-    case LIBXL_TSC_MODE_NATIVE:
-        tsc_mode = 2;
-        break;
-    case LIBXL_TSC_MODE_NATIVE_PARAVIRT:
-        LOGD(ERROR, domid, "TSC Mode native_paravirt (a.k.a PVRDTSCP) has been removed");
-        ret = ERROR_FEATURE_REMOVED;
-        goto out;
-    default:
-        abort();
-    }
-
-    if (xc_domain_set_tsc_info(ctx->xch, domid, tsc_mode, 0, 0, 0)) {
-        LOGE(ERROR, "xc_domain_set_tsc_info() failed");
-        ret = ERROR_FAIL;
-        goto out;
-    }
-
-    rtc_timeoffset = d_config->b_info.rtc_timeoffset;
-    if (libxl_defbool_val(d_config->b_info.localtime)) {
-        time_t t;
-        struct tm *tm, result;
-
-        t = time(NULL);
-        tm = localtime_r(&t, &result);
-
-        if (!tm) {
-            LOGED(ERROR, domid, "Failed to call localtime_r");
-            ret = ERROR_FAIL;
-            goto out;
-        }
-
-        rtc_timeoffset += tm->tm_gmtoff;
-    }
-
-    if (rtc_timeoffset)
-        xc_domain_set_time_offset(ctx->xch, domid, rtc_timeoffset);
-
-    if (d_config->b_info.type != LIBXL_DOMAIN_TYPE_PV) {
-        unsigned long shadow = DIV_ROUNDUP(d_config->b_info.shadow_memkb,
-                                           1024);
-        xc_shadow_control(ctx->xch, domid, XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION,
-                          NULL, 0, &shadow, 0, NULL);
-    }
-
-    if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
-            libxl_defbool_val(d_config->b_info.u.pv.e820_host)) {
-        ret = libxl__e820_alloc(gc, domid, d_config);
-        if (ret) {
-            LOGED(ERROR, domid, "Failed while collecting E820 with: %d (errno:%d)\n",
-                 ret, errno);
-        }
-    }
-
-out:
-    return ret;
-}
-
-int libxl__arch_extra_memory(libxl__gc *gc,
-                             const libxl_domain_build_info *info,
-                             uint64_t *out)
-{
-    *out = LIBXL_MAXMEM_CONSTANT;
-
-    return 0;
-}
-
-int libxl__arch_domain_init_hw_description(libxl__gc *gc,
-                                           libxl_domain_build_info *info,
-                                           libxl__domain_build_state *state,
-                                           struct xc_dom_image *dom)
-{
-    return 0;
-}
-
-int libxl__arch_build_dom_finish(libxl__gc *gc,
-                                 libxl_domain_build_info *info,
-                                 struct xc_dom_image *dom,
-                                 libxl__domain_build_state *state)
-{
-    return 0;
-}
-
-/* Return 0 on success, ERROR_* on failure. */
-int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
-                                      uint32_t domid,
-                                      libxl_domain_build_info *b_info,
-                                      libxl__domain_build_state *state)
-{
-    int nid, nr_vmemrange, rc;
-    uint32_t nr_e820, e820_count;
-    struct e820entry map[E820MAX];
-    xen_vmemrange_t *vmemranges;
-    unsigned int array_size;
-
-    /* If e820_host is not set, call the generic function */
-    if (!(b_info->type == LIBXL_DOMAIN_TYPE_PV &&
-          libxl_defbool_val(b_info->u.pv.e820_host)))
-        return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, b_info,
-                                                       state);
-
-    assert(state->vmemranges == NULL);
-
-    nr_e820 = E820MAX;
-    rc = e820_host_sanitize(gc, b_info, map, &nr_e820);
-    if (rc) goto out;
-
-    e820_count = 0;
-    nr_vmemrange = 0;
-    vmemranges = NULL;
-    array_size = 0;
-    for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) {
-        libxl_vnode_info *p = &b_info->vnuma_nodes[nid];
-        uint64_t remaining_bytes = (p->memkb << 10), bytes;
-
-        while (remaining_bytes > 0) {
-            if (e820_count >= nr_e820) {
-                rc = ERROR_NOMEM;
-                goto out;
-            }
-
-            /* Skip non RAM region */
-            if (map[e820_count].type != E820_RAM) {
-                e820_count++;
-                continue;
-            }
-
-            if (nr_vmemrange >= array_size) {
-                array_size += 32;
-                GCREALLOC_ARRAY(vmemranges, array_size);
-            }
-
-            bytes = map[e820_count].size >= remaining_bytes ?
-                remaining_bytes : map[e820_count].size;
-
-            vmemranges[nr_vmemrange].start = map[e820_count].addr;
-            vmemranges[nr_vmemrange].end = map[e820_count].addr + bytes;
-
-            if (map[e820_count].size >= remaining_bytes) {
-                map[e820_count].addr += bytes;
-                map[e820_count].size -= bytes;
-            } else {
-                e820_count++;
-            }
-
-            remaining_bytes -= bytes;
-
-            vmemranges[nr_vmemrange].flags = 0;
-            vmemranges[nr_vmemrange].nid = nid;
-            nr_vmemrange++;
-        }
-    }
-
-    state->vmemranges = vmemranges;
-    state->num_vmemranges = nr_vmemrange;
-
-    rc = 0;
-out:
-    return rc;
-}
-
-int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
-{
-    int ret;
-
-    ret = xc_physdev_map_pirq(CTX->xch, domid, irq, &irq);
-    if (ret)
-        return ret;
-
-    ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
-
-    return ret;
-}
-
-/*
- * Here we're just trying to set these kinds of e820 mappings:
- *
- * #1. Low memory region
- *
- * Low RAM starts at least from 1M to make sure all standard regions
- * of the PC memory map, like BIOS, VGA memory-mapped I/O and vgabios,
- * have enough space.
- * Note: Those stuffs below 1M are still constructed with multiple
- * e820 entries by hvmloader. At this point we don't change anything.
- *
- * #2. RDM region if it exists
- *
- * #3. High memory region if it exists
- *
- * Note: these regions are not overlapping since we already check
- * to adjust them. Please refer to libxl__domain_device_construct_rdm().
- */
-#define GUEST_LOW_MEM_START_DEFAULT 0x100000
-static int domain_construct_memmap(libxl__gc *gc,
-                                   libxl_domain_config *d_config,
-                                   uint32_t domid,
-                                   struct xc_dom_image *dom)
-{
-    int rc = 0;
-    unsigned int nr = 0, i;
-    /* We always own at least one lowmem entry. */
-    unsigned int e820_entries = 1;
-    struct e820entry *e820 = NULL;
-    uint64_t highmem_size =
-                    dom->highmem_end ? dom->highmem_end - (1ull << 32) : 0;
-    uint32_t lowmem_start = dom->device_model ? GUEST_LOW_MEM_START_DEFAULT : 0;
-    unsigned page_size = XC_DOM_PAGE_SIZE(dom);
-
-    /* Add all rdm entries. */
-    for (i = 0; i < d_config->num_rdms; i++)
-        if (d_config->rdms[i].policy != LIBXL_RDM_RESERVE_POLICY_INVALID)
-            e820_entries++;
-
-    /* Add the HVM special pages to PVH memmap as RESERVED. */
-    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH)
-        e820_entries++;
-
-    /* If we should have a highmem range. */
-    if (highmem_size)
-        e820_entries++;
-
-    for (i = 0; i < MAX_ACPI_MODULES; i++)
-        if (dom->acpi_modules[i].length)
-            e820_entries++;
-
-    if (e820_entries >= E820MAX) {
-        LOGD(ERROR, domid, "Ooops! Too many entries in the memory map!");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    e820 = libxl__malloc(gc, sizeof(struct e820entry) * e820_entries);
-
-    /* Low memory */
-    e820[nr].addr = lowmem_start;
-    e820[nr].size = dom->lowmem_end - lowmem_start;
-    e820[nr].type = E820_RAM;
-    nr++;
-
-    /* RDM mapping */
-    for (i = 0; i < d_config->num_rdms; i++) {
-        if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
-            continue;
-
-        e820[nr].addr = d_config->rdms[i].start;
-        e820[nr].size = d_config->rdms[i].size;
-        e820[nr].type = E820_RESERVED;
-        nr++;
-    }
-
-    /* HVM special pages */
-    if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PVH) {
-        e820[nr].addr = (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES)
-                        << XC_PAGE_SHIFT;
-        e820[nr].size = X86_HVM_NR_SPECIAL_PAGES << XC_PAGE_SHIFT;
-        e820[nr].type = E820_RESERVED;
-        nr++;
-    }
-
-    for (i = 0; i < MAX_ACPI_MODULES; i++) {
-        if (dom->acpi_modules[i].length) {
-            e820[nr].addr = dom->acpi_modules[i].guest_addr_out & ~(page_size - 1);
-            e820[nr].size = dom->acpi_modules[i].length +
-                (dom->acpi_modules[i].guest_addr_out & (page_size - 1));
-            e820[nr].type = E820_ACPI;
-            nr++;
-        }
-    }
-
-    /* High memory */
-    if (highmem_size) {
-        e820[nr].addr = ((uint64_t)1 << 32);
-        e820[nr].size = highmem_size;
-        e820[nr].type = E820_RAM;
-    }
-
-    if (xc_domain_set_memory_map(CTX->xch, domid, e820, e820_entries) != 0) {
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    dom->e820 = e820;
-    dom->e820_entries = e820_entries;
-
-out:
-    return rc;
-}
-
-int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
-                                               uint32_t domid,
-                                               libxl_domain_config *d_config,
-                                               struct xc_dom_image *dom)
-{
-    libxl_domain_build_info *const info = &d_config->b_info;
-    int rc;
-
-    if (info->type == LIBXL_DOMAIN_TYPE_PV)
-        return 0;
-
-    if (info->type == LIBXL_DOMAIN_TYPE_PVH) {
-        rc = libxl__dom_load_acpi(gc, info, dom);
-        if (rc != 0) {
-            LOGE(ERROR, "libxl_dom_load_acpi failed");
-            return rc;
-        }
-    }
-
-    rc = domain_construct_memmap(gc, d_config, domid, dom);
-    if (rc != 0)
-        LOGE(ERROR, "setting domain memory map failed");
-
-    return rc;
-}
-
-void libxl__arch_domain_create_info_setdefault(libxl__gc *gc,
-                                               libxl_domain_create_info *c_info)
-{
-}
-
-void libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
-                                              libxl_domain_build_info *b_info)
-{
-    libxl_defbool_setdefault(&b_info->acpi, true);
-}
-
-int libxl__arch_passthrough_mode_setdefault(libxl__gc *gc,
-                                            uint32_t domid,
-                                            libxl_domain_config *d_config,
-                                            const libxl_physinfo *physinfo)
-{
-    int rc;
-    libxl_domain_create_info *const c_info = &d_config->c_info;
-
-    if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED &&
-        c_info->type == LIBXL_DOMAIN_TYPE_PVH) {
-        LOGD(ERROR, domid,
-             "passthrough not yet supported for x86 PVH guests\n");
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    const char *whynot_pt_share =
-        c_info->type == LIBXL_DOMAIN_TYPE_PV ? "not valid for PV domain" :
-        !physinfo->cap_iommu_hap_pt_share ? "not supported on this platform" :
-        !libxl_defbool_val(d_config->c_info.hap) ?"only valid for HAP guests":
-        NULL;
-
-    if (c_info->passthrough == LIBXL_PASSTHROUGH_ENABLED) {
-        c_info->passthrough = whynot_pt_share
-            ? LIBXL_PASSTHROUGH_SYNC_PT : LIBXL_PASSTHROUGH_SHARE_PT;
-    }
-
-    if (c_info->passthrough == LIBXL_PASSTHROUGH_SHARE_PT && whynot_pt_share) {
-        LOGD(ERROR, domid,
-             "passthrough=\"share_pt\" %s\n",
-             whynot_pt_share);
-        rc = ERROR_INVAL;
-        goto out;
-    }
-
-    rc = 0;
- out:
-    return rc;
-}
-
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_x86_acpi.c b/tools/libxl/libxl_x86_acpi.c
deleted file mode 100644 (file)
index 3df86c7..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- *
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
- */
-
-#include "libxl_internal.h"
-#include "libxl_arch.h"
-#include <xen/hvm/hvm_info_table.h>
-#include <xen/hvm/e820.h>
-#include "libacpi/libacpi.h"
-
-#include <xenctrl_dom.h>
-
- /* Number of pages holding ACPI tables */
-#define NUM_ACPI_PAGES 16
-
-struct libxl_acpi_ctxt {
-    struct acpi_ctxt c;
-
-    unsigned int page_size;
-    unsigned int page_shift;
-
-    /* Memory allocator */
-    unsigned long alloc_base_paddr;
-    unsigned long alloc_base_vaddr;
-    unsigned long alloc_currp;
-    unsigned long alloc_end;
-};
-
-extern const unsigned char dsdt_pvh[];
-extern const unsigned int dsdt_pvh_len;
-
-/* Assumes contiguous physical space */
-static unsigned long virt_to_phys(struct acpi_ctxt *ctxt, void *v)
-{
-    struct libxl_acpi_ctxt *libxl_ctxt =
-        CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
-
-    return (((unsigned long)v - libxl_ctxt->alloc_base_vaddr) +
-            libxl_ctxt->alloc_base_paddr);
-}
-
-static void *mem_alloc(struct acpi_ctxt *ctxt,
-                       uint32_t size, uint32_t align)
-{
-    struct libxl_acpi_ctxt *libxl_ctxt =
-        CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
-    unsigned long s, e;
-
-    /* Align to at least 16 bytes. */
-    if (align < 16)
-        align = 16;
-
-    s = (libxl_ctxt->alloc_currp + align) & ~((unsigned long)align - 1);
-    e = s + size - 1;
-
-    /* TODO: Reallocate memory */
-    if ((e < s) || (e >= libxl_ctxt->alloc_end))
-        return NULL;
-
-    while (libxl_ctxt->alloc_currp >> libxl_ctxt->page_shift != 
-           e >> libxl_ctxt->page_shift)
-        libxl_ctxt->alloc_currp += libxl_ctxt->page_size;
-
-    libxl_ctxt->alloc_currp = e;
-
-    return (void *)s;
-}
-
-static void acpi_mem_free(struct acpi_ctxt *ctxt,
-                          void *v, uint32_t size)
-{
-}
-
-static uint32_t acpi_lapic_id(unsigned cpu)
-{
-    return cpu * 2;
-}
-
-static int init_acpi_config(libxl__gc *gc, 
-                            struct xc_dom_image *dom,
-                            const libxl_domain_build_info *b_info,
-                            struct acpi_config *config)
-{
-    xc_interface *xch = dom->xch;
-    uint32_t domid = dom->guest_domid;
-    xc_dominfo_t info;
-    struct hvm_info_table *hvminfo;
-    int i, r, rc;
-
-    config->dsdt_anycpu = config->dsdt_15cpu = dsdt_pvh;
-    config->dsdt_anycpu_len = config->dsdt_15cpu_len = dsdt_pvh_len;
-
-    r = xc_domain_getinfo(xch, domid, 1, &info);
-    if (r < 0) {
-        LOG(ERROR, "getdomaininfo failed (rc=%d)", r);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    hvminfo = libxl__zalloc(gc, sizeof(*hvminfo));
-
-    hvminfo->apic_mode = libxl_defbool_val(b_info->apic);
-
-    if (dom->nr_vnodes) {
-        unsigned int *vcpu_to_vnode, *vdistance;
-        struct xen_vmemrange *vmemrange;
-        struct acpi_numa *numa = &config->numa;
-
-        r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
-                               &numa->nr_vmemranges,
-                               &hvminfo->nr_vcpus, NULL, NULL, NULL);
-        if (r) {
-            LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
-        vmemrange = libxl__zalloc(gc, dom->nr_vmemranges * sizeof(*vmemrange));
-        vdistance = libxl__zalloc(gc, dom->nr_vnodes * sizeof(*vdistance));
-        vcpu_to_vnode = libxl__zalloc(gc, hvminfo->nr_vcpus *
-                                      sizeof(*vcpu_to_vnode));
-        r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
-                               &numa->nr_vmemranges, &hvminfo->nr_vcpus,
-                               vmemrange, vdistance, vcpu_to_vnode);
-        if (r) {
-            LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-        numa->vmemrange = vmemrange;
-        numa->vdistance = vdistance;
-        numa->vcpu_to_vnode = vcpu_to_vnode;
-    } else {
-        hvminfo->nr_vcpus = info.max_vcpu_id + 1;
-    }
-
-    for (i = 0; i < hvminfo->nr_vcpus; i++)
-        hvminfo->vcpu_online[i / 8] |= 1 << (i & 7);
-
-    config->hvminfo = hvminfo;
-
-    config->lapic_base_address = LAPIC_BASE_ADDRESS;
-    config->lapic_id = acpi_lapic_id;
-    config->acpi_revision = 5;
-
-    rc = 0;
-out:
-    return rc;
-}
-
-int libxl__dom_load_acpi(libxl__gc *gc,
-                         const libxl_domain_build_info *b_info,
-                         struct xc_dom_image *dom)
-{
-    struct acpi_config config = {0};
-    struct libxl_acpi_ctxt libxl_ctxt;
-    int rc = 0, acpi_pages_num;
-    void *acpi_pages;
-    unsigned long page_mask;
-
-    if (b_info->type != LIBXL_DOMAIN_TYPE_PVH)
-        goto out;
-
-    libxl_ctxt.page_size = XC_DOM_PAGE_SIZE(dom);
-    libxl_ctxt.page_shift =  XC_DOM_PAGE_SHIFT(dom);
-    page_mask = (1UL << libxl_ctxt.page_shift) - 1;
-
-    libxl_ctxt.c.mem_ops.alloc = mem_alloc;
-    libxl_ctxt.c.mem_ops.v2p = virt_to_phys;
-    libxl_ctxt.c.mem_ops.free = acpi_mem_free;
-
-    rc = init_acpi_config(gc, dom, b_info, &config);
-    if (rc) {
-        LOG(ERROR, "init_acpi_config failed (rc=%d)", rc);
-        goto out;
-    }
-
-    config.rsdp = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
-    config.infop = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
-    /* Pages to hold ACPI tables */
-    acpi_pages =  libxl__malloc(gc, (NUM_ACPI_PAGES + 1) *
-                                libxl_ctxt.page_size);
-
-    /*
-     * Set up allocator memory.
-     * Start next to acpi_info page to avoid fracturing e820.
-     */
-    libxl_ctxt.alloc_base_paddr = ACPI_INFO_PHYSICAL_ADDRESS +
-        libxl_ctxt.page_size;
-    libxl_ctxt.alloc_base_vaddr = libxl_ctxt.alloc_currp =
-        (unsigned long)acpi_pages;
-    libxl_ctxt.alloc_end = (unsigned long)acpi_pages +
-        (NUM_ACPI_PAGES * libxl_ctxt.page_size);
-
-    /* Build the tables. */
-    rc = acpi_build_tables(&libxl_ctxt.c, &config);
-    if (rc) {
-        LOG(ERROR, "acpi_build_tables failed with %d", rc);
-        goto out;
-    }
-
-    /* Calculate how many pages are needed for the tables. */
-    acpi_pages_num =
-        ((libxl_ctxt.alloc_currp - (unsigned long)acpi_pages)
-         >> libxl_ctxt.page_shift) +
-        ((libxl_ctxt.alloc_currp & page_mask) ? 1 : 0);
-
-    dom->acpi_modules[0].data = (void *)config.rsdp;
-    dom->acpi_modules[0].length = 64;
-    /*
-     * Some Linux versions cannot properly process hvm_start_info.rsdp_paddr
-     * and so we need to put RSDP in location that can be discovered by ACPI's
-     * standard search method, in R-O BIOS memory (we chose last 64 bytes)
-     */
-    if (strcmp(dom->parms.guest_os, "linux") ||
-        elf_xen_feature_get(XENFEAT_linux_rsdp_unrestricted,
-                            dom->parms.f_supported))
-        dom->acpi_modules[0].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
-            (1 + acpi_pages_num) * libxl_ctxt.page_size;
-    else
-        dom->acpi_modules[0].guest_addr_out = 0x100000 - 64;
-
-    dom->acpi_modules[1].data = (void *)config.infop;
-    dom->acpi_modules[1].length = 4096;
-    dom->acpi_modules[1].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS;
-
-    dom->acpi_modules[2].data = acpi_pages;
-    dom->acpi_modules[2].length = acpi_pages_num  << libxl_ctxt.page_shift;
-    dom->acpi_modules[2].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
-        libxl_ctxt.page_size;
-
-out:
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_x86_acpi.h b/tools/libxl/libxl_x86_acpi.h
deleted file mode 100644 (file)
index d404637..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- *
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
- */
-
-#ifndef LIBXL_X86_ACPI_H
-#define LIBXL_X86_ACPI_H
-
-#include "libxl_internal.h"
-
-#define ASSERT(x) assert(x)
-
-static inline int test_bit(unsigned int b, const void *p)
-{
-    return !!(((const uint8_t *)p)[b>>3] & (1u<<(b&7)));
-}
-
-#endif /* LIBXL_X_86_ACPI_H */
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/libxl_xshelp.c b/tools/libxl/libxl_xshelp.c
deleted file mode 100644 (file)
index 751cd94..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h" /* must come before any other headers */
-
-#include "libxl_internal.h"
-
-char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array)
-{
-    char **kvs;
-    int i, length;
-
-    if (!array)
-        return NULL;
-
-    length = array->count;
-    if (!length)
-        return NULL;
-
-    kvs = libxl__calloc(gc, length + 2, sizeof(char *));
-    if (kvs) {
-        for (i = 0; i < length; i += 2) {
-            void *ptr;
-
-            flexarray_get(array, i, &ptr);
-            kvs[i] = (char *) ptr;
-            flexarray_get(array, i + 1, &ptr);
-            kvs[i + 1] = (char *) ptr;
-        }
-        kvs[i] = NULL;
-        kvs[i + 1] = NULL;
-    }
-    return kvs;
-}
-
-int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
-                           const char *dir, char *kvs[],
-                           struct xs_permissions *perms,
-                           unsigned int num_perms)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *path;
-    int i;
-
-    if (!kvs)
-        return 0;
-
-    for (i = 0; kvs[i] != NULL; i += 2) {
-        path = GCSPRINTF("%s/%s", dir, kvs[i]);
-        if (path && kvs[i + 1]) {
-            int length = strlen(kvs[i + 1]);
-            xs_write(ctx->xsh, t, path, kvs[i + 1], length);
-            if (perms)
-                xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
-        }
-    }
-    return 0;
-}
-
-int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
-                     const char *dir, char *kvs[])
-{
-    return libxl__xs_writev_perms(gc, t, dir, kvs, NULL, 0);
-}
-
-int libxl__xs_writev_atonce(libxl__gc *gc,
-                            const char *dir, char *kvs[])
-{
-    int rc;
-    xs_transaction_t t = XBT_NULL;
-
-    for (;;) {
-        rc = libxl__xs_transaction_start(gc, &t);
-        if (rc) goto out;
-
-        rc = libxl__xs_writev(gc, t, dir, kvs);
-        if (rc) goto out;
-
-        rc = libxl__xs_transaction_commit(gc, &t);
-        if (!rc) break;
-        if (rc<0) goto out;
-    }
-
-out:
-    libxl__xs_transaction_abort(gc, &t);
-
-    return rc;
-
-}
-
-int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t,
-                      const char *path, const char *fmt, va_list ap)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *s;
-    bool ok;
-
-    s = libxl__vsprintf(gc, fmt, ap);
-
-    ok = xs_write(ctx->xsh, t, path, s, strlen(s));
-    if (!ok) {
-        LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, s);
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t,
-                     const char *path, const char *fmt, ...)
-{
-    va_list ap;
-    int rc;
-
-    va_start(ap, fmt);
-    rc = libxl__xs_vprintf(gc, t, path, fmt, ap);
-    va_end(ap);
-
-    return rc;
-}
-
-char * libxl__xs_read(libxl__gc *gc, xs_transaction_t t, const char *path)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *ptr;
-
-    ptr = xs_read(ctx->xsh, t, path, NULL);
-    libxl__ptr_add(gc, ptr);
-    return ptr;
-}
-
-char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char *s = xs_get_domain_path(ctx->xsh, domid);
-    if (!s) {
-        LOGED(ERROR, domid, "Failed to get dompath");
-        return NULL;
-    }
-    libxl__ptr_add(gc, s);
-    return s;
-}
-
-char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t,
-                           const char *path, unsigned int *nb)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    char **ret = NULL;
-    ret = xs_directory(ctx->xsh, t, path, nb);
-    libxl__ptr_add(gc, ret);
-    return ret;
-}
-
-int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t,
-                    const char *path, struct xs_permissions *perms,
-                    unsigned int num_perms)
-{
-    libxl_ctx *ctx = libxl__gc_owner(gc);
-    bool ok;
-
-    ok = xs_write(ctx->xsh, t, path, "", 0);
-    if (!ok) {
-        LOGE(ERROR, "xenstore write failed: `%s' = ''", path);
-        return ERROR_FAIL;
-    }
-
-    ok = xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
-    if (!ok) {
-        LOGE(ERROR, "xenstore set permissions failed on `%s'", path);
-        return ERROR_FAIL;
-    }
-
-    return 0;
-}
-
-char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid)
-{
-    char *s = GCSPRINTF("/libxl/%i", domid);
-    if (!s)
-        LOGD(ERROR, domid, "cannot allocate create paths");
-    return s;
-}
-
-int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t,
-                             const char *path, const char **result_out)
-{
-    char *result = libxl__xs_read(gc, t, path);
-    if (!result) {
-        LOGE(ERROR, "xenstore read failed: `%s'", path);
-        return ERROR_FAIL;
-    }
-    *result_out = result;
-    return 0;
-}
-
-int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t,
-                           const char *path, const char **result_out)
-{
-    char *result = libxl__xs_read(gc, t, path);
-    if (!result && errno != ENOENT) {
-        LOGE(ERROR, "xenstore read failed: `%s'", path);
-        return ERROR_FAIL;
-    }
-    *result_out = result;
-    return 0;
-}
-
-int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t,
-                            const char *path, const char *string)
-{
-    size_t length = strlen(string);
-    if (!xs_write(CTX->xsh, t, path, string, length)) {
-        LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, string);
-        return ERROR_FAIL;
-    }
-    return 0;
-}
-
-int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path)
-{
-    if (!xs_rm(CTX->xsh, t, path)) {
-        if (errno == ENOENT)
-            return 0;
-
-        LOGE(ERROR, "xenstore rm failed: `%s'", path);
-        return ERROR_FAIL;
-    }
-    return 0;
-}
-
-int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t)
-{
-    assert(!*t);
-    *t = xs_transaction_start(CTX->xsh);
-    if (!*t) {
-        LOGE(ERROR, "could not create xenstore transaction");
-        return ERROR_FAIL;
-    }
-    return 0;
-}
-
-int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t)
-{
-    assert(*t);
-
-    if (!xs_transaction_end(CTX->xsh, *t, 0)) {
-        *t = 0;
-        if (errno == EAGAIN)
-            return +1;
-
-        LOGE(ERROR, "could not commit xenstore transaction");
-        return ERROR_FAIL;
-    }
-
-    *t = 0;
-    return 0;
-}
-
-void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t)
-{
-    if (!*t)
-        return;
-
-    if (!xs_transaction_end(CTX->xsh, *t, 1))
-        LOGE(ERROR, "could not abort xenstore transaction");
-
-    *t = 0;
-}
-
-int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t,
-                           const char *user_path)
-{
-    unsigned int nb = 0;
-    char *path, *last, *val;
-    int rc;
-
-    /* A path and transaction must be provided by the caller */
-    assert(user_path && t);
-
-    path = libxl__strdup(gc, user_path);
-    if (!xs_rm(CTX->xsh, t, path)) {
-        if (errno != ENOENT)
-            LOGE(DEBUG, "unable to remove path %s", path);
-        rc = ERROR_FAIL;
-        goto out;
-    }
-
-    for (last = strrchr(path, '/'); last != NULL; last = strrchr(path, '/')) {
-        *last = '\0';
-
-        if (!strlen(path)) break;
-
-        val = libxl__xs_read(gc, t, path);
-        if (!val || strlen(val) != 0) break;
-
-        if (!libxl__xs_directory(gc, t, path, &nb) || nb != 0) break;
-
-        if (!xs_rm(CTX->xsh, t, path)) {
-            if (errno != ENOENT)
-                LOGE(DEBUG, "unable to remove path %s", path);
-            rc = ERROR_FAIL;
-            goto out;
-        }
-    }
-    rc = 0;
-
-out:
-    return rc;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/osdeps.c b/tools/libxl/osdeps.c
deleted file mode 100644 (file)
index 0e0b447..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2009      Citrix Ltd.
- * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
- *
- * This program 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; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program 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.
- */
-
-#include "libxl_osdeps.h"
-
-#include <unistd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/time.h>
-#include <stdlib.h>
-
-#ifdef NEED_OWN_ASPRINTF
-
-int vasprintf(char **buffer, const char *fmt, va_list ap)
-{
-    int size = 0;
-    int nchars;
-
-    *buffer = 0;
-
-    nchars = vsnprintf(*buffer, 0, fmt, ap);
-
-    if (nchars >= size)
-    {
-        char *tmpbuff;
-        /* Reallocate buffer now that we know how much space is needed. */
-        size = nchars+1;
-        tmpbuff = (char*)realloc(*buffer, size);
-
-
-        if (tmpbuff == NULL) { /* we need to free it*/
-            free(*buffer);
-            return -1;
-        }
-
-        *buffer=tmpbuff;
-        /* Try again. */
-        nchars = vsnprintf(*buffer, size, fmt, ap);
-    }
-
-    if (nchars < 0) return nchars;
-    return size;
-}
-
-int asprintf(char **buffer, char *fmt, ...)
-{
-    int status;
-    va_list ap;
-
-    va_start (ap, fmt);
-    status = vasprintf (buffer, fmt, ap);
-    va_end (ap);
-    return status;
-}
-
-#endif
-
-/*
- * Local variables:
- * mode: C
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/tools/libxl/test_common.c b/tools/libxl/test_common.c
deleted file mode 100644 (file)
index c6bbbab..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "test_common.h"
-
-libxl_ctx *ctx;
-
-void test_common_setup(int level)
-{
-    xentoollog_logger_stdiostream *logger_s
-        = xtl_createlogger_stdiostream(stderr, level,  0);
-    assert(logger_s);
-
-    xentoollog_logger *logger = (xentoollog_logger*)logger_s;
-
-    int rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, logger);
-    assert(!rc);
-}
-
-struct timeval now;
-
-void test_common_get_now(void)
-{
-    int r = gettimeofday(&now, 0);  assert(!r);
-}
-
-int poll_nfds, poll_nfds_allocd;
-struct pollfd *poll_fds;
-int poll_timeout;
-
-void test_common_beforepoll(void)
-{
-    for (;;) {
-        test_common_get_now();
-
-        poll_timeout = -1;
-        poll_nfds = poll_nfds_allocd;
-        int rc = libxl_osevent_beforepoll(ctx, &poll_nfds, poll_fds,
-                                          &poll_timeout, now);
-        if (!rc) return;
-        assert(rc == ERROR_BUFFERFULL);
-
-        assert(poll_nfds > poll_nfds_allocd);
-        poll_fds = realloc(poll_fds, poll_nfds * sizeof(poll_fds[0]));
-        assert(poll_fds);
-        poll_nfds_allocd = poll_nfds;
-    }
-}
-
-void test_common_dopoll(void) {
-    errno = 0;
-    int r = poll(poll_fds, poll_nfds, poll_timeout);
-    fprintf(stderr, "poll: r=%d errno=%s\n", r, strerror(errno));
-}
-
-void test_common_afterpoll(void)
-{
-    test_common_get_now();
-    libxl_osevent_afterpoll(ctx, poll_nfds, poll_fds, now);
-}
diff --git a/tools/libxl/test_common.h b/tools/libxl/test_common.h
deleted file mode 100644 (file)
index 10c7166..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef TEST_COMMON_H
-#define TEST_COMMON_H
-
-#include "libxl.h"
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-void test_common_setup(int level);
-
-extern libxl_ctx *ctx;
-
-void test_common_get_now(void);
-
-extern struct timeval now;
-
-void test_common_beforepoll(void);
-void test_common_dopoll(void);
-void test_common_afterpoll(void);
-
-extern int poll_nfds, poll_nfds_allocd;
-extern struct pollfd *poll_fds;
-extern int poll_timeout;
-
-#endif /*TEST_COMMON_H*/
diff --git a/tools/libxl/test_fdderegrace.c b/tools/libxl/test_fdderegrace.c
deleted file mode 100644 (file)
index f57965f..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "test_common.h"
-#include "libxl_test_fdevent.h"
-
-int main(int argc, char **argv) {
-    int rc, i;
-    libxl_asyncop_how how;
-    libxl_event *event;
-
-    test_common_setup(XTL_DEBUG);
-
-    how.callback = NULL;
-    how.u.for_event = 1;
-
-    int fd = open("/dev/null", O_RDONLY);
-    assert(fd > 0);
-
-    rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
-    assert(!rc);
-
-    test_common_beforepoll();
-
-    rc = libxl_ao_abort(ctx, &how);
-    assert(!rc);
-
-    rc = libxl_event_check(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0);
-    assert(!rc);
-    assert(event);
-    assert(event->for_user == how.u.for_event);
-    assert(event->type == LIBXL_EVENT_TYPE_OPERATION_COMPLETE);
-    assert(event->u.operation_complete.rc == ERROR_ABORTED);
-
-    close(fd);
-
-    test_common_dopoll();
-
-    for (i=0; i<poll_nfds; i++) {
-        if (poll_fds[i].fd == fd && (poll_fds[i].revents & POLLNVAL)) {
-            fprintf(stderr, "POLLNVAL on fd=%d in slot i=%d as expected\n",
-                    fd, i);
-            goto found;
-        }
-    }
-    abort();
- found:;
-
-    int fd2 = open("/dev/null", O_RDONLY);
-    assert(fd2 == fd);
-
-    how.u.for_event++;
-    rc = libxl_test_fdevent(ctx, fd, POLLIN, &how);
-    assert(!rc);
-
-    test_common_afterpoll();
-
-    fprintf(stderr, "complete\n");
-}
diff --git a/tools/libxl/test_timedereg.c b/tools/libxl/test_timedereg.c
deleted file mode 100644 (file)
index 0081ce3..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "test_common.h"
-#include "libxl_test_timedereg.h"
-
-int main(int argc, char **argv) {
-    int rc;
-
-    test_common_setup(XTL_DEBUG);
-
-    rc = libxl_test_timedereg(ctx, 0);
-    assert(!rc);
-}
index 0d1549dd3a814490c6e36c8127eed143a5b0f304..cbe1569cc58d8c055fad78e113088e976019cb1c 100644 (file)
@@ -50,10 +50,10 @@ xenlight.mli: xenlight.mli.in _libxl_types.mli.in
          < xenlight.mli.in > xenlight.mli.tmp
        $(Q)mv xenlight.mli.tmp xenlight.mli
 
-_libxl_types.ml.in _libxl_types.mli.in _libxl_types.inc: genwrap.py $(XEN_ROOT)/tools/libxl/libxl_types.idl \
-                $(XEN_ROOT)/tools/libxl/idl.py
-       PYTHONPATH=$(XEN_ROOT)/tools/libxl $(PYTHON) genwrap.py \
-               $(XEN_ROOT)/tools/libxl/libxl_types.idl \
+_libxl_types.ml.in _libxl_types.mli.in _libxl_types.inc: genwrap.py $(XEN_ROOT)/tools/libs/light/libxl_types.idl \
+                $(XEN_ROOT)/tools/libs/light/idl.py
+       PYTHONPATH=$(XEN_ROOT)/tools/libs/light $(PYTHON) genwrap.py \
+               $(XEN_ROOT)/tools/libs/light/libxl_types.idl \
                _libxl_types.mli.in _libxl_types.ml.in _libxl_types.inc
 
 libs: $(LIBS)