From patchwork Mon Mar 7 20:10:34 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Roth X-Patchwork-Id: 85809 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 AF6E2B70F7 for ; Tue, 8 Mar 2011 07:43:15 +1100 (EST) Received: from localhost ([127.0.0.1]:42111 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pwh6S-0002TR-6E for incoming@patchwork.ozlabs.org; Mon, 07 Mar 2011 15:32:28 -0500 Received: from [140.186.70.92] (port=41617 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pwgm7-0001kn-93 for qemu-devel@nongnu.org; Mon, 07 Mar 2011 15:11:33 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Pwgls-0006SL-7M for qemu-devel@nongnu.org; Mon, 07 Mar 2011 15:11:26 -0500 Received: from e3.ny.us.ibm.com ([32.97.182.143]:54834) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Pwglr-0006S9-W4 for qemu-devel@nongnu.org; Mon, 07 Mar 2011 15:11:12 -0500 Received: from d01dlp01.pok.ibm.com (d01dlp01.pok.ibm.com [9.56.224.56]) by e3.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p27Jp2TN025035 for ; Mon, 7 Mar 2011 14:51:02 -0500 Received: from d01relay05.pok.ibm.com (d01relay05.pok.ibm.com [9.56.227.237]) by d01dlp01.pok.ibm.com (Postfix) with ESMTP id 7EF8538C803B for ; Mon, 7 Mar 2011 15:11:09 -0500 (EST) Received: from d01av01.pok.ibm.com (d01av01.pok.ibm.com [9.56.224.215]) by d01relay05.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p27KBBl0216996 for ; Mon, 7 Mar 2011 15:11:11 -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 p27KB9mN012479 for ; Mon, 7 Mar 2011 15:11:11 -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 p27KAi8I010005; Mon, 7 Mar 2011 15:11:09 -0500 From: Michael Roth To: qemu-devel@nongnu.org Date: Mon, 7 Mar 2011 14:10:34 -0600 Message-Id: <1299528642-23631-9-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.143 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 08/16] virtagnet: base RPC server definitions 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-server.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++++++ virtagent-server.h | 40 +++++++ 2 files changed, 353 insertions(+), 0 deletions(-) create mode 100644 virtagent-server.c create mode 100644 virtagent-server.h diff --git a/virtagent-server.c b/virtagent-server.c new file mode 100644 index 0000000..f84546b --- /dev/null +++ b/virtagent-server.c @@ -0,0 +1,313 @@ +/* + * virtagent - host/guest RPC server 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 +#include "virtagent-common.h" +#include "qemu_socket.h" +#include "qjson.h" +#include "qint.h" + +static VARPCFunction guest_functions[]; +static VARPCFunction host_functions[]; +static VAServerData *va_server_data; +static bool va_enable_syslog = false; /* enable syslog'ing of RPCs */ + +#define SLOG(msg, ...) do { \ + char msg_buf[1024]; \ + if (!va_enable_syslog) { \ + break; \ + } \ + snprintf(msg_buf, 1024, msg, ## __VA_ARGS__); \ + syslog(LOG_INFO, "virtagent, %s", msg_buf); \ +} while(0) + +/* helper functions for RPCs */ + +static QDict *va_server_format_response(QDict *return_data, int errnum, + const char *errstr) +{ + QDict *response = qdict_new(); + + if (errnum == -1) { + if (!errstr) { + errstr = "unknown remote error handling RPC"; + } + } + if (errstr) { + qdict_put_obj(response, "errstr", + QOBJECT(qstring_from_str(errstr))); + } + qdict_put_obj(response, "errnum", QOBJECT(qint_from_int(errnum))); + if (return_data) { + qdict_put_obj(response, "return_data", QOBJECT(return_data)); + } + + return response; +} + +/* RPCs */ + +/* va_hello(): handle client startup notification + * params/response qdict format (*=optional): + * response{error}: + * response{errstr}: + */ +static QDict *va_hello(const QDict *params) +{ + int ret; + TRACE("called"); + SLOG("va_hello()"); + ret = va_client_init_capabilities(); + if (ret < 0) { + LOG("error setting initializing client capabilities"); + } + return va_server_format_response(NULL, 0, NULL); +} + +/* va_capabilities(): return server capabilities + * params/response qdict format (*=optional): + * response{error}: + * response{errstr}: + * response{return_data}{methods}: list of callable RPCs + * response{return_data}{version}: virtagent version + */ +static QDict *va_capabilities(const QDict *params) +{ + QList *functions = qlist_new(); + QDict *ret = qdict_new(); + int i; + const char *func_name; + + TRACE("called"); + SLOG("va_capabilities()"); + + for (i = 0; va_server_data->functions[i].func != NULL; ++i) { + func_name = va_server_data->functions[i].func_name; + qlist_append_obj(functions, QOBJECT(qstring_from_str(func_name))); + } + qdict_put_obj(ret, "methods", QOBJECT(functions)); + qdict_put_obj(ret, "version", QOBJECT(qstring_from_str(VA_VERSION))); + + return va_server_format_response(ret, 0, NULL); +} + +static VARPCFunction guest_functions[] = { + { .func = va_capabilities, + .func_name = "capabilities" }, + { NULL, NULL } +}; + +static VARPCFunction host_functions[] = { + { .func = va_hello, + .func_name = "hello" }, + { NULL, NULL } +}; + +static bool va_server_is_enabled(void) +{ + return va_server_data && va_server_data->enabled; +} + +typedef struct VARequestData { + QDict *request; + QString *response; +} VARequestData; + +static int va_do_server_rpc(VARequestData *d, const char *tag) +{ + int ret = 0, i; + const char *func_name; + VARPCFunction *func_list = va_server_data->is_host ? + host_functions : guest_functions; + QDict *response = NULL, *params = NULL; + bool found; + + TRACE("called"); + + if (!va_server_is_enabled()) { + ret = -EBUSY; + goto out; + } + + if (!d->request) { + ret = -EINVAL; + goto out; + } + + if (!va_qdict_haskey_with_type(d->request, "method", QTYPE_QSTRING)) { + ret = -EINVAL; + va_server_job_cancel(va_server_data->manager, tag); + goto out; + } + func_name = qdict_get_str(d->request, "method"); + for (i = 0; func_list[i].func != NULL; ++i) { + if (strcmp(func_name, func_list[i].func_name) == 0) { + if (va_qdict_haskey_with_type(d->request, "params", QTYPE_QDICT)) { + params = qdict_get_qdict(d->request, "params"); + } + response = func_list[i].func(params); + found = true; + break; + } + } + + if (!response) { + if (found) { + response = va_server_format_response(NULL, -1, + "error executing rpc"); + } else { + response = va_server_format_response(NULL, -1, + "unsupported rpc specified"); + } + } + /* TODO: store the json rather than the QDict that generates it */ + d->response = qobject_to_json(QOBJECT(response)); + if (!d->response) { + ret = -EINVAL; + goto out; + } + + va_server_job_execute_done(va_server_data->manager, tag); + +out: + return ret; +} + +int va_server_init(VAManager *m, VAServerData *server_data, bool is_host) +{ + va_enable_syslog = !is_host; /* enable logging for guest agent */ + server_data->functions = is_host ? host_functions : guest_functions; + server_data->enabled = true; + server_data->is_host = is_host; + server_data->manager = m; + va_server_data = server_data; + + return 0; +} + +int va_server_close(void) +{ + if (va_server_data != NULL) { + va_server_data = NULL; + } + return 0; +} + +/* called by VAManager to start executing the RPC */ +static int va_execute(void *opaque, const char *tag) +{ + VARequestData *d = opaque; + int ret = va_do_server_rpc(d, tag); + if (ret < 0) { + LOG("error occurred executing RPC: %s", strerror(-ret)); + } + + return ret; +} + +/* called by xport layer to indicate send completion to VAManager */ +static void va_send_response_cb(const void *opaque) +{ + const char *tag = opaque; + va_server_job_send_done(va_server_data->manager, tag); +} + +/* called by VAManager to start send, in turn calls out to xport layer */ +static int va_send_response(void *opaque, const char *tag) +{ + VARequestData *d = opaque; + const char *json_resp; + int ret; + + TRACE("called, request data d: %p", opaque); + if (!d->response) { + LOG("server generated null response"); + ret = -EINVAL; + goto out_cancel; + } + json_resp = qstring_get_str(d->response); + if (!json_resp) { + ret = -EINVAL; + LOG("server generated invalid JSON response"); + goto out_cancel; + } + + ret = va_xport_send_response(json_resp, strlen(json_resp), + tag, tag, va_send_response_cb); + return ret; +out_cancel: + va_server_job_cancel(va_server_data->manager, tag); + return ret; +} + +static int va_cleanup(void *opaque, const char *tag) +{ + VARequestData *d = opaque; + if (d) { + if (d->request) { + QDECREF(d->request); + } + if (d->response) { + QDECREF(d->response); + } + qemu_free(d); + } + return 0; +} + +static VAServerJobOps server_job_ops = { + .execute = va_execute, + .send = va_send_response, + .callback = va_cleanup, +}; + +/* create server jobs from requests read from xport layer */ +int va_server_job_create(const char *content, size_t content_len, const char *tag) +{ + VARequestData *d = qemu_mallocz(sizeof(VAServerData)); + QObject *request_obj; + + if (!content) { + LOG("recieved job with null request string"); + goto out_bad; + } + + request_obj = qobject_from_json(content); + if (!request_obj) { + LOG("unable to parse JSON arguments"); + goto out_bad; + } + + d->request = qobject_to_qdict(request_obj); + if (!d->request) { + LOG("recieved qobject of unexpected type: %d", + qobject_type(request_obj)); + goto out_bad_free; + } + + if (!va_qdict_haskey_with_type(d->request, "method", QTYPE_QSTRING)) { + LOG("RPC command not specified"); + goto out_bad_free; + } + + va_server_job_add(va_server_data->manager, tag, d, server_job_ops); + + return 0; +out_bad_free: + if (d->request) { + QDECREF(d->request); + } + qemu_free(d); +out_bad: + return -EINVAL; +} diff --git a/virtagent-server.h b/virtagent-server.h new file mode 100644 index 0000000..1f27577 --- /dev/null +++ b/virtagent-server.h @@ -0,0 +1,40 @@ +/* + * virt-agent - host/guest RPC daemon functions + * + * Copyright IBM Corp. 2010 + * + * Authors: + * 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-manager.h" +#include "qdict.h" + +#define GUEST_AGENT_SERVICE_ID "virtagent" +#define GUEST_AGENT_PATH "/tmp/virtagent-guest.sock" +#define HOST_AGENT_SERVICE_ID "virtagent-host" +#define HOST_AGENT_PATH "/tmp/virtagent-host.sock" +#define VA_GETFILE_MAX 1 << 30 +#define VA_FILEBUF_LEN 16384 +#define VA_DMESG_LEN 16384 + +typedef struct VARPCFunction { + QDict *(*func)(const QDict *params); + const char *func_name; +} VARPCFunction; + +typedef struct VAServerData { + bool enabled; + bool is_host; + VARPCFunction *functions; + VAManager *manager; +} VAServerData; + +int va_server_init(VAManager *m, VAServerData *server_data, bool is_host); +int va_server_close(void); +//int va_do_server_rpc(const char *content, size_t content_len, const char tag[64]); +int va_server_job_create(const char *content, size_t content_len, const char *tag);