[PATCH 2/5] libxl: call hotplug scripts from libxl for vbd

Roger Pau Monne
Mon, 16 Apr 2012 15:06:41 -0000

This is a rather big change, that adds the necessary machinery to perform
hotplug script calling from libxl for Linux. This patch launches the necessary
scripts to attach and detach PHY and TAP backend types (Qemu doesn''t
use hotplug
scripts).

Here''s a list of the major changes introduced:

 * libxl_device_disk_add makes use of the new event library and takes the
   optional parameter libxl_asyncop_how, to choose how the operation has to be
   issued (sync or async).

 * libxl_device_disk_add waits for backend to switch to state 2 (XenbusInitWait)
   and then launches the hotplug script.

 * libxl__devices_destroy no longer calls libxl__device_destroy, and instead
   calls libxl__initiate_device_remove, so we can disconnect the device and
   execute the necessary hotplug scripts instead of just deleting the backend
   entries. So libxl__devices_destroy now uses the event library, and so does
   libxl_domain_destroy.

 * Since libxl__devices_destroy calls multiple times
   libxl__initiate_device_remove, this function now returns a different value
   regarding the actions taken (if an event was added or not). The user has to
   call libxl__ao_complete after using this function if necessary.

 * The internal API for hotplug scripts has been added, which consists of one
   function; libxl__device_hotplug that takes the device and the action to
   execute.

 * Linux hotplug scripts are called by setting the necessary env variables to
   emulate udev rules, so there''s no need to change them (although a
rework to
   pass this as parameters insted of env variables would be suitable, so both
   NetBSD and Linux hotplug scripts take the same parameters).

 * Added a check in xen-hotplug-common.sh, so scripts are only executed from
   udev when using xend. xl adds an execute_udev=n to xenstore device backend
   and passes XL_CALL=y to the env of the script call, and udev calls check that
   execute_udev is not set and XL_CALL is empty also.

I''ve also modified the python binding for pyxl_domain_destroy, that now
take the
ao_how parameter, but I''m not really sure if it''s done
correctly, let''s just say
that it compiles.

Signed-off-by: Roger Pau Monne <roger.pau@citrix.com>
---
 tools/hotplug/Linux/xen-hotplug-common.sh |    6 ++
 tools/libxl/Makefile                      |    3 +-
 tools/libxl/libxl.c                       |  104 ++++++++++++++++++----
 tools/libxl/libxl.h                       |    7 +-
 tools/libxl/libxl_create.c                |    4 +-
 tools/libxl/libxl_device.c                |  140 +++++++++++++++++++++++++++--
 tools/libxl/libxl_dm.c                    |    4 +-
 tools/libxl/libxl_hotplug.c               |   67 ++++++++++++++
 tools/libxl/libxl_internal.h              |   43 +++++++++-
 tools/libxl/libxl_linux.c                 |  117 ++++++++++++++++++++++++
 tools/libxl/xl_cmdimpl.c                  |   14 ++--
 tools/python/xen/lowlevel/xl/xl.c         |    5 +-
 12 files changed, 474 insertions(+), 40 deletions(-)
 create mode 100644 tools/libxl/libxl_hotplug.c

diff --git a/tools/hotplug/Linux/xen-hotplug-common.sh
b/tools/hotplug/Linux/xen-hotplug-common.sh
index 8f6557d..dc3e867 100644
--- a/tools/hotplug/Linux/xen-hotplug-common.sh
+++ b/tools/hotplug/Linux/xen-hotplug-common.sh
@@ -15,6 +15,12 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #

+# Hack to prevent the execution of hotplug scripts form udev if the domain
+# has ben launched from libxl
+execute_udev=`xenstore-read $XENBUS_PATH/execute_udev 2>/dev/null`
+if [ -n "$execute_udev" ] && [ -z "$XL_CALL" ];
then
+    exit 0
+fi

 dir=$(dirname "$0")
 . "$dir/hotplugpath.sh"
diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index b30d11f..ac8b810 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -53,7 +53,8 @@ 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_qmp.o libxl_event.o libxl_fork.o $(LIBXL_OBJS-y)
+			libxl_qmp.o libxl_event.o libxl_fork.o libxl_hotplug.o \
+			$(LIBXL_OBJS-y)
 LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o

 $(LIBXL_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest)
$(CFLAGS_libxenstore) $(CFLAGS_libblktapctl) -include $(XEN_ROOT)/tools/config.h
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index 16ebef3..fb4fbf2 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -1062,13 +1062,15 @@ void libxl_evdisable_disk_eject(libxl_ctx *ctx,
libxl_evgen_disk_eject *evg) {
     GC_FREE;
 }

-int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid)
+int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
+                         const libxl_asyncop_how *ao_how)
 {
-    GC_INIT(ctx);
+    AO_CREATE(ctx, domid, ao_how);
     char *dom_path;
     char *vm_path;
     char *pid;
     int rc, dm_present;
+    int rc_ao = 0;

     rc = libxl_domain_info(ctx, NULL, domid);
     switch(rc) {
@@ -1110,7 +1112,8 @@ int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid)

         libxl__qmp_cleanup(gc, domid);
     }
-    if (libxl__devices_destroy(gc, domid) < 0)
+    rc_ao = libxl__devices_destroy(egc, ao, domid);
+    if (rc_ao < 0)
         LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
                    "libxl__devices_destroy failed for %d", domid);

@@ -1133,9 +1136,10 @@ int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid)
         goto out;
     }
     rc = 0;
+
 out:
-    GC_FREE;
-    return rc;
+    if (rc_ao) return AO_ABORT(rc_ao);
+    return AO_INPROGRESS;
 }

 int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
libxl_console_type type)
@@ -1313,9 +1317,11 @@ static int libxl__device_from_disk(libxl__gc *gc,
uint32_t domid,
     return 0;
 }

-int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid, libxl_device_disk
*disk)
+int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
+                          libxl_device_disk *disk,
+                          const libxl_asyncop_how *ao_how)
 {
-    GC_INIT(ctx);
+    AO_CREATE(ctx, domid, ao_how);
     flexarray_t *front;
     flexarray_t *back;
     char *dev;
@@ -1330,7 +1336,7 @@ int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
libxl_device_disk *dis
         rc = ERROR_NOMEM;
         goto out;
     }
-    back = flexarray_make(16, 1);
+    back = flexarray_make(20, 1);
     if (!back) {
         rc = ERROR_NOMEM;
         goto out_free;
@@ -1361,6 +1367,11 @@ int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
libxl_device_disk *dis
             flexarray_append(back, "params");
             flexarray_append(back, dev);

+            flexarray_append(back, "script");
+            flexarray_append(back, libxl__sprintf(gc, "%s/%s",
+                                                  libxl_xen_script_dir_path(),
+                                                  "block"));
+
             assert(device.backend_kind == LIBXL__DEVICE_KIND_VBD);
             break;
         case LIBXL_DISK_BACKEND_TAP:
@@ -1374,6 +1385,11 @@ int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
libxl_device_disk *dis
                 libxl__device_disk_string_of_format(disk->format),
                 disk->pdev_path));

+            flexarray_append(back, "script");
+            flexarray_append(back, libxl__sprintf(gc, "%s/%s",
+                             libxl_xen_script_dir_path(),
+                             "blktap"));
+
             /* now create a phy device to export the device to the guest */
             goto do_backend_phy;
         case LIBXL_DISK_BACKEND_QDISK:
@@ -1406,6 +1422,9 @@ int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
libxl_device_disk *dis
     flexarray_append(back, disk->readwrite ? "w" : "r");
     flexarray_append(back, "device-type");
     flexarray_append(back, disk->is_cdrom ? "cdrom" :
"disk");
+    /* compatibility addon to keep udev rules */
+    flexarray_append(back, "execute_udev");
+    flexarray_append(back, "n");

     flexarray_append(front, "backend-id");
     flexarray_append(front, libxl__sprintf(gc, "%d",
disk->backend_domid));
@@ -1420,14 +1439,23 @@ int libxl_device_disk_add(libxl_ctx *ctx, uint32_t
domid, libxl_device_disk *dis
                              libxl__xs_kvs_of_flexarray(gc, back,
back->count),
                              libxl__xs_kvs_of_flexarray(gc, front,
front->count));

+    if (disk->backend == LIBXL_DISK_BACKEND_PHY ||
+        disk->backend == LIBXL_DISK_BACKEND_TAP) {
+        rc = libxl__initiate_device_add(egc, ao, &device);
+        if (rc) goto out_free;
+    } else {
+        /* no need to execute hotplug scripts */
+        libxl__ao_complete(egc, ao, 0);
+    }
+
     rc = 0;

 out_free:
     flexarray_free(back);
     flexarray_free(front);
 out:
-    GC_FREE;
-    return rc;
+    if (rc) return AO_ABORT(rc);
+    return AO_INPROGRESS;
 }

 int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid,
@@ -1442,7 +1470,18 @@ int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t
domid,
     if (rc != 0) goto out;

     rc = libxl__initiate_device_remove(egc, ao, &device);
-    if (rc) goto out;
+    switch(rc) {
+    case 1:
+        /* event added */
+        break;
+    case 0:
+        /* no event added */
+        libxl__ao_complete(egc, ao, 0);
+        break;
+    default:
+        /* error */
+        goto out;
+    }

     return AO_INPROGRESS;

@@ -1666,11 +1705,11 @@ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid,
libxl_device_disk *disk)
     ret = 0;

     libxl_device_disk_remove(ctx, domid, disks + i, 0);
-    libxl_device_disk_add(ctx, domid, disk);
+    libxl_device_disk_add(ctx, domid, disk, 0);
     stubdomid = libxl_get_stubdom_id(ctx, domid);
     if (stubdomid) {
         libxl_device_disk_remove(ctx, stubdomid, disks + i, 0);
-        libxl_device_disk_add(ctx, stubdomid, disk);
+        libxl_device_disk_add(ctx, stubdomid, disk, 0);
     }
 out:
     for (i = 0; i < num; i++)
@@ -1909,7 +1948,18 @@ int libxl_device_nic_remove(libxl_ctx *ctx, uint32_t
domid,
     if (rc != 0) goto out;

     rc = libxl__initiate_device_remove(egc, ao, &device);
-    if (rc) goto out;
+    switch(rc) {
+    case 1:
+        /* event added */
+        break;
+    case 0:
+        /* no event added */
+        libxl__ao_complete(egc, ao, 0);
+        break;
+    default:
+        /* error */
+        goto out;
+    }

     return AO_INPROGRESS;

@@ -2256,7 +2306,18 @@ int libxl_device_vkb_remove(libxl_ctx *ctx, uint32_t
domid,
     if (rc != 0) goto out;

     rc = libxl__initiate_device_remove(egc, ao, &device);
-    if (rc) goto out;
+    switch(rc) {
+    case 1:
+        /* event added */
+        break;
+    case 0:
+        /* no event added */
+        libxl__ao_complete(egc, ao, 0);
+        break;
+    default:
+        /* error */
+        goto out;
+    }

     return AO_INPROGRESS;

@@ -2389,7 +2450,18 @@ int libxl_device_vfb_remove(libxl_ctx *ctx, uint32_t
domid,
     if (rc != 0) goto out;

     rc = libxl__initiate_device_remove(egc, ao, &device);
-    if (rc) goto out;
+    switch(rc) {
+    case 1:
+        /* event added */
+        break;
+    case 0:
+        /* no event added */
+        libxl__ao_complete(egc, ao, 0);
+        break;
+    default:
+        /* error */
+        goto out;
+    }

     return AO_INPROGRESS;

diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 50bae2f..b7347be 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -394,7 +394,8 @@ int libxl_domain_suspend(libxl_ctx *ctx,
libxl_domain_suspend_info *info,
 int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid);
 int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid);
 int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid);
-int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid);
+int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
+                         const libxl_asyncop_how *ao_how);
 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 */
@@ -523,7 +524,9 @@ void libxl_vminfo_list_free(libxl_vminfo *list, int nr);
  */

 /* Disks */
-int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid, libxl_device_disk
*disk);
+int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
+                          libxl_device_disk *disk,
+                          const libxl_asyncop_how *ao_how);
 int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid,
                              libxl_device_disk *disk,
                              const libxl_asyncop_how *ao_how);
diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
index 3675afe..de598ad 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -598,7 +598,7 @@ static int do_domain_create(libxl__gc *gc,
libxl_domain_config *d_config,
     store_libxl_entry(gc, domid, &d_config->b_info);

     for (i = 0; i < d_config->num_disks; i++) {
-        ret = libxl_device_disk_add(ctx, domid, &d_config->disks[i]);
+        ret = libxl_device_disk_add(ctx, domid, &d_config->disks[i], 0);
         if (ret) {
             LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
                        "cannot add disk %d to domain: %d", i, ret);
@@ -721,7 +721,7 @@ static int do_domain_create(libxl__gc *gc,
libxl_domain_config *d_config,

 error_out:
     if (domid)
-        libxl_domain_destroy(ctx, domid);
+        libxl_domain_destroy(ctx, domid, 0);

     return ret;
 }
diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c
index c7e057d..eb94bd2 100644
--- a/tools/libxl/libxl_device.c
+++ b/tools/libxl/libxl_device.c
@@ -356,26 +356,95 @@ int libxl__device_disk_dev_number(const char *virtpath,
int *pdisk,
     return -1;
 }

+static int libxl__xs_path_cleanup(libxl__gc *gc, char *path)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    unsigned int nb = 0;
+    char *last;
+
+    if (!path)
+        return 0;
+
+    xs_rm(ctx->xsh, XBT_NULL, path);
+
+    for (last = strrchr(path, ''/''); last != NULL; last =
strrchr(path, ''/'')) {
+        *last = ''\0'';
+        if (!libxl__xs_directory(gc, XBT_NULL, path, &nb))
+            continue;
+        if (nb == 0) {
+            xs_rm(ctx->xsh, XBT_NULL, path);
+        }
+    }
+    return 0;
+}

 typedef struct {
     libxl__ao *ao;
     libxl__ev_devstate ds;
+    libxl__device *dev;
 } libxl__ao_device_remove;

+typedef struct {
+    libxl__ao *ao;
+    libxl__ev_devstate ds;
+    libxl__device *dev;
+} libxl__ao_device_add;
+
 static void device_remove_cleanup(libxl__gc *gc,
                                   libxl__ao_device_remove *aorm) {
     if (!aorm) return;
     libxl__ev_devstate_cancel(gc, &aorm->ds);
 }

+static void device_add_cleanup(libxl__gc *gc,
+                               libxl__ao_device_add *aorm) {
+    if (!aorm) return;
+    libxl__ev_devstate_cancel(gc, &aorm->ds);
+}
+
 static void device_remove_callback(libxl__egc *egc, libxl__ev_devstate *ds,
                                    int rc) {
     libxl__ao_device_remove *aorm = CONTAINER_OF(ds, *aorm, ds);
     libxl__gc *gc = &aorm->ao->gc;
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+
+    rc = libxl__device_hotplug(gc, aorm->dev, DISCONNECT);
+    if (rc)
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to execute hotplug
script for"
+                                          " device %"PRIu32,
aorm->dev->devid);
+    libxl__xs_path_cleanup(gc, libxl__device_frontend_path(gc, aorm->dev));
+    libxl__xs_path_cleanup(gc, libxl__device_backend_path(gc, aorm->dev));
     libxl__ao_complete(egc, aorm->ao, rc);
     device_remove_cleanup(gc, aorm);
 }

+static void device_add_callback(libxl__egc *egc, libxl__ev_devstate *ds,
+                                int rc)
+{
+    libxl__ao_device_add *aorm = CONTAINER_OF(ds, *aorm, ds);
+    libxl__gc *gc = &aorm->ao->gc;
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+
+    rc = libxl__device_hotplug(gc, aorm->dev, CONNECT);
+    if (rc)
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to execute hotplug
script for"
+                                          " device %"PRIu32,
aorm->dev->devid);
+    libxl__ao_complete(egc, aorm->ao, rc);
+    if (aorm)
+        libxl__ev_devstate_cancel(gc, &aorm->ds);
+    return;
+}
+
+/*
+ * Return codes:
+ * 1 Success and event(s) HAVE BEEN added
+ * 0 Success and NO events where added
+ * < 0 Error
+ *
+ * Since this function can be called several times, and several events
+ * can be added to the same context, the user has to call
+ * libxl__ao_complete when necessary (if no events are added).
+ */
 int libxl__initiate_device_remove(libxl__egc *egc, libxl__ao *ao,
                                   libxl__device *dev)
 {
@@ -392,7 +461,6 @@ int libxl__initiate_device_remove(libxl__egc *egc, libxl__ao
*ao,
         goto out_ok;
     if (atoi(state) != 4) {
         libxl__device_destroy_tapdisk(gc, be_path);
-        xs_rm(ctx->xsh, XBT_NULL, be_path);
         goto out_ok;
     }

@@ -413,6 +481,7 @@ retry_transaction:

     aorm = libxl__zalloc(gc, sizeof(*aorm));
     aorm->ao = ao;
+    aorm->dev = dev;
     libxl__ev_devstate_init(&aorm->ds);

     rc = libxl__ev_devstate_wait(gc, &aorm->ds, device_remove_callback,
@@ -420,7 +489,7 @@ retry_transaction:
                                  LIBXL_DESTROY_TIMEOUT * 1000);
     if (rc) goto out_fail;

-    return 0;
+    return 1;

  out_fail:
     assert(rc);
@@ -428,8 +497,51 @@ retry_transaction:
     return rc;

  out_ok:
-    libxl__ao_complete(egc, ao, 0);
+    rc = libxl__device_hotplug(gc, dev, DISCONNECT);
+    libxl__xs_path_cleanup(gc, libxl__device_frontend_path(gc, dev));
+    libxl__xs_path_cleanup(gc, be_path);
+    return 0;
+}
+
+int libxl__initiate_device_add(libxl__egc *egc, libxl__ao *ao,
+                               libxl__device *dev)
+{
+    AO_GC;
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *state_path = libxl__sprintf(gc, "%s/state", be_path);
+    char *state = libxl__xs_read(gc, XBT_NULL, state_path);
+    int rc = 0;
+    libxl__ao_device_add *aorm = 0;
+
+    if (atoi(state) == XenbusStateInitWait)
+        goto out_ok;
+
+    aorm = libxl__zalloc(gc, sizeof(*aorm));
+    aorm->ao = ao;
+    aorm->dev = dev;
+    libxl__ev_devstate_init(&aorm->ds);
+
+    rc = libxl__ev_devstate_wait(gc, &aorm->ds, device_add_callback,
+                                 state_path, XenbusStateInitWait,
+                                 LIBXL_INIT_TIMEOUT * 1000);
+    if (rc) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to initialize device
%"
+                                          PRIu32, dev->devid);
+        libxl__ev_devstate_cancel(gc, &aorm->ds);
+        goto out_fail;
+    }
+
     return 0;
+
+out_fail:
+    assert(rc);
+    device_add_cleanup(gc, aorm);
+    return rc;
+out_ok:
+    rc = libxl__device_hotplug(gc, dev, CONNECT);
+    libxl__ao_complete(egc, ao, 0);
+    return rc;
 }

 int libxl__device_destroy(libxl__gc *gc, libxl__device *dev)
@@ -446,13 +558,14 @@ int libxl__device_destroy(libxl__gc *gc, libxl__device
*dev)
     return 0;
 }

-int libxl__devices_destroy(libxl__gc *gc, uint32_t domid)
+int libxl__devices_destroy(libxl__egc *egc, libxl__ao *ao, uint32_t domid)
 {
+    AO_GC;
     libxl_ctx *ctx = libxl__gc_owner(gc);
     char *path;
     unsigned int num_kinds, num_devs;
     char **kinds = NULL, **devs = NULL;
-    int i, j;
+    int i, j, events = 0, rc;
     libxl__device dev;
     libxl__device_kind kind;

@@ -482,11 +595,24 @@ int libxl__devices_destroy(libxl__gc *gc, uint32_t domid)
                 dev.domid = domid;
                 dev.kind = kind;
                 dev.devid = atoi(devs[j]);
-
-                libxl__device_destroy(gc, &dev);
+                rc = libxl__initiate_device_remove(egc, ao, &dev);
+                switch(rc) {
+                case 1:
+                    events++;
+                    break;
+                case 0:
+                    /* no events added */
+                    break;
+                default:
+                    LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Could not remove
device "
+                                                      "%s", path);
+                }
             }
         }
     }
+    /* Check if any events where added */
+    if (!events)
+        libxl__ao_complete(egc, ao, 0);

     /* console 0 frontend directory is not under
/local/domain/<domid>/device */
     path = libxl__sprintf(gc, "/local/domain/%d/console/backend",
domid);
diff --git a/tools/libxl/libxl_dm.c b/tools/libxl/libxl_dm.c
index 30908d1..2d51a7f 100644
--- a/tools/libxl/libxl_dm.c
+++ b/tools/libxl/libxl_dm.c
@@ -790,7 +790,7 @@ retry_transaction:
             goto retry_transaction;

     for (i = 0; i < dm_config.num_disks; i++) {
-        ret = libxl_device_disk_add(ctx, dm_domid, &dm_config.disks[i]);
+        ret = libxl_device_disk_add(ctx, dm_domid, &dm_config.disks[i], 0);
         if (ret)
             goto out_free;
     }
@@ -1044,7 +1044,7 @@ int libxl__destroy_device_model(libxl__gc *gc, uint32_t
domid)
             goto out;
         }
         LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Device model is a stubdom,
domid=%d", stubdomid);
-        ret = libxl_domain_destroy(ctx, stubdomid);
+        ret = libxl_domain_destroy(ctx, stubdomid, 0);
         if (ret)
             goto out;

diff --git a/tools/libxl/libxl_hotplug.c b/tools/libxl/libxl_hotplug.c
new file mode 100644
index 0000000..2eace0c
--- /dev/null
+++ b/tools/libxl/libxl_hotplug.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012
+ * Author Roger Pau Monne <roger.pau@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"
+
+/*
+ * Generic hotplug helpers
+ *
+ * This helpers are both used by Linux and NetBSD hotplug script
+ * dispatcher functions.
+ */
+
+static void hotplug_exited(libxl__egc *egc, libxl__ev_child *child,
+                           pid_t pid, int status)
+{
+    libxl__hotplug_state *hs = CONTAINER_OF(child, *hs, child);
+    EGC_GC;
+
+    if (status) {
+        libxl_report_child_exitstatus(CTX, hs->rc ? LIBXL__LOG_ERROR
+                                                  : LIBXL__LOG_WARNING,
+                                      "hotplug child", pid, status);
+    }
+}
+
+int libxl__hotplug_exec(libxl__gc *gc, char *arg0, char **args, char **env)
+{
+    int rc = 0;
+    pid_t pid = -1;
+    libxl__hotplug_state *hs;
+
+    hs = libxl__zalloc(gc, sizeof(*hs));
+
+    pid = libxl__ev_child_fork(gc, &hs->child, hotplug_exited);
+    if (pid == -1) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    if (!pid) {
+        /* child */
+        signal(SIGCHLD, SIG_DFL);
+        libxl__exec(-1, -1, -1, arg0, args, env);
+    }
+out:
+    if (libxl__ev_child_inuse(&hs->child)) {
+        hs->rc = rc;
+        /* we will get a callback when the child dies */
+        return 0;
+    }
+
+    assert(rc);
+    return rc;
+}
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 2181774..a39346c 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -70,6 +70,7 @@
 #include "_libxl_types_internal_json.h"

 #define LIBXL_DESTROY_TIMEOUT 10
+#define LIBXL_INIT_TIMEOUT 10
 #define LIBXL_DEVICE_MODEL_START_TIMEOUT 10
 #define LIBXL_XENCONSOLE_LIMIT 1048576
 #define LIBXL_XENCONSOLE_PROTOCOL "vt100"
@@ -455,6 +456,37 @@ _hidden bool libxl__xs_mkdir(libxl__gc *gc,
xs_transaction_t t,

 _hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid);

+/*
+ * Hotplug handling
+ *
+ * Hotplug scripts are launched automatically by libxl at guest creation &
+ * shutdown/destroy.
+ */
+
+/* Action to pass to hotplug caller functions */
+typedef enum {
+    CONNECT = 1,
+    DISCONNECT = 2
+} libxl__hotplug_action;
+
+typedef struct libxl__hotplug_state libxl__hotplug_state;
+
+struct libxl__hotplug_state {
+    /* public, result, caller may only read in callback */
+    int rc;
+    /* private for implementation */
+    libxl__ev_child child;
+};
+
+/*
+ * libxl__hotplug_exec is an internal function that should not be used
+ * directly, all hotplug script calls have to implement and use
+ * libxl__device_hotplug.
+ */
+_hidden int libxl__device_hotplug(libxl__gc *gc, libxl__device *dev,
+                                  libxl__hotplug_action action);
+_hidden int libxl__hotplug_exec(libxl__gc *gc, char *arg0, char **args,
+                                char **env);

 /*
  * Event generation functions provided by the libxl event core to the
@@ -740,7 +772,8 @@ _hidden char *libxl__device_frontend_path(libxl__gc *gc,
libxl__device *device);
 _hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path,
                                       libxl__device *dev);
 _hidden int libxl__device_destroy(libxl__gc *gc, libxl__device *dev);
-_hidden int libxl__devices_destroy(libxl__gc *gc, uint32_t domid);
+_hidden int libxl__devices_destroy(libxl__egc *egc, libxl__ao *ao,
+                                   uint32_t domid);
 _hidden int libxl__wait_for_backend(libxl__gc *gc, char *be_path, char *state);

 /*
@@ -763,6 +796,14 @@ _hidden int libxl__wait_for_backend(libxl__gc *gc, char
*be_path, char *state);
 _hidden int libxl__initiate_device_remove(libxl__egc*, libxl__ao*,
                                           libxl__device *dev);

+/* Arranges that dev will be added to the guest, and the
+ * hotplug scripts will be executed (if necessary). When
+ * this is done, the ao will be completed. An error
+ * return from libxl__initiate_device_add means that the ao
+ * will _not_ be completed and the caller must do so. */
+_hidden int libxl__initiate_device_add(libxl__egc*, libxl__ao*,
+                                       libxl__device *dev);
+
 /*
  * libxl__ev_devstate - waits a given time for a device to
  * reach a given state.  Follows the libxl_ev_* conventions.
diff --git a/tools/libxl/libxl_linux.c b/tools/libxl/libxl_linux.c
index 925248b..9a9e44a 100644
--- a/tools/libxl/libxl_linux.c
+++ b/tools/libxl/libxl_linux.c
@@ -25,3 +25,120 @@ int libxl__try_phy_backend(mode_t st_mode)

     return 1;
 }
+
+/* Hotplug scripts helpers */
+static char **get_hotplug_env(libxl__gc *gc, libxl__device *dev)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *script;
+    flexarray_t *f_env;
+    int nr = 0;
+
+    script = libxl__xs_read(gc, XBT_NULL,
+                            libxl__sprintf(gc, "%s/%s", be_path,
"script"));
+    if (!script) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to read script from
%s",
+                                          be_path);
+        return NULL;
+    }
+
+    f_env = flexarray_make(11, 1);
+    if (!f_env) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
+                   "unable to create environment array");
+        return NULL;
+    }
+
+    flexarray_set(f_env, nr++, "script");
+    flexarray_set(f_env, nr++, script);
+    flexarray_set(f_env, nr++, "XENBUS_TYPE");
+    flexarray_set(f_env, nr++, (char *)
+                  libxl__device_kind_to_string(dev->backend_kind));
+    flexarray_set(f_env, nr++, "XENBUS_PATH");
+    flexarray_set(f_env, nr++,
+                  libxl__sprintf(gc, "backend/%s/%u/%d",
+                  libxl__device_kind_to_string(dev->backend_kind),
+                  dev->domid, dev->devid));
+    flexarray_set(f_env, nr++, "XENBUS_BASE_PATH");
+    flexarray_set(f_env, nr++, "backend");
+    /* compatibility addon to keep udev rules */
+    flexarray_set(f_env, nr++, "XL_CALL");
+    flexarray_set(f_env, nr++, "y");
+
+    flexarray_set(f_env, nr++, NULL);
+
+    return (char **) flexarray_contents(f_env);
+}
+
+/* Hotplug scripts caller functions */
+
+static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
+                               libxl__hotplug_action action)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *be_path = libxl__device_backend_path(gc, dev);
+    char *what, *script;
+    char **args, **env;
+    int nr = 0, rc = 0;
+    flexarray_t *f_args;
+
+    script = libxl__xs_read(gc, XBT_NULL,
+                            libxl__sprintf(gc, "%s/%s", be_path,
"script"));
+    if (!script) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to read script from
%s",
+                                          be_path);
+        return -1;
+    }
+
+    env = get_hotplug_env(gc, dev);
+    if (!env)
+        return -1;
+
+    f_args = flexarray_make(3, 1);
+    if (!f_args) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to create arguments
array");
+        return -1;
+    }
+
+    flexarray_set(f_args, nr++, script);
+    flexarray_set(f_args, nr++, action == CONNECT ? "add" :
"remove");
+    flexarray_set(f_args, nr++, NULL);
+
+    args = (char **) flexarray_contents(f_args);
+    what = libxl__sprintf(gc, "%s %s", args[0],
+                          action == CONNECT ? "connect" :
"disconnect");
+    LIBXL__LOG(ctx, LIBXL__LOG_DEBUG,
+               "Calling hotplug script: %s %s",
+               args[0], args[1]);
+    rc = libxl__hotplug_exec(gc, args[0], args, env);
+    if (rc) {
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable execute hotplug scripts
for "
+                                          "device %"PRIu32,
dev->devid);
+        goto out_free;
+    }
+
+    rc = 0;
+
+out_free:
+    free(env);
+    free(args);
+    return rc;
+}
+
+int libxl__device_hotplug(libxl__gc *gc, libxl__device *dev,
+                          libxl__hotplug_action action)
+{
+    int rc = 0;
+
+    switch (dev->backend_kind) {
+    case LIBXL__DEVICE_KIND_VBD:
+        rc = libxl__hotplug_disk(gc, dev, action);
+        break;
+    default:
+        rc = 0;
+        break;
+    }
+
+    return rc;
+}
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index 08815a3..01ff363 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -1301,7 +1301,7 @@ static int handle_domain_death(libxl_ctx *ctx, uint32_t
domid,
         /* fall-through */
     case LIBXL_ACTION_ON_SHUTDOWN_DESTROY:
         LOG("Domain %d needs to be cleaned up: destroying the
domain", domid);
-        libxl_domain_destroy(ctx, domid);
+        libxl_domain_destroy(ctx, domid, 0);
         break;

     case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY:
@@ -1862,7 +1862,7 @@ start:
 error_out:
     release_lock();
     if (libxl_domid_valid_guest(domid))
-        libxl_domain_destroy(ctx, domid);
+        libxl_domain_destroy(ctx, domid, 0);

 out:
     if (logfile != 2)
@@ -2339,7 +2339,7 @@ static void destroy_domain(const char *p)
         fprintf(stderr, "Cannot destroy privileged domain 0.\n\n");
         exit(-1);
     }
-    rc = libxl_domain_destroy(ctx, domid);
+    rc = libxl_domain_destroy(ctx, domid, 0);
     if (rc) { fprintf(stderr,"destroy failed (rc=%d)\n",rc);
exit(-1); }
 }

@@ -2613,7 +2613,7 @@ static int save_domain(const char *p, const char
*filename, int checkpoint,
     if (checkpoint)
         libxl_domain_unpause(ctx, domid);
     else
-        libxl_domain_destroy(ctx, domid);
+        libxl_domain_destroy(ctx, domid, 0);

     exit(0);
 }
@@ -2846,7 +2846,7 @@ static void migrate_domain(const char *domain_spec, const
char *rune,
     }

     fprintf(stderr, "migration sender: Target reports successful
startup.\n");
-    libxl_domain_destroy(ctx, domid); /* bang! */
+    libxl_domain_destroy(ctx, domid, 0); /* bang! */
     fprintf(stderr, "Migration successful.\n");
     exit(0);

@@ -2964,7 +2964,7 @@ static void migrate_receive(int debug, int daemonize, int
monitor)
     if (rc) {
         fprintf(stderr, "migration target: Failure, destroying our
copy.\n");

-        rc2 = libxl_domain_destroy(ctx, domid);
+        rc2 = libxl_domain_destroy(ctx, domid, 0);
         if (rc2) {
             fprintf(stderr, "migration target: Failed to destroy our
copy"
                     " (code %d).\n", rc2);
@@ -5011,7 +5011,7 @@ int main_blockattach(int argc, char **argv)
         return 0;
     }

-    if (libxl_device_disk_add(ctx, fe_domid, &disk)) {
+    if (libxl_device_disk_add(ctx, fe_domid, &disk, 0)) {
         fprintf(stderr, "libxl_device_disk_add failed.\n");
     }
     return 0;
diff --git a/tools/python/xen/lowlevel/xl/xl.c
b/tools/python/xen/lowlevel/xl/xl.c
index bbbf2a9..a41a720 100644
--- a/tools/python/xen/lowlevel/xl/xl.c
+++ b/tools/python/xen/lowlevel/xl/xl.c
@@ -444,9 +444,10 @@ static PyObject *pyxl_domain_reboot(XlObject *self,
PyObject *args)
 static PyObject *pyxl_domain_destroy(XlObject *self, PyObject *args)
 {
     int domid;
-    if ( !PyArg_ParseTuple(args, "i", &domid) )
+    const libxl_asyncop_how *ao_how;
+    if ( !PyArg_ParseTuple(args, "ii", &domid, &ao_how) )
         return NULL;
-    if ( libxl_domain_destroy(self->ctx, domid) ) {
+    if ( libxl_domain_destroy(self->ctx, domid, ao_how) ) {
         PyErr_SetString(xl_error_obj, "cannot destroy domain");
         return NULL;
     }
--
1.7.7.5 (Apple Git-26)