qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Corey Bryant <coreyb@linux.vnet.ibm.com>
To: qemu-devel@nongnu.org
Cc: kwolf@redhat.com, aliguori@us.ibm.com,
	Corey Bryant <coreyb@linux.vnet.ibm.com>,
	eblake@redhat.com, stefanha@linux.vnet.ibm.com
Subject: [Qemu-devel] [PATCH 3/3] Sample server that opens image files for QEMU
Date: Mon,  4 Jun 2012 09:10:10 -0400	[thread overview]
Message-ID: <1338815410-24890-4-git-send-email-coreyb@linux.vnet.ibm.com> (raw)
In-Reply-To: <1338815410-24890-1-git-send-email-coreyb@linux.vnet.ibm.com>

This sample server opens image files and passes the fds to QEMU.  The
paths for two image files are passed as parameters, the first being
the boot image, and the second being an image to be hot-attached.  The
server will open the files and pass the fds to QEMU in one of two ways:

  1) Over the command line (using -drive file=/dev/fd/X) or
  2) Via the QMP monitor with the getfd command (using SCM_RIGHTS)
     followed by drive_add (using file=/dev/fd/X) and then
     device_add.

Usage:
  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.

Signed-off-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
---
 test-fd-passing.c |  321 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 321 insertions(+)
 create mode 100644 test-fd-passing.c

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;
+}
-- 
1.7.10.2

  parent reply	other threads:[~2012-06-04 14:04 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-04 13:10 [Qemu-devel] [PATCH 0/3] file descriptor passing using getfd over QMP Corey Bryant
2012-06-04 13:10 ` [Qemu-devel] [PATCH 1/3] qmp/hmp: Add QMP getfd command that returns fd Corey Bryant
2012-06-04 14:45   ` Kevin Wolf
2012-06-04 15:57     ` Corey Bryant
2012-06-05 18:30   ` Luiz Capitulino
2012-06-06 14:04     ` Corey Bryant
2012-06-06 17:50       ` Luiz Capitulino
2012-06-06 19:42         ` Corey Bryant
2012-06-08 10:46       ` Daniel P. Berrange
2012-06-08 13:17         ` Corey Bryant
2012-06-04 13:10 ` [Qemu-devel] [PATCH 2/3] block: Add support to "open" /dev/fd/X filenames Corey Bryant
2012-06-04 14:32   ` Eric Blake
2012-06-04 15:51     ` Corey Bryant
2012-06-04 16:03       ` Eric Blake
2012-06-04 16:28         ` Corey Bryant
2012-06-04 16:36           ` Eric Blake
2012-06-04 16:40             ` Corey Bryant
2012-06-04 14:54   ` Kevin Wolf
2012-06-04 16:07     ` Corey Bryant
2012-06-04 13:10 ` Corey Bryant [this message]
2012-06-04 15:01   ` [Qemu-devel] [PATCH 3/3] Sample server that opens image files for QEMU Kevin Wolf
2012-06-04 16:15     ` Corey Bryant

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1338815410-24890-4-git-send-email-coreyb@linux.vnet.ibm.com \
    --to=coreyb@linux.vnet.ibm.com \
    --cc=aliguori@us.ibm.com \
    --cc=eblake@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).