From patchwork Mon Mar 7 20:10:31 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Roth X-Patchwork-Id: 85813 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E4462B6F10 for ; Tue, 8 Mar 2011 07:48:51 +1100 (EST) Received: from localhost ([127.0.0.1]:53388 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PwhAM-0004Lx-VJ for incoming@patchwork.ozlabs.org; Mon, 07 Mar 2011 15:36:31 -0500 Received: from [140.186.70.92] (port=41292 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PwgvX-0007gd-71 for qemu-devel@nongnu.org; Mon, 07 Mar 2011 15:21:15 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Pwgs5-0008BW-47 for qemu-devel@nongnu.org; Mon, 07 Mar 2011 15:17:39 -0500 Received: from e9.ny.us.ibm.com ([32.97.182.139]:49430) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Pwgs4-0008BI-Rr for qemu-devel@nongnu.org; Mon, 07 Mar 2011 15:17:37 -0500 Received: from d01dlp01.pok.ibm.com (d01dlp01.pok.ibm.com [9.56.224.56]) by e9.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p27JinCY015194 for ; Mon, 7 Mar 2011 14:44:49 -0500 Received: from d01relay03.pok.ibm.com (d01relay03.pok.ibm.com [9.56.227.235]) by d01dlp01.pok.ibm.com (Postfix) with ESMTP id 3ADCF38C803D for ; Mon, 7 Mar 2011 15:11:05 -0500 (EST) Received: from d01av01.pok.ibm.com (d01av01.pok.ibm.com [9.56.224.215]) by d01relay03.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p27KB7Cw264646 for ; Mon, 7 Mar 2011 15:11:07 -0500 Received: from d01av01.pok.ibm.com (loopback [127.0.0.1]) by d01av01.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p27KB6u2012171 for ; Mon, 7 Mar 2011 15:11:06 -0500 Received: from localhost.localdomain (sig-9-76-30-5.mts.ibm.com [9.76.30.5]) by d01av01.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p27KAi8F010005; Mon, 7 Mar 2011 15:11:05 -0500 From: Michael Roth To: qemu-devel@nongnu.org Date: Mon, 7 Mar 2011 14:10:31 -0600 Message-Id: <1299528642-23631-6-git-send-email-mdroth@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1299528642-23631-1-git-send-email-mdroth@linux.vnet.ibm.com> References: <1299528642-23631-1-git-send-email-mdroth@linux.vnet.ibm.com> X-Content-Scanned: Fidelis XPS MAILER X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.182.139 Cc: agl@linux.vnet.ibm.com, stefanha@linux.vnet.ibm.com, Jes.Sorensen@redhat.com, mdroth@linux.vnet.ibm.com, markus_mueller@de.ibm.com, aliguori@linux.vnet.ibm.com, abeekhof@redhat.com Subject: [Qemu-devel] [RFC][PATCH v7 05/16] virtagent: common helpers and init routines X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Michael Roth --- virtagent-common.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++ virtagent-common.h | 95 ++++++++++++++++++++++++ 2 files changed, 301 insertions(+), 0 deletions(-) create mode 100644 virtagent-common.c create mode 100644 virtagent-common.h diff --git a/virtagent-common.c b/virtagent-common.c new file mode 100644 index 0000000..4b13ee8 --- /dev/null +++ b/virtagent-common.c @@ -0,0 +1,206 @@ +/* + * virtagent - common host/guest functions + * + * Copyright IBM Corp. 2010 + * + * Authors: + * Adam Litke + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "virtagent-common.h" + +VAState *va_state; + +/* helper to avoid tedious key/type checking on QDict entries */ +bool va_qdict_haskey_with_type(const QDict *qdict, const char *key, + qtype_code type) +{ + QObject *qobj; + if (!qdict) { + return false; + } + if (!qdict_haskey(qdict, key)) { + return false; + } + qobj = qdict_get(qdict, key); + if (qobject_type(qobj) != type) { + return false; + } + + return true; +} + +static void va_qdict_insert(const char *key, QObject *entry, void *opaque) +{ + QDict *dict = opaque; + + if (key && entry) { + qdict_put_obj(dict, key, entry); + } +} + +QDict *va_qdict_copy(const QDict *old) +{ + QDict *new; + + if (!old) { + return NULL; + } + + new = qdict_new(); + qdict_iter(old, va_qdict_insert, new); + + return new; +} + +static int va_connect(void) +{ + QemuOpts *opts; + int fd, ret = 0; + + TRACE("called"); + if (va_state->channel_method == NULL) { + LOG("no channel method specified"); + return -EINVAL; + } + if (va_state->channel_path == NULL) { + LOG("no channel path specified"); + return -EINVAL; + } + + if (strcmp(va_state->channel_method, "unix-connect") == 0) { + TRACE("connecting to %s", va_state->channel_path); + opts = qemu_opts_create(qemu_find_opts("chardev"), NULL, 0); + qemu_opt_set(opts, "path", va_state->channel_path); + fd = unix_connect_opts(opts); + if (fd == -1) { + qemu_opts_del(opts); + LOG("error opening channel: %s", strerror(errno)); + return -errno; + } + qemu_opts_del(opts); + socket_set_nonblock(fd); + } else if (strcmp(va_state->channel_method, "virtio-serial") == 0) { + if (va_state->is_host) { + LOG("specified channel method not available for host"); + return -EINVAL; + } + if (va_state->channel_path == NULL) { + va_state->channel_path = VA_GUEST_PATH_VIRTIO_DEFAULT; + } + TRACE("opening %s", va_state->channel_path); + fd = qemu_open(va_state->channel_path, O_RDWR); + if (fd == -1) { + LOG("error opening channel: %s", strerror(errno)); + return -errno; + } + ret = fcntl(fd, F_GETFL); + if (ret < 0) { + LOG("error getting channel flags: %s", strerror(errno)); + return -errno; + } + ret = fcntl(fd, F_SETFL, ret | O_ASYNC | O_NONBLOCK); + if (ret < 0) { + LOG("error setting channel flags: %s", strerror(errno)); + return -errno; + } + } else if (strcmp(va_state->channel_method, "isa-serial") == 0) { + struct termios tio; + if (va_state->is_host) { + LOG("specified channel method not available for host"); + return -EINVAL; + } + if (va_state->channel_path == NULL) { + LOG("you must specify the path of the serial device to use"); + return -EINVAL; + } + TRACE("opening %s", va_state->channel_path); + fd = qemu_open(va_state->channel_path, O_RDWR | O_NOCTTY); + if (fd == -1) { + LOG("error opening channel: %s", strerror(errno)); + return -errno; + } + tcgetattr(fd, &tio); + /* set up serial port for non-canonical, dumb byte streaming */ + tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | + INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | IMAXBEL); + tio.c_oflag = 0; + tio.c_lflag = 0; + tio.c_cflag |= VA_BAUDRATE; + /* 1 available byte min, else reads will block (we'll set non-blocking + * elsewhere, else we'd have to deal with read()=0 instead) + */ + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + /* flush everything waiting for read/xmit, it's garbage at this point */ + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &tio); + } else { + LOG("invalid channel method"); + return -EINVAL; + } + + va_state->fd = fd; + return 0; +} + +int va_init(VAContext ctx) +{ + VAState *s; + VAManager *m; + int ret; + + TRACE("called"); + if (va_state) { + LOG("virtagent already initialized"); + return -EPERM; + } + + s = qemu_mallocz(sizeof(VAState)); + m = va_manager_new(); + + ret = va_server_init(m, &s->server_data, ctx.is_host); + if (ret) { + LOG("error initializing virtagent server"); + goto out_bad; + } + ret = va_client_init(m, &s->client_data); + if (ret) { + LOG("error initializing virtagent client"); + goto out_bad; + } + + s->client_job_count = 0; + s->client_jobs_in_flight = 0; + s->server_job_count = 0; + s->channel_method = ctx.channel_method; + s->channel_path = ctx.channel_path; + s->is_host = ctx.is_host; + s->manager = m; + va_state = s; + + /* connect to our end of the channel */ + ret = va_connect(); + if (ret) { + LOG("error connecting to channel"); + goto out_bad; + } + + /* start listening for requests/responses */ + qemu_set_fd_handler(va_state->fd, va_http_read_handler, NULL, NULL); + + if (!va_state->is_host) { + /* tell the host the agent is running */ + va_send_hello(); + } + + return 0; +out_bad: + qemu_free(s); + return ret; +} diff --git a/virtagent-common.h b/virtagent-common.h new file mode 100644 index 0000000..5ae50d1 --- /dev/null +++ b/virtagent-common.h @@ -0,0 +1,95 @@ +/* + * virt-agent - host/guest RPC client functions + * + * Copyright IBM Corp. 2010 + * + * Authors: + * Adam Litke + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#ifndef VIRTAGENT_COMMON_H +#define VIRTAGENT_COMMON_H + +#include +#include "qemu-common.h" +#include "qemu_socket.h" +#include "qemu-timer.h" +#include "monitor.h" +#include "virtagent-manager.h" +#include "virtagent-server.h" +#include "virtagent.h" + +#define DEBUG_VA + +#ifdef DEBUG_VA +#define TRACE(msg, ...) do { \ + fprintf(stderr, "%s:%s():L%d: " msg "\n", \ + __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ +} while(0) +#else +#define TRACE(msg, ...) \ + do { } while (0) +#endif + +#define LOG(msg, ...) do { \ + fprintf(stderr, "%s:%s(): " msg "\n", \ + __FILE__, __FUNCTION__, ## __VA_ARGS__); \ +} while(0) + +#define VA_VERSION "1.0" +#define EOL "\r\n" + +#define VA_PIDFILE "/var/run/qemu-va.pid" +#define VA_HDR_LEN_MAX 4096 /* http header limit */ +#define VA_CONTENT_LEN_MAX 2*1024*1024 /* rpc/http send limit */ +#define VA_CLIENT_JOBS_MAX 5 /* max client rpcs we can queue */ +#define VA_SERVER_JOBS_MAX 5 /* max server rpcs we can queue */ +#define VA_SERVER_TIMEOUT_MS 5 * 1000 +#define VA_CLIENT_TIMEOUT_MS 5 * 1000 +#define VA_SENTINEL 0xFF +#define VA_BAUDRATE B38400 /* for isa-serial channels */ + +typedef struct VAContext { + bool is_host; + const char *channel_method; + const char *channel_path; +} VAContext; + +typedef struct VAState { + bool is_host; + const char *channel_method; + const char *channel_path; + int fd; + QEMUTimer *client_timer; + QEMUTimer *server_timer; + VAClientData client_data; + VAServerData server_data; + int client_job_count; + int client_jobs_in_flight; + int server_job_count; + VAManager *manager; +} VAState; + +enum va_job_status { + VA_JOB_STATUS_PENDING = 0, + VA_JOB_STATUS_OK, + VA_JOB_STATUS_ERROR, + VA_JOB_STATUS_CANCELLED, +}; + +typedef void (VAHTSendCallback)(const void *opaque); + +int va_init(VAContext ctx); +bool va_qdict_haskey_with_type(const QDict *qdict, const char *key, + qtype_code type); +QDict *va_qdict_copy(const QDict *old); +int va_xport_send_response(const char *content, size_t content_len, const char *tag, + const void *opaque, VAHTSendCallback cb); +int va_xport_send_request(const char *content, size_t content_len, const char *tag, + const void *opaque, VAHTSendCallback cb); +void va_http_read_handler(void *opaque); +#endif /* VIRTAGENT_COMMON_H */