From patchwork Mon Jun 4 13:10:10 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Bryant X-Patchwork-Id: 162807 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id B6FD8B6FA5 for ; Tue, 5 Jun 2012 00:05:05 +1000 (EST) Received: from localhost ([::1]:44432 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SbXu3-0007vj-IB for incoming@patchwork.ozlabs.org; Mon, 04 Jun 2012 10:05:03 -0400 Received: from eggs.gnu.org ([208.118.235.92]:54138) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SbXtR-0006TR-4F for qemu-devel@nongnu.org; Mon, 04 Jun 2012 10:04:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SbXtF-0004TN-Me for qemu-devel@nongnu.org; Mon, 04 Jun 2012 10:04:24 -0400 Received: from cpe-174-097-227-101.nc.res.rr.com ([174.97.227.101]:35257 helo=localhost.localdomain) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SbXtF-0004ST-Ew for qemu-devel@nongnu.org; Mon, 04 Jun 2012 10:04:13 -0400 Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by localhost.localdomain (8.14.5/8.14.5) with ESMTP id q54DAJUG024968; Mon, 4 Jun 2012 09:10:19 -0400 Received: (from corey@localhost) by localhost.localdomain (8.14.5/8.14.5/Submit) id q54DAICH024958; Mon, 4 Jun 2012 09:10:18 -0400 From: Corey Bryant To: qemu-devel@nongnu.org Date: Mon, 4 Jun 2012 09:10:10 -0400 Message-Id: <1338815410-24890-4-git-send-email-coreyb@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.10.2 In-Reply-To: <1338815410-24890-1-git-send-email-coreyb@linux.vnet.ibm.com> References: <1338815410-24890-1-git-send-email-coreyb@linux.vnet.ibm.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 174.97.227.101 Cc: kwolf@redhat.com, aliguori@us.ibm.com, Corey Bryant , eblake@redhat.com, stefanha@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH 3/3] Sample server that opens image files for QEMU X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 + * Stefan Hajnoczi + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 \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; +}