diff --git a/test-fd-passing.c b/test-fd-passing.c
new file mode 100644
index 0000000..f3f06e6
--- /dev/null
+++ b/test-fd-passing.c
@@ -0,0 +1,321 @@
+/*
+ * QEMU fd passing test server
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Corey Bryant      <coreyb@linux.vnet.ibm.com>
+ *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ * gcc -Wall -o test-fd-passing test-fd-passing.c -L/usr/local/lib -ljson
+ * ./test-fd-passing /path/hda.img /path/hdb.img
+ *
+ * Note: This requires json-c and json-c-devel packages.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <spawn.h>
+#include <sys/un.h>
+#include <json/json.h>
+
+static int openMonitor(const char *mon_path)
+{
+    int i;
+    int rc;
+    struct sockaddr_un addr;
+    int fd = 0;
+
+    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        goto error;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strcpy(addr.sun_path, mon_path);
+
+    for (i = 0; i < 100; i++) {
+        rc = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+        if (rc == 0) {
+            break;
+        }
+        sleep(1);
+    }
+
+    if (rc != 0) {
+        fprintf(stderr, "unable to connect to monitor socket\n");
+        goto error;
+    }
+
+    return fd;
+
+error:
+    close(fd);
+    return -1;
+}
+
+static int writeQMPfd(int qmpfd, int fd, const char *str)
+{
+    int rc;
+    char *json_str = NULL;
+    struct json_object *json_obj;
+    struct msghdr msg;
+    struct iovec iov[1];
+    char control[CMSG_SPACE(sizeof(int))];
+    struct cmsghdr *cmsg;
+
+    fprintf(stderr, "write (fd=%d) %s\n", fd, str);
+
+    memset(&msg, 0, sizeof(msg));
+
+    json_obj = json_tokener_parse(str);
+    asprintf(&json_str, "%s\r\n", json_object_to_json_string(json_obj));
+    json_object_put(json_obj);
+
+    iov[0].iov_base = (void *)json_str;
+    iov[0].iov_len = strlen(json_str);
+
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    msg.msg_control = control;
+    msg.msg_controllen = sizeof(control);
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+    do {
+        rc = sendmsg(qmpfd, &msg, 0);
+    } while (rc < 0 && errno == EINTR);
+
+    if (rc < 0) {
+        perror("sendmsg");
+    }
+
+    return rc;
+}
+
+static int writeQMP(int qmpfd, const char *str) {
+    int rc;
+    char *json_str = NULL;
+    struct json_object *json_obj;
+
+    fprintf(stderr, "write %s\n", str);
+
+    json_obj = json_tokener_parse(str);
+    asprintf(&json_str, "%s\r\n", json_object_to_json_string(json_obj));
+    json_object_put(json_obj);
+
+    rc = write(qmpfd, json_str, strlen(json_str));
+    if (rc < 0) {
+        perror("write");
+    }
+
+    return rc;
+}
+
+static char *readQMP(int qmpfd) {
+    int rc;
+    int i;
+    char *json_str;
+
+    json_str = calloc(1024, sizeof(char));
+    if (json_str == NULL) {
+        return NULL;
+    }
+
+    i = 0;
+    do {
+        sleep(1);
+        rc = read(qmpfd, json_str, 1024);
+        if (rc < 0) {
+            perror("read");
+            return NULL;
+        }
+        i++;
+    } while (i < 100 && rc == 0);
+
+    if (rc == 0) {
+        fprintf(stderr, "no data to read");
+        free(json_str);
+        return NULL;
+    }
+
+    fprintf(stderr, "read %s", json_str);
+
+    return json_str;
+}
+
+int main(int argc, char *argv[]) {
+    int rc = -1;
+    int qmpfd, bootfd, hotfd_host, hotfd_guest;
+    int flags = O_RDWR;
+    int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+    pid_t child_pid;
+    char *drive_str = NULL;;
+    char *drive_add_str = NULL;
+    char *device_add_str = NULL;
+    char *drive_add_qmp = NULL;
+    char *device_add_qmp = NULL;
+    char *json_str;
+    struct json_object *json_obj;
+
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s <boot-image-file> <attach-image-file>\n",
+                argv[0]);
+        goto error;
+    }
+
+    /* open the image files */
+    bootfd = open(argv[1], flags, mode);
+    if (bootfd == -1) {
+        perror("open");
+        goto error;
+    }
+
+    hotfd_host = open(argv[2], flags, mode);
+    if (hotfd_host == -1) {
+        perror("open");
+        goto error;
+    }
+
+    /* use '-drive file=/dev/fd/X' on QEMU command line to open boot image */
+    asprintf(&drive_str, "file=/dev/fd/%d,if=none,id=drive-virtio-disk0",
+             bootfd);
+
+    char *child_argv[] = {
+        "qemu-system-x86_64",
+        "-enable-kvm",
+        "-m", "1024",
+        "-chardev", "socket,id=qmp,path=./qmp-sock,server",
+        "-mon", "chardev=qmp,mode=control,pretty=on",
+        "-drive", drive_str,
+        "-device",
+        "virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0",
+        "-vnc", ":0",
+        NULL,
+    };
+
+    rc = posix_spawn(&child_pid, "/usr/local/bin/qemu-system-x86_64",
+                     NULL, NULL, child_argv, environ);
+    if (rc != 0) {
+        perror("posix_spawn\n");
+        goto error;
+    }
+
+    qmpfd = openMonitor("./qmp-sock");
+    if (qmpfd == -1) {
+        rc = -1;
+        goto error;
+    }
+
+    json_str = readQMP(qmpfd);
+    if (json_str == NULL) {
+        rc = -1;
+        goto error;
+    }
+    free(json_str);
+
+    rc = writeQMP(qmpfd, "{ \"execute\": \"qmp_capabilities\" }");
+    if (rc < 0) {
+        goto error;
+    }
+
+    json_str = readQMP(qmpfd);
+    if (json_str == NULL) {
+        rc = -1;
+        goto error;
+    }
+    free(json_str);
+
+    /* issue 'getfd' monitor command and get QEMU guest's fd in return */
+    rc = writeQMPfd(qmpfd, hotfd_host,
+         "{ \"execute\": \"getfd\", \"arguments\": { \"fdname\": \"fd1\" } }");
+    if (rc < 0) {
+        goto error;
+    }
+
+    json_str = readQMP(qmpfd);
+    if (json_str == NULL) {
+        rc = -1;
+        goto error;
+    }
+    json_obj = json_tokener_parse(json_str);
+    json_obj = json_object_object_get(json_obj, "return");
+    hotfd_guest = json_object_get_int(json_obj);
+    json_object_put(json_obj);
+    free(json_str);
+
+    /* issue 'drive_add' monitor command with guest's /dev/fd/X path */
+    asprintf(&drive_add_str, "drive_add data_drive file=/dev/fd/%d,%s",
+             hotfd_guest,
+             "if=none,id=drive-virtio-disk1,cache=writethrough\r\n");
+    asprintf(&drive_add_qmp,
+             "{ \"%s\": \"%s\", \"%s\": { \"%s\": \"%s\" } }",
+             "execute", "human-monitor-command", "arguments",
+             "command-line", drive_add_str);
+    rc = writeQMP(qmpfd, drive_add_qmp);
+    if (rc < 0) {
+        goto error;
+    }
+
+    json_str = readQMP(qmpfd);
+    if (json_str == NULL) {
+        rc = -1;
+        goto error;
+    }
+    free(json_str);
+
+    /* issue 'device_add' monitor command to add the disk drive */
+    asprintf(&device_add_str, "device_add virtio-blk-pci,bus=pci.0,%s",
+             "addr=0x6,drive=drive-virtio-disk1,id=virtio-disk1\r\n");
+    asprintf(&device_add_qmp,
+             "{ \"%s\": \"%s\", \"%s\": { \"%s\": \"%s\" } }",
+             "execute", "human-monitor-command", "arguments",
+             "command-line", device_add_str);
+    rc = writeQMP(qmpfd, device_add_qmp);
+    if (rc < 0) {
+        goto error;
+    }
+
+    json_str = readQMP(qmpfd);
+    if (json_str == NULL) {
+        rc = -1;
+        goto error;
+    }
+    free(json_str);
+
+    rc = 0;
+
+error:
+    if (drive_str) {
+        free(drive_str);
+    }
+    if (drive_add_str) {
+        free(drive_add_str);
+    }
+    if (device_add_str) {
+        free(device_add_str);
+    }
+    if (drive_add_qmp) {
+        free(drive_add_qmp);
+    }
+    if (device_add_qmp) {
+        free(device_add_qmp);
+    }
+    return rc;
+}
