* [Qemu-devel] [RFC][PATCH v2 01/10] virtagent: add common rpc transport defs
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 02/10] virtagent: base definitions for host/guest RPC daemon Michael Roth
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Common code for sending/recieving RPCs via http over virtproxy channel.
All communication is done via asynchronous read/write handlers and using
non-blocking reads/writes
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
virtagent-common.c | 431 ++++++++++++++++++++++++++++++++++++++++++++++++++++
virtagent-common.h | 73 +++++++++
2 files changed, 504 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..cc58938
--- /dev/null
+++ b/virtagent-common.c
@@ -0,0 +1,431 @@
+/*
+ * virt-agent - common host/guest RPC functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * 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"
+
+#define VA_READ true
+#define VA_SEND false
+
+enum va_rpc_type {
+ VA_RPC_REQUEST,
+ VA_RPC_RESPONSE,
+};
+
+typedef struct VARPCState {
+ char hdr[VA_HDR_LEN_MAX];
+ int fd;
+ size_t hdr_len;
+ size_t hdr_pos;
+ enum {
+ VA_READ_START,
+ VA_READ_HDR,
+ VA_READ_BODY,
+ VA_SEND_START,
+ VA_SEND_HDR,
+ VA_SEND_BODY,
+ } state;
+ enum va_rpc_type rpc_type;
+ char *content;
+ size_t content_len;
+ size_t content_pos;
+ VARPCData *data;
+} VARPCState;
+
+static void va_rpc_read_handler(void *opaque);
+static void va_rpc_send_handler(void *opaque);
+
+static int end_of_header(char *buf, int end_pos)
+{
+ return !strncmp(buf+(end_pos-2), "\n\r\n", 3);
+}
+
+static void va_rpc_hdr_init(VARPCState *s) {
+ const char *preamble;
+
+ TRACE("called");
+ /* essentially ignored in the context of virtagent, but might as well */
+ if (s->rpc_type == VA_RPC_REQUEST) {
+ preamble = "POST /RPC2 HTTP/1.1";
+ } else if (s->rpc_type == VA_RPC_RESPONSE) {
+ preamble = "HTTP/1.1 200 OK";
+ } else {
+ s->hdr_len = 0;
+ return;
+ }
+
+ s->hdr_len = sprintf(s->hdr,
+ "%s" EOL
+ "Content-Type: text/xml" EOL
+ "Content-Length: %u" EOL EOL,
+ preamble,
+ (uint32_t)s->content_len);
+}
+
+static void va_rpc_parse_hdr(VARPCState *s)
+{
+ int i, line_pos = 0;
+ char line_buf[4096];
+
+ for (i = 0; i < VA_HDR_LEN_MAX; ++i) {
+ if (s->hdr[i] != '\n') {
+ /* read line */
+ line_buf[line_pos++] = s->hdr[i];
+ } else {
+ /* process line */
+ if (strncmp(line_buf, "Content-Length: ", 16) == 0) {
+ s->content_len = atoi(&line_buf[16]);
+ return;
+ }
+ line_pos = 0;
+ }
+ }
+}
+
+static VARPCState *va_rpc_state_new(VARPCData *data, int fd,
+ enum va_rpc_type rpc_type, bool read)
+{
+ VARPCState *s = qemu_mallocz(sizeof(VARPCState));
+
+ s->rpc_type = rpc_type;
+ s->fd = fd;
+ s->data = data;
+ if (s->data == NULL) {
+ goto EXIT_BAD;
+ }
+
+ if (read) {
+ s->state = VA_READ_START;
+ s->content = NULL;
+ } else {
+ s->state = VA_SEND_START;
+ if (rpc_type == VA_RPC_REQUEST) {
+ s->content = XMLRPC_MEMBLOCK_CONTENTS(char, s->data->send_req_xml);
+ s->content_len = XMLRPC_MEMBLOCK_SIZE(char, s->data->send_req_xml);
+ } else if (rpc_type == VA_RPC_RESPONSE) {
+ s->content = XMLRPC_MEMBLOCK_CONTENTS(char, s->data->send_resp_xml);
+ s->content_len = XMLRPC_MEMBLOCK_SIZE(char, s->data->send_resp_xml);
+ } else {
+ LOG("unknown rcp type");
+ goto EXIT_BAD;
+ }
+ va_rpc_hdr_init(s);
+ if (s->hdr_len == 0) {
+ LOG("failed to initialize http header");
+ goto EXIT_BAD;
+ }
+ }
+
+ return s;
+EXIT_BAD:
+ qemu_free(s);
+ return NULL;
+}
+
+/* called by va_rpc_read_handler after reading requests */
+static int va_rpc_send_response(VARPCData *data, int fd)
+{
+ VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_RESPONSE, VA_SEND);
+
+ TRACE("called");
+ if (s == NULL) {
+ LOG("failed to set up RPC state");
+ return -1;
+ }
+ TRACE("setting up send handler for RPC request");
+ vp_set_fd_handler(fd, NULL, va_rpc_send_handler, s);
+
+ return 0;
+}
+
+static void va_rpc_read_handler_completion(VARPCState *s) {
+ int ret;
+
+ if (s->rpc_type == VA_RPC_REQUEST) {
+ /* server read request, call it's cb function then set up
+ * a send handler for the rpc response if there weren't any
+ * communication errors
+ */
+ s->data->cb(s->data);
+ if (s->data->status == VA_RPC_STATUS_OK) {
+ ret = va_rpc_send_response(s->data, s->fd);
+ if (ret != 0) {
+ LOG("error setting up send handler for rpc response");
+ }
+ } else {
+ LOG("error reading rpc request, skipping response");
+ vp_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ qemu_free(s->data);
+ }
+ } else if (s->rpc_type == VA_RPC_RESPONSE) {
+ /* client read response, call it's cb function and complete
+ * the RPC
+ */
+ s->data->cb(s->data);
+ vp_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ qemu_free(s->data);
+ } else {
+ LOG("unknown rpc_type");
+ }
+ if (s->content != NULL) {
+ qemu_free(s->content);
+ }
+ qemu_free(s);
+}
+
+static void va_rpc_read_handler(void *opaque)
+{
+ VARPCState *s = opaque;
+ int ret;
+
+ TRACE("called with opaque: %p", opaque);
+
+ switch (s->state) {
+ case VA_READ_START:
+ s->state = VA_READ_HDR;
+ case VA_READ_HDR:
+ while((ret = read(s->fd, s->hdr + s->hdr_pos, 1)) > 0
+ && s->hdr_pos < VA_HDR_LEN_MAX) {
+ s->hdr_pos += ret;
+ if (end_of_header(s->hdr, s->hdr_pos - 1)) {
+ break;
+ }
+ }
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return;
+ } else {
+ LOG("error reading connection: %s", strerror(errno));
+ goto out_bad;
+ }
+ } else if (ret == 0) {
+ LOG("connected closed unexpectedly");
+ goto out_bad;
+ } else if (s->hdr_pos >= VA_HDR_LEN_MAX) {
+ LOG("http header too long");
+ goto out_bad;
+ } else {
+ s->content_len = -1;
+ va_rpc_parse_hdr(s);
+ if (s->content_len == -1) {
+ LOG("malformed http header");
+ goto out_bad;
+ } else if (s->content_len > VA_CONTENT_LEN_MAX) {
+ LOG("http content length too long");
+ goto out_bad;
+ }
+ s->content = qemu_mallocz(s->content_len);
+ s->state = VA_READ_BODY;
+ }
+ case VA_READ_BODY:
+ while(s->content_pos < s->content_len) {
+ ret = read(s->fd, s->content + s->content_pos,
+ s->content_len - s->content_pos);
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return;
+ } else {
+ LOG("error reading connection: %s", strerror(errno));
+ goto out_bad;
+ }
+ } else if (ret == 0) {
+ LOG("connection closed unexpectedly:"
+ " read %u bytes, expected %u bytes",
+ (unsigned int)s->content_pos, (unsigned int)s->content_len);
+ goto out_bad;
+ }
+ s->content_pos += ret;
+ }
+
+ if (s->rpc_type == VA_RPC_REQUEST) {
+ s->data->req_xml = s->content;
+ s->data->req_xml_len = s->content_len;
+ } else if (s->rpc_type == VA_RPC_RESPONSE) {
+ s->data->resp_xml = s->content;
+ s->data->resp_xml_len = s->content_len;
+ }
+ s->data->status = VA_RPC_STATUS_OK;
+ goto out;
+ default:
+ LOG("unknown state");
+ goto out_bad;
+ }
+
+out_bad:
+ s->data->status = VA_RPC_STATUS_ERR;
+out:
+ va_rpc_read_handler_completion(s);
+}
+
+/* called by va_rpc_send_handler after sending requests */
+static int va_rpc_read_response(VARPCData *data, int fd)
+{
+ VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_RESPONSE, VA_READ);
+
+ TRACE("called");
+ if (s == NULL) {
+ LOG("failed to set up RPC state");
+ return -1;
+ }
+ TRACE("setting up send handler for RPC request");
+ vp_set_fd_handler(fd, NULL, va_rpc_read_handler, s);
+
+ return 0;
+}
+
+static void va_rpc_send_handler_completion(VARPCState *s) {
+ int ret;
+
+ if (s->rpc_type == VA_RPC_REQUEST) {
+ /* client sent request. free request's memblock, and set up read
+ * handler for server response if there weren't any communication
+ * errors
+ */
+ XMLRPC_MEMBLOCK_FREE(char, s->data->send_req_xml);
+ if (s->data->status == VA_RPC_STATUS_OK) {
+ ret = va_rpc_read_response(s->data, s->fd);
+ if (ret != 0) {
+ LOG("error setting up read handler for rpc response");
+ }
+ } else {
+ s->data->cb(s->data);
+ LOG("error sending rpc request, skipping response");
+ vp_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ qemu_free(s->data);
+ }
+ } else if (s->rpc_type == VA_RPC_RESPONSE) {
+ /* server sent response. call it's cb once more, then free
+ * response's memblock and complete the RPC
+ */
+ s->data->cb(s->data);
+ XMLRPC_MEMBLOCK_FREE(char, s->data->send_resp_xml);
+ vp_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ qemu_free(s->data);
+ } else {
+ LOG("unknown rpc_type");
+ }
+ qemu_free(s);
+}
+
+static void va_rpc_send_handler(void *opaque)
+{
+ VARPCState *s = opaque;
+ int ret;
+
+ TRACE("called with opaque: %p", opaque);
+
+ switch (s->state) {
+ case VA_SEND_START:
+ s->state = VA_SEND_HDR;
+ case VA_SEND_HDR:
+ do {
+ ret = write(s->fd, s->hdr + s->hdr_pos, s->hdr_len - s->hdr_pos);
+ if (ret <= 0) {
+ break;
+ }
+ s->hdr_pos += ret;
+ } while (s->hdr_pos < s->hdr_len);
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return;
+ } else {
+ LOG("error reading connection: %s", strerror(errno));
+ goto out_bad;
+ }
+ } else if (ret == 0) {
+ LOG("connected closed unexpectedly");
+ goto out_bad;
+ } else {
+ s->state = VA_SEND_BODY;
+ }
+ case VA_SEND_BODY:
+ do {
+ ret = write(s->fd, s->content + s->content_pos,
+ s->content_len - s->content_pos);
+ if (ret <= 0) {
+ break;
+ }
+ s->content_pos += ret;
+ } while (s->content_pos < s->content_len);
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return;
+ } else {
+ LOG("error reading connection: %s", strerror(errno));
+ goto out_bad;
+ }
+ } else if (ret == 0) {
+ LOG("connected closed unexpectedly");
+ goto out_bad;
+ } else {
+ s->data->status = VA_RPC_STATUS_OK;
+ goto out;
+ }
+ default:
+ LOG("unknown state");
+ goto out_bad;
+ }
+
+out_bad:
+ s->data->status = VA_RPC_STATUS_ERR;
+out:
+ va_rpc_send_handler_completion(s);
+}
+
+/* called by rpc client
+ * one callback to data->cb after response is read.
+ * data and data->send_req_xml should be allocated by caller,
+ * callee will de-allocate these after calling data->cb(data)
+ *
+ * if non-zero returned however, caller should free data and hanging refs
+ */
+int va_rpc_send_request(VARPCData *data, int fd)
+{
+ VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_REQUEST, VA_SEND);
+
+ TRACE("called");
+ if (s == NULL) {
+ LOG("failed to set up RPC state");
+ return -1;
+ }
+ TRACE("setting up send handler for RPC request");
+ vp_set_fd_handler(fd, NULL, va_rpc_send_handler, s);
+
+ return 0;
+}
+
+/* called by rpc server
+ * one callback to current data->cb after read, one callback after send.
+ * data should be allocated by caller, data->send_resp_xml should be
+ * allocated by first data->cb(data) callback, "callee" will de-allocate
+ * data and data->send_resp_xml after sending rpc response
+ *
+ * if non-zero returned however, caller should free data and hanging refs
+ */
+int va_rpc_read_request(VARPCData *data, int fd)
+{
+ VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_REQUEST, VA_READ);
+
+ TRACE("called");
+ if (s == NULL) {
+ LOG("failed to set up RPC state");
+ return -1;
+ }
+ TRACE("setting up read handler for RPC request");
+ vp_set_fd_handler(fd, va_rpc_read_handler, NULL, s);
+ return 0;
+}
diff --git a/virtagent-common.h b/virtagent-common.h
new file mode 100644
index 0000000..6a58bcd
--- /dev/null
+++ b/virtagent-common.h
@@ -0,0 +1,73 @@
+/*
+ * virt-agent - host/guest RPC client functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * 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 <xmlrpc-c/base.h>
+#include <xmlrpc-c/client.h>
+#include <xmlrpc-c/server.h>
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "monitor.h"
+#include "virtproxy.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 TADDR "127.0.0.1:8080"
+#define URL "http://localhost:8080/RPC2"
+#define NAME "QEMU virt-agent host client"
+#define VERSION "1.0"
+#define EOL "\r\n"
+
+#define VA_RPC_STATUS_OK 0
+#define VA_RPC_STATUS_ERR 1
+#define VA_HDR_LEN_MAX 4096 /* http header limit */
+#define VA_CONTENT_LEN_MAX 2*1024*1024 /* rpc/http send limit */
+
+typedef void (VARPCDataCallback)(void *rpc_data);
+typedef struct VARPCData {
+ VARPCDataCallback *cb;
+ int status;
+ void *opaque;
+ /* provided/allocated by caller for sending as memblocks */
+ xmlrpc_mem_block *send_req_xml;
+ xmlrpc_mem_block *send_resp_xml;
+ /* recieved, and passed to cb func, as char arrays */
+ char *req_xml;
+ int req_xml_len;
+ char *resp_xml;
+ int resp_xml_len;
+ /* for use by QMP functions */
+ MonitorCompletion *mon_cb;
+ void *mon_data;
+} VARPCData;
+
+int va_rpc_send_request(VARPCData *data, int fd);
+int va_rpc_read_request(VARPCData *data, int fd);
+#endif /* VIRTAGENT_COMMON_H */
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 02/10] virtagent: base definitions for host/guest RPC daemon
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 01/10] virtagent: add common rpc transport defs Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 03/10] virtagent: qemu-vp, integrate virtagent server Michael Roth
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Basic skeleton code for RPC daemon loop. This is shared by both the
guest-side RPC server as well as the host-side one (the advertised RPCs
for each by guest/host-specific arrays).
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
virtagent-daemon.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++
virtagent-daemon.h | 20 +++++++
2 files changed, 176 insertions(+), 0 deletions(-)
create mode 100644 virtagent-daemon.c
create mode 100644 virtagent-daemon.h
diff --git a/virtagent-daemon.c b/virtagent-daemon.c
new file mode 100644
index 0000000..71a36d4
--- /dev/null
+++ b/virtagent-daemon.c
@@ -0,0 +1,156 @@
+/*
+ * virt-agent - host/guest RPC daemon functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * 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 "qemu_socket.h"
+#include "virtagent-daemon.h"
+#include "virtagent-common.h"
+
+static int va_accept(int listen_fd) {
+ struct sockaddr_in saddr;
+ struct sockaddr *addr;
+ socklen_t len;
+ int fd;
+
+ while (1) {
+ len = sizeof(saddr);
+ addr = (struct sockaddr *)&saddr;
+ fd = qemu_accept(listen_fd, addr, &len);
+ if (fd < 0 && errno != EINTR) {
+ LOG("accept() failed");
+ break;
+ } else if (fd >= 0) {
+ TRACE("accepted connection");
+ break;
+ }
+ }
+ return fd;
+}
+
+typedef struct RPCFunction {
+ xmlrpc_value *(*func)(xmlrpc_env *env, xmlrpc_value *param, void *unused);
+ const char *func_name;
+} RPCFunction;
+
+static RPCFunction guest_functions[] = {
+ { NULL, NULL }
+};
+static RPCFunction host_functions[] = {
+ { NULL, NULL }
+};
+
+static void va_register_functions(xmlrpc_env *env, xmlrpc_registry *registry,
+ RPCFunction *list)
+{
+ int i;
+ for (i = 0; list[i].func != NULL; ++i) {
+ TRACE("adding func: %s", list[i].func_name);
+ xmlrpc_registry_add_method(env, registry, NULL, list[i].func_name,
+ list[i].func, NULL);
+ }
+}
+
+typedef struct VARPCServerState {
+ int listen_fd;
+ xmlrpc_env env;
+ xmlrpc_registry *registry;
+} VARPCServerState;
+
+static void va_accept_handler(void *opaque);
+
+static void va_rpc_send_cb(void *opaque)
+{
+ VARPCData *rpc_data = opaque;
+ VARPCServerState *s = rpc_data->opaque;
+
+ TRACE("called");
+ if (rpc_data->status != VA_RPC_STATUS_OK) {
+ LOG("error sending RPC response");
+ } else {
+ TRACE("RPC completed");
+ }
+
+ TRACE("waiting for RPC request...");
+ vp_set_fd_handler(s->listen_fd, va_accept_handler, NULL, s);
+}
+
+static void va_rpc_read_cb(void *opaque)
+{
+ VARPCData *rpc_data = opaque;
+ VARPCServerState *s = rpc_data->opaque;
+
+ TRACE("called");
+ if (rpc_data->status != VA_RPC_STATUS_OK) {
+ LOG("error reading RPC request");
+ goto out_bad;
+ }
+
+ rpc_data->send_resp_xml =
+ xmlrpc_registry_process_call(&s->env, s->registry, NULL,
+ rpc_data->req_xml, rpc_data->req_xml_len);
+ if (rpc_data->send_resp_xml == NULL) {
+ LOG("error handling RPC request");
+ goto out_bad;
+ }
+
+ rpc_data->cb = va_rpc_send_cb;
+ return;
+
+out_bad:
+ TRACE("waiting for RPC request...");
+ vp_set_fd_handler(s->listen_fd, va_accept_handler, NULL, s);
+}
+
+static void va_accept_handler(void *opaque)
+{
+ VARPCServerState *s = opaque;
+ VARPCData *rpc_data;
+ int ret, fd;
+
+ TRACE("called");
+ fd = va_accept(s->listen_fd);
+ if (fd < 0) {
+ TRACE("connection error: %s", strerror(errno));
+ return;
+ }
+ ret = fcntl(fd, F_GETFL);
+ ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
+
+ TRACE("RPC client connected, reading RPC request...");
+ rpc_data = qemu_mallocz(sizeof(VARPCData));
+ rpc_data->cb = va_rpc_read_cb;
+ rpc_data->opaque = s;
+ ret = va_rpc_read_request(rpc_data, fd);
+ if (ret != 0) {
+ LOG("error setting up read handler");
+ qemu_free(rpc_data);
+ return;
+ }
+ vp_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+}
+
+int va_server_start(int listen_fd, bool is_host)
+{
+ VARPCServerState *s;
+ RPCFunction *func_list = is_host ? host_functions : guest_functions;
+
+ s = qemu_mallocz(sizeof(VARPCServerState));
+ s->listen_fd = listen_fd;
+ xmlrpc_env_init(&s->env);
+ s->registry = xmlrpc_registry_new(&s->env);
+ va_register_functions(&s->env, s->registry, func_list);
+
+ TRACE("waiting for RPC request...");
+ vp_set_fd_handler(s->listen_fd, va_accept_handler, NULL, s);
+
+ return 0;
+}
diff --git a/virtagent-daemon.h b/virtagent-daemon.h
new file mode 100644
index 0000000..da926b3
--- /dev/null
+++ b/virtagent-daemon.h
@@ -0,0 +1,20 @@
+/*
+ * virt-agent - host/guest RPC daemon functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#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
+
+int va_server_start(int listen_fd, bool is_host);
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 03/10] virtagent: qemu-vp, integrate virtagent server
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 01/10] virtagent: add common rpc transport defs Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 02/10] virtagent: base definitions for host/guest RPC daemon Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 04/10] virtagent: base RPC client definitions Michael Roth
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
This allows the guest RPC server to be integrated into the
qemu-vp/virtproxy i/o loop
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
qemu-vp.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 106 insertions(+), 9 deletions(-)
diff --git a/qemu-vp.c b/qemu-vp.c
index 0cc0e67..8944e68 100644
--- a/qemu-vp.c
+++ b/qemu-vp.c
@@ -37,6 +37,7 @@
#include "qemu-option.h"
#include "qemu_socket.h"
#include "virtproxy.h"
+#include "virtagent-daemon.h"
static bool verbose_enabled = 0;
#define DEBUG_ENABLED
@@ -246,14 +247,18 @@ static void usage(const char *cmd)
"[-o <oforward_opts> ...]\n"
"QEMU virt-proxy communication channel\n"
"\n"
-" -c, --channel channel options of the form:\n"
-" <method>:<addr>:<port>[:channel_id]\n"
-" -o, --oforward oforward options of the form:\n"
-" <service_id>:<addr>:<port>[:channel_id]\n"
-" -i, --iforward iforward options of the form:\n"
-" <service_id>:<addr>:<port>[:channel_id]\n"
-" -v, --verbose display extra debugging information\n"
-" -h, --help display this help and exit\n"
+" -c, --channel channel options of the form:\n"
+" <method>:<addr>:<port>[:channel_id]\n"
+" -p, --host-agent host rpc server, options of the form:\n"
+" [channel_id]\n"
+" -g, --guest-agent guest rpc server, options of the form:\n"
+" [channel_id]\n"
+" -o, --oforward oforward options of the form:\n"
+" <service_id>:<addr>:<port>[:channel_id]\n"
+" -i, --iforward iforward options of the form:\n"
+" <service_id>:<addr>:<port>[:channel_id]\n"
+" -v, --verbose display extra debugging information\n"
+" -h, --help display this help and exit\n"
"\n"
" channels are used to establish a data connection between 2 end-points in\n"
" the host or the guest (connection method specified by <method>).\n"
@@ -527,13 +532,44 @@ static int init_iforwards(void) {
return 0;
}
+static int init_agent(const VPData *agent_iforward, bool is_host) {
+ QemuOpts *opts = agent_iforward->opts;
+ int listen_fd, ret;
+
+ INFO("initializing agent...");
+ if (verbose_enabled) {
+ qemu_opts_print(opts, NULL);
+ }
+
+ /* create unix socket pair that agent http/rpc daemon will listen on */
+ listen_fd = unix_listen_opts(agent_iforward->opts);
+ if (listen_fd < 0) {
+ return -1;
+ }
+
+ /* start RPC server */
+ ret = va_server_start(listen_fd, is_host);
+ if (ret != 0) {
+ warnx("error starting RPC server");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ closesocket(listen_fd);
+ return -1;
+}
+
int main(int argc, char **argv)
{
- const char *sopt = "hVvi:o:c:";
+ const char *sopt = "hVvi:o:c:g::p::";
struct option lopt[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ "verbose", 0, NULL, 'v' },
+ { "host-agent", 0, NULL, 'p' },
+ { "guest-agent", 0, NULL, 'g' },
{ "iforward", 0, NULL, 'i' },
{ "oforward", 0, NULL, 'o' },
{ "channel", 0, NULL, 'c' },
@@ -543,10 +579,13 @@ int main(int argc, char **argv)
QTAILQ_INIT(&iforwards);
QTAILQ_INIT(&oforwards);
QTAILQ_INIT(&channels);
+ VPData *guest_agent_iforward = NULL;
+ VPData *host_agent_iforward = NULL;
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
QemuOpts *opts;
VPData *data;
+ char optarg_tmp[VP_ARG_LEN];
switch (ch) {
case 'i':
opts = qemu_opts_create(&vp_opts, NULL, 0);
@@ -578,6 +617,50 @@ int main(int argc, char **argv)
data->opts = opts;
QTAILQ_INSERT_TAIL(&channels, data, next);
break;
+ case 'g':
+ /* create pre-baked iforward for guest agent */
+ if (guest_agent_iforward) {
+ errx(EXIT_FAILURE, "only one --guest-agent argument allowed");
+ }
+ opts = qemu_opts_create(&vp_opts, NULL, 0);
+ if (optarg == 0) {
+ sprintf(optarg_tmp, "%s:%s:-", GUEST_AGENT_SERVICE_ID,
+ GUEST_AGENT_PATH);
+ } else {
+ sprintf(optarg_tmp, "%s:%s:-:%d", GUEST_AGENT_SERVICE_ID,
+ GUEST_AGENT_PATH, atoi(optarg));
+ }
+ ret = vp_parse(opts, optarg_tmp, 0);
+ if (ret) {
+ errx(EXIT_FAILURE, "error parsing arg: %s", optarg);
+ }
+ data = qemu_mallocz(sizeof(VPData));
+ data->opts = opts;
+ QTAILQ_INSERT_TAIL(&iforwards, data, next);
+ guest_agent_iforward = data;
+ break;
+ case 'p':
+ /* create pre-baked iforward for host agent */
+ if (host_agent_iforward) {
+ errx(EXIT_FAILURE, "only one --host-agent argument allowed");
+ }
+ opts = qemu_opts_create(&vp_opts, NULL, 0);
+ if (optarg == 0) {
+ sprintf(optarg_tmp, "%s:%s:-", HOST_AGENT_SERVICE_ID,
+ HOST_AGENT_PATH);
+ } else {
+ sprintf(optarg_tmp, "%s:%s:-:%d", HOST_AGENT_SERVICE_ID,
+ HOST_AGENT_PATH, atoi(optarg));
+ }
+ ret = vp_parse(opts, optarg_tmp, 0);
+ if (ret) {
+ errx(EXIT_FAILURE, "error parsing arg: %s", optarg);
+ }
+ data = qemu_mallocz(sizeof(VPData));
+ data->opts = opts;
+ QTAILQ_INSERT_TAIL(&iforwards, data, next);
+ host_agent_iforward = data;
+ break;
case 'v':
verbose_enabled = 1;
break;
@@ -607,6 +690,20 @@ int main(int argc, char **argv)
"error initializing service mappings for incoming connections");
}
+ if (guest_agent_iforward) {
+ ret = init_agent(guest_agent_iforward, false);
+ if (ret) {
+ errx(EXIT_FAILURE,
+ "error initializing guest agent");
+ }
+ } else if (host_agent_iforward) {
+ ret = init_agent(host_agent_iforward, true);
+ if (ret) {
+ errx(EXIT_FAILURE,
+ "error initializing host agent");
+ }
+ }
+
/* main i/o loop */
for (;;) {
DEBUG("entering main_loop_wait()");
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 04/10] virtagent: base RPC client definitions
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
` (2 preceding siblings ...)
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 03/10] virtagent: qemu-vp, integrate virtagent server Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 05/10] virtagent: add getfile RPC Michael Roth
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Base skeleton and helpers for executing RPC commands. Monitor commands
will result in a connect() being issued to the virtagent service socket,
which will then be transported to the listening RPC server in the guest
via the virtproxy layer, RPC requests are then sent/recieved via http
over the resulting connection.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
monitor.c | 1 +
qerror.c | 4 +++
qerror.h | 3 ++
virtagent.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
virtagent.h | 25 +++++++++++++++++
5 files changed, 121 insertions(+), 0 deletions(-)
create mode 100644 virtagent.c
create mode 100644 virtagent.h
diff --git a/monitor.c b/monitor.c
index 260cc02..6c99206 100644
--- a/monitor.c
+++ b/monitor.c
@@ -42,6 +42,7 @@
#include "audio/audio.h"
#include "disas.h"
#include "balloon.h"
+#include "virtagent.h"
#include "qemu-timer.h"
#include "migration.h"
#include "kvm.h"
diff --git a/qerror.c b/qerror.c
index ac2cdaf..2f111a9 100644
--- a/qerror.c
+++ b/qerror.c
@@ -200,6 +200,10 @@ static const QErrorStringTable qerror_table[] = {
.error_fmt = QERR_VNC_SERVER_FAILED,
.desc = "Could not start VNC server on %(target)",
},
+ {
+ .error_fmt = QERR_RPC_FAILED,
+ .desc = "An RPC error has occurred",
+ },
{}
};
diff --git a/qerror.h b/qerror.h
index 943a24b..43cce4a 100644
--- a/qerror.h
+++ b/qerror.h
@@ -165,4 +165,7 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_VNC_SERVER_FAILED \
"{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"
+#define QERR_RPC_FAILED \
+ "{ 'class': 'RPCFailed', 'data': { 'code': %i, 'message': %s } }"
+
#endif /* QERROR_H */
diff --git a/virtagent.c b/virtagent.c
new file mode 100644
index 0000000..84dcf3b
--- /dev/null
+++ b/virtagent.c
@@ -0,0 +1,88 @@
+/*
+ * virt-agent - host/guest RPC client functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * 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 "qemu_socket.h"
+#include "virtagent-daemon.h"
+#include "virtagent-common.h"
+#include "virtagent.h"
+
+static int rpc_has_error(xmlrpc_env *env)
+{
+ if (env->fault_occurred) {
+ LOG("An RPC error has occurred (%i): %s\n", env->fault_code, env->fault_string);
+ //qerror_report(QERR_RPC_FAILED, env->fault_code, env->fault_string);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Get a connected socket that can be used to make an RPC call
+ * This interface will eventually return the connected virtproxy socket for the
+ * virt-agent channel
+ */
+static int get_transport_fd(void)
+{
+ /* TODO: eventually this will need a path that is unique to other
+ * instances of qemu-vp/qemu. for the integrated qemu-vp we should
+ * explore the possiblity of not requiring a unix socket under the
+ * covers, as well as having client init code set up the oforward
+ * for the service rather than qemu-vp
+ */
+ int ret;
+ int fd = unix_connect(GUEST_AGENT_PATH_CLIENT);
+ if (fd < 0) {
+ LOG("failed to connect to virtagent service");
+ }
+ ret = fcntl(fd, F_GETFL);
+ ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
+ return fd;
+}
+
+static int rpc_execute(xmlrpc_env *const env, const char *function,
+ xmlrpc_value *params, VARPCData *rpc_data)
+{
+ xmlrpc_mem_block *call_xml;
+ int fd, ret;
+
+ fd = get_transport_fd();
+ if (fd < 0) {
+ LOG("invalid fd");
+ ret = -1;
+ goto out;
+ }
+
+ call_xml = XMLRPC_MEMBLOCK_NEW(char, env, 0);
+ xmlrpc_serialize_call(env, call_xml, function, params);
+ if (rpc_has_error(env)) {
+ ret = -EREMOTE;
+ goto out_callxml;
+ }
+
+ rpc_data->send_req_xml = call_xml;
+
+ ret = va_rpc_send_request(rpc_data, fd);
+ if (ret != 0) {
+ ret = -1;
+ goto out_callxml;
+ } else {
+ ret = 0;
+ goto out;
+ }
+
+out_callxml:
+ XMLRPC_MEMBLOCK_FREE(char, call_xml);
+out:
+ return ret;
+}
diff --git a/virtagent.h b/virtagent.h
new file mode 100644
index 0000000..abdb32a
--- /dev/null
+++ b/virtagent.h
@@ -0,0 +1,25 @@
+/*
+ * virt-agent - host/guest RPC client functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * 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_H
+#define VIRTAGENT_H
+
+#include "monitor.h"
+#include "virtagent-common.h"
+
+#define GUEST_AGENT_PATH_CLIENT "/tmp/virtagent-guest-client.sock"
+#define HOST_AGENT_PATH_CLIENT "/tmp/virtagent-host-client.sock"
+#define VA_MAX_CHUNK_SIZE 4096 /* max bytes at a time for get/send file */
+
+#endif /* VIRTAGENT_H */
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 05/10] virtagent: add getfile RPC
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
` (3 preceding siblings ...)
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 04/10] virtagent: base RPC client definitions Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 06/10] virtagent: add agent_viewfile command Michael Roth
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Add RPC to retrieve a guest file. A size limit of some sort will
eventually be needed else we can block the monitor for arbitrarily long
periods of time. This interface is intended for smaller reads like
peeking at logs and /proc and such.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
virtagent-daemon.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 53 insertions(+), 0 deletions(-)
diff --git a/virtagent-daemon.c b/virtagent-daemon.c
index 71a36d4..bf28be2 100644
--- a/virtagent-daemon.c
+++ b/virtagent-daemon.c
@@ -15,6 +15,57 @@
#include "virtagent-daemon.h"
#include "virtagent-common.h"
+/* RPC functions common to guest/host daemons */
+
+static xmlrpc_value *getfile(xmlrpc_env *env,
+ xmlrpc_value *param,
+ void *user_data)
+{
+ const char *path;
+ char *file_contents = NULL;
+ char buf[VA_FILEBUF_LEN];
+ int fd, ret, count = 0;
+ xmlrpc_value *result = NULL;
+
+ /* parse argument array */
+ xmlrpc_decompose_value(env, param, "(s)", &path);
+ if (env->fault_occurred) {
+ return NULL;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ LOG("open failed: %s", strerror(errno));
+ xmlrpc_faultf(env, "open failed: %s", strerror(errno));
+ return NULL;
+ }
+
+ while ((ret = read(fd, buf, VA_FILEBUF_LEN)) > 0) {
+ file_contents = qemu_realloc(file_contents, count + VA_FILEBUF_LEN);
+ memcpy(file_contents + count, buf, ret);
+ count += ret;
+ if (count > VA_GETFILE_MAX) {
+ xmlrpc_faultf(env, "max file size (%d bytes) exceeded",
+ VA_GETFILE_MAX);
+ goto EXIT_CLOSE_BAD;
+ }
+ }
+ if (ret == -1) {
+ LOG("read failed: %s", strerror(errno));
+ xmlrpc_faultf(env, "read failed: %s", strerror(errno));
+ goto EXIT_CLOSE_BAD;
+ }
+
+ result = xmlrpc_build_value(env, "6", file_contents, count);
+
+EXIT_CLOSE_BAD:
+ if (file_contents) {
+ qemu_free(file_contents);
+ }
+ close(fd);
+ return result;
+}
+
static int va_accept(int listen_fd) {
struct sockaddr_in saddr;
struct sockaddr *addr;
@@ -42,6 +93,8 @@ typedef struct RPCFunction {
} RPCFunction;
static RPCFunction guest_functions[] = {
+ { .func = getfile,
+ .func_name = "getfile" },
{ NULL, NULL }
};
static RPCFunction host_functions[] = {
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 06/10] virtagent: add agent_viewfile command
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
` (4 preceding siblings ...)
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 05/10] virtagent: add getfile RPC Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 07/10] virtagent: add getdmesg RPC Michael Roth
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Utilize the getfile RPC to provide a means to view text files in the
guest. Getfile can handle binary files as well but we don't advertise
that here due to the special handling requiring to store it and provide
it back to the user (base64 encoding it for instance). Hence the
potentially confusing "viewfile" as opposed to "getfile".
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
hmp-commands.hx | 16 +++++++++
qmp-commands.hx | 33 ++++++++++++++++++
virtagent.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
virtagent.h | 4 ++
4 files changed, 152 insertions(+), 0 deletions(-)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 81999aa..e9a7f4a 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1211,6 +1211,22 @@ show available trace events and their state
ETEXI
#endif
+ {
+ .name = "agent_viewfile",
+ .args_type = "filepath:s",
+ .params = "filepath",
+ .help = "Echo a file from the guest filesystem",
+ .user_print = do_agent_viewfile_print,
+ .mhandler.cmd_async = do_agent_viewfile,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+
+STEXI
+@item agent_viewfile @var{filepath}
+@findex agent_viewfile
+Echo the file identified by @var{filepath} on the guest filesystem
+ETEXI
+
STEXI
@end table
ETEXI
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 793cf1c..efa2137 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -738,6 +738,39 @@ Example:
EQMP
{
+ .name = "agent_viewfile",
+ .args_type = "filepath:s",
+ .params = "filepath",
+ .help = "Echo a file from the guest filesystem",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_async = do_agent_viewfile,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+
+STEXI
+@item agent_viewfile @var{filepath}
+@findex agent_viewfile
+Echo the file identified by @var{filepath} on the guest filesystem
+ETEXI
+SQMP
+agent_viewfile
+--------
+
+Echo the file identified by @var{filepath} from the guest filesystem.
+
+Arguments:
+
+- "filepath": Full guest path of the desired file
+
+Example:
+
+-> { "execute": "agent_viewfile",
+ "arguments": { "filepath": "/sys/kernel/kexec_loaded" } }
+<- { "return": { "contents": "0" } }
+
+EQMP
+
+ {
.name = "qmp_capabilities",
.args_type = "",
.params = "",
diff --git a/virtagent.c b/virtagent.c
index 84dcf3b..187bb28 100644
--- a/virtagent.c
+++ b/virtagent.c
@@ -86,3 +86,102 @@ out_callxml:
out:
return ret;
}
+
+void do_agent_viewfile_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+ const char *contents = NULL;
+ int i;
+
+ qdict = qobject_to_qdict(data);
+ if (!qdict_haskey(qdict, "contents")) {
+ return;
+ }
+
+ contents = qdict_get_str(qdict, "contents");
+ if (contents != NULL) {
+ /* monitor_printf truncates so do it in chunks. also, file_contents
+ * may not be null-termed at proper location so explicitly calc
+ * last chunk sizes */
+ for (i = 0; i < strlen(contents); i += 1024) {
+ monitor_printf(mon, "%.1024s", contents + i);
+ }
+ }
+ monitor_printf(mon, "\n");
+}
+
+static void do_agent_viewfile_cb(void *opaque)
+{
+ VARPCData *rpc_data = opaque;
+ xmlrpc_value *resp = NULL;
+ char *file_contents = NULL;
+ int file_size, ret;
+ xmlrpc_env env;
+ QDict *qdict = qdict_new();
+
+ if (rpc_data->status != VA_RPC_STATUS_OK) {
+ LOG("error handling RPC request");
+ goto out_no_resp;
+ }
+
+ xmlrpc_env_init(&env);
+ resp = xmlrpc_parse_response(&env, rpc_data->resp_xml,
+ rpc_data->resp_xml_len);
+ if (rpc_has_error(&env)) {
+ ret = -1;
+ goto out_no_resp;
+ }
+
+ xmlrpc_parse_value(&env, resp, "6", &file_contents, &file_size);
+ if (rpc_has_error(&env)) {
+ ret = -1;
+ goto out;
+ }
+
+ if (file_contents != NULL) {
+ qdict_put(qdict, "contents",
+ qstring_from_substr(file_contents, 0, file_size-1));
+ }
+
+out:
+ xmlrpc_DECREF(resp);
+out_no_resp:
+ rpc_data->mon_cb(rpc_data->mon_data, QOBJECT(qdict));
+}
+
+/*
+ * do_agent_viewfile(): View a text file in the guest
+ */
+int do_agent_viewfile(Monitor *mon, const QDict *mon_params,
+ MonitorCompletion cb, void *opaque)
+{
+ xmlrpc_env env;
+ xmlrpc_value *params;
+ VARPCData *rpc_data;
+ const char *filepath;
+ int ret;
+
+ filepath = qdict_get_str(mon_params, "filepath");
+ xmlrpc_env_init(&env);
+ params = xmlrpc_build_value(&env, "(s)", filepath);
+ if (rpc_has_error(&env)) {
+ return -1;
+ }
+
+ rpc_data = qemu_mallocz(sizeof(VARPCData));
+ rpc_data->cb = do_agent_viewfile_cb;
+ rpc_data->mon_cb = cb;
+ rpc_data->mon_data = opaque;
+
+ ret = rpc_execute(&env, "getfile", params, rpc_data);
+ if (ret == -EREMOTE) {
+ monitor_printf(mon, "RPC Failed (%i): %s\n", env.fault_code,
+ env.fault_string);
+ return -1;
+ } else if (ret == -1) {
+ monitor_printf(mon, "RPC communication error\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/virtagent.h b/virtagent.h
index abdb32a..d027eac 100644
--- a/virtagent.h
+++ b/virtagent.h
@@ -22,4 +22,8 @@
#define HOST_AGENT_PATH_CLIENT "/tmp/virtagent-host-client.sock"
#define VA_MAX_CHUNK_SIZE 4096 /* max bytes at a time for get/send file */
+void do_agent_viewfile_print(Monitor *mon, const QObject *qobject);
+int do_agent_viewfile(Monitor *mon, const QDict *mon_params,
+ MonitorCompletion cb, void *opaque);
+
#endif /* VIRTAGENT_H */
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 07/10] virtagent: add getdmesg RPC
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
` (5 preceding siblings ...)
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 06/10] virtagent: add agent_viewfile command Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 08/10] virtagent: add agent_viewdmesg command Michael Roth
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Add RPC to view guest dmesg output.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
virtagent-daemon.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
virtagent-daemon.h | 1 +
2 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/virtagent-daemon.c b/virtagent-daemon.c
index bf28be2..181edac 100644
--- a/virtagent-daemon.c
+++ b/virtagent-daemon.c
@@ -66,6 +66,48 @@ EXIT_CLOSE_BAD:
return result;
}
+/* getdmesg(): return dmesg output
+ * rpc return values:
+ * - dmesg output as a string
+ */
+static xmlrpc_value *getdmesg(xmlrpc_env *env,
+ xmlrpc_value *param,
+ void *user_data)
+{
+ char *dmesg_buf = NULL, cmd[256];
+ int ret;
+ xmlrpc_value *result = NULL;
+ FILE *pipe;
+
+ dmesg_buf = qemu_mallocz(VA_DMESG_LEN + 2048);
+ sprintf(cmd, "dmesg -s %d", VA_DMESG_LEN);
+
+ pipe = popen(cmd, "r");
+ if (pipe == NULL) {
+ LOG("popen failed: %s", strerror(errno));
+ xmlrpc_faultf(env, "popen failed: %s", strerror(errno));
+ goto EXIT_NOCLOSE;
+ }
+
+ ret = fread(dmesg_buf, sizeof(char), VA_DMESG_LEN, pipe);
+ if (!ferror(pipe)) {
+ dmesg_buf[ret] = '\0';
+ TRACE("dmesg:\n%s", dmesg_buf);
+ result = xmlrpc_build_value(env, "s", dmesg_buf);
+ } else {
+ LOG("fread failed");
+ xmlrpc_faultf(env, "popen failed: %s", strerror(errno));
+ }
+
+ pclose(pipe);
+EXIT_NOCLOSE:
+ if (dmesg_buf) {
+ qemu_free(dmesg_buf);
+ }
+
+ return result;
+}
+
static int va_accept(int listen_fd) {
struct sockaddr_in saddr;
struct sockaddr *addr;
@@ -95,6 +137,8 @@ typedef struct RPCFunction {
static RPCFunction guest_functions[] = {
{ .func = getfile,
.func_name = "getfile" },
+ { .func = getdmesg,
+ .func_name = "getdmesg" },
{ NULL, NULL }
};
static RPCFunction host_functions[] = {
diff --git a/virtagent-daemon.h b/virtagent-daemon.h
index da926b3..d19f8e3 100644
--- a/virtagent-daemon.h
+++ b/virtagent-daemon.h
@@ -16,5 +16,6 @@
#define HOST_AGENT_PATH "/tmp/virtagent-host.sock"
#define VA_GETFILE_MAX 1 << 30
#define VA_FILEBUF_LEN 16384
+#define VA_DMESG_LEN 16384
int va_server_start(int listen_fd, bool is_host);
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 08/10] virtagent: add agent_viewdmesg command
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
` (6 preceding siblings ...)
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 07/10] virtagent: add getdmesg RPC Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 09/10] virtagent: Makefile/configure changes to build virtagent bits Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 10/10] virtproxy: add compat defs for linking against vl.c Michael Roth
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Add commands to view guest dmesg output. Currently it is a 16K buffer.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
hmp-commands.hx | 16 +++++++++
qmp-commands.hx | 35 +++++++++++++++++++
virtagent.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
virtagent.h | 3 ++
4 files changed, 153 insertions(+), 0 deletions(-)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index e9a7f4a..0e7a6c9 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1227,6 +1227,22 @@ STEXI
Echo the file identified by @var{filepath} on the guest filesystem
ETEXI
+ {
+ .name = "agent_viewdmesg",
+ .args_type = "",
+ .params = "",
+ .help = "View guest dmesg output",
+ .user_print = do_agent_viewdmesg_print,
+ .mhandler.cmd_async = do_agent_viewdmesg,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+
+STEXI
+@item agent_viewdmesg
+@findex agent_viewdmesg
+View guest dmesg output
+ETEXI
+
STEXI
@end table
ETEXI
diff --git a/qmp-commands.hx b/qmp-commands.hx
index efa2137..dc319b7 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -771,6 +771,41 @@ Example:
EQMP
{
+ .name = "agent_viewdmesg",
+ .args_type = "",
+ .params = "",
+ .help = "View guest dmesg output",
+ .user_print = do_agent_viewdmesg_print,
+ .mhandler.cmd_async = do_agent_viewdmesg,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+
+STEXI
+@item agent_viewdmesg
+@findex agent_viewdmesg
+View guest dmesg output
+ETEXI
+SQMP
+agent_viewdmesg
+--------
+
+View guest dmesg output
+
+Arguments:
+
+(none)
+
+Example:
+
+-> { "execute": "agent_viewdmesg" }
+<- { "return": {
+ "contents": "[353487.942215] usb 1-4: USB disconnect, address 9\n..."
+ }
+ }
+
+EQMP
+
+ {
.name = "qmp_capabilities",
.args_type = "",
.params = "",
diff --git a/virtagent.c b/virtagent.c
index 187bb28..e1fceed 100644
--- a/virtagent.c
+++ b/virtagent.c
@@ -185,3 +185,102 @@ int do_agent_viewfile(Monitor *mon, const QDict *mon_params,
return 0;
}
+
+void do_agent_viewdmesg_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+ const char *contents = NULL;
+ int i;
+
+ qdict = qobject_to_qdict(data);
+ if (!qdict_haskey(qdict, "contents")) {
+ goto out;
+ }
+
+ contents = qdict_get_str(qdict, "contents");
+ if (contents != NULL) {
+ /* monitor_printf truncates so do it in chunks. also, file_contents
+ * may not be null-termed at proper location so explicitly calc
+ * last chunk sizes */
+ for (i = 0; i < strlen(contents); i += 1024) {
+ monitor_printf(mon, "%.1024s", contents + i);
+ }
+ }
+
+out:
+ monitor_printf(mon, "\n");
+}
+
+static void do_agent_viewdmesg_cb(void *opaque)
+{
+ VARPCData *rpc_data = opaque;
+ xmlrpc_value *resp = NULL;
+ char *dmesg = NULL;
+ int ret;
+ xmlrpc_env env;
+ QDict *qdict = qdict_new();
+
+ if (rpc_data->status != VA_RPC_STATUS_OK) {
+ LOG("error handling RPC request");
+ goto out_no_resp;
+ }
+
+ xmlrpc_env_init(&env);
+ resp = xmlrpc_parse_response(&env, rpc_data->resp_xml,
+ rpc_data->resp_xml_len);
+ if (rpc_has_error(&env)) {
+ ret = -1;
+ goto out_no_resp;
+ }
+
+ xmlrpc_parse_value(&env, resp, "s", &dmesg);
+ if (rpc_has_error(&env)) {
+ ret = -1;
+ goto out;
+ }
+
+ if (dmesg != NULL) {
+ qdict_put(qdict, "contents", qstring_from_str(dmesg));
+ }
+
+out:
+ xmlrpc_DECREF(resp);
+out_no_resp:
+ rpc_data->mon_cb(rpc_data->mon_data, QOBJECT(qdict));
+}
+
+/*
+ * do_agent_viewdmesg(): View guest dmesg output
+ */
+int do_agent_viewdmesg(Monitor *mon, const QDict *mon_params,
+ MonitorCompletion cb, void *opaque)
+{
+ xmlrpc_env env;
+ xmlrpc_value *params;
+ VARPCData *rpc_data;
+ int ret;
+
+ xmlrpc_env_init(&env);
+
+ params = xmlrpc_build_value(&env, "(n)");
+ if (rpc_has_error(&env)) {
+ return -1;
+ }
+
+ rpc_data = qemu_mallocz(sizeof(VARPCData));
+ rpc_data->cb = do_agent_viewdmesg_cb;
+ rpc_data->mon_cb = cb;
+ rpc_data->mon_data = opaque;
+
+ ret = rpc_execute(&env, "getdmesg", params, rpc_data);
+ if (ret == -EREMOTE) {
+ monitor_printf(mon, "RPC Failed (%i): %s\n", env.fault_code,
+ env.fault_string);
+ return -1;
+ } else if (ret == -1) {
+ monitor_printf(mon, "RPC communication error\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/virtagent.h b/virtagent.h
index d027eac..82a6924 100644
--- a/virtagent.h
+++ b/virtagent.h
@@ -25,5 +25,8 @@
void do_agent_viewfile_print(Monitor *mon, const QObject *qobject);
int do_agent_viewfile(Monitor *mon, const QDict *mon_params,
MonitorCompletion cb, void *opaque);
+void do_agent_viewdmesg_print(Monitor *mon, const QObject *qobject);
+int do_agent_viewdmesg(Monitor *mon, const QDict *mon_params,
+ MonitorCompletion cb, void *opaque);
#endif /* VIRTAGENT_H */
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 09/10] virtagent: Makefile/configure changes to build virtagent bits
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
` (7 preceding siblings ...)
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 08/10] virtagent: add agent_viewdmesg command Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 10/10] virtproxy: add compat defs for linking against vl.c Michael Roth
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
Makefile | 2 +-
Makefile.target | 2 +-
configure | 25 +++++++++++++++++++++++++
3 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index 2dd64a3..b513d33 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,7 @@ qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(trace-obj-y) $(block-ob
qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y)
-qemu-vp$(EXESUF): qemu-vp.o virtproxy.o qemu-tool.o qemu-error.o qemu-sockets.c $(block-obj-y) $(qobject-obj-y)
+qemu-vp$(EXESUF): qemu-vp.o virtproxy.o virtagent-daemon.o virtagent-common.o qemu-tool.o qemu-error.o qemu-sockets.c $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y)
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@")
diff --git a/Makefile.target b/Makefile.target
index c48cbcc..f8f4c13 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -164,7 +164,7 @@ endif #CONFIG_BSD_USER
# System emulator target
ifdef CONFIG_SOFTMMU
-obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
+obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o virtagent.o virtagent-common.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
diff --git a/configure b/configure
index 27f92e0..cfddad7 100755
--- a/configure
+++ b/configure
@@ -1261,6 +1261,31 @@ EOF
fi
##########################################
+# xmlrpc-c probe
+
+# Look for the xmlrpc-c config program
+if test -n "$cross_prefix" && has ${cross_prefix}xmlrpc-c-config; then
+ xmlrpccconfig=${cross_prefix}xmlrpc-c-config
+elif has xmlrpc-c-config; then
+ xmlrpccconfig=xmlrpc-c-config
+else
+ feature_not_found "xmlrpc-c"
+fi
+
+cat > $TMPC << EOF
+#include <xmlrpc.h>
+int main(void) { xmlrpc_env env; xmlrpc_env_init(&env); return 0; }
+EOF
+xmlrpc_cflags=`$xmlrpccconfig --cflags 2> /dev/null`
+xmlrpc_libs=`$xmlrpccconfig client server-util --libs 2> /dev/null`
+if compile_prog "$xmlrpc_cflags" "$xmlrpc_libs"; then
+ libs_softmmu="$xmlrpc_libs $libs_softmmu"
+ libs_tools="$xmlrpc_libs $libs_tools"
+else
+ feature_not_found "xmlrpc-c"
+fi
+
+##########################################
# VNC TLS detection
if test "$vnc_tls" != "no" ; then
cat > $TMPC <<EOF
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [RFC][PATCH v2 10/10] virtproxy: add compat defs for linking against vl.c
2010-11-03 11:35 [Qemu-devel] [RFC][PATCH v2 00/10] virtagent: host/guest RPC communication agent Michael Roth
` (8 preceding siblings ...)
2010-11-03 11:35 ` [Qemu-devel] [RFC][PATCH v2 09/10] virtagent: Makefile/configure changes to build virtagent bits Michael Roth
@ 2010-11-03 11:35 ` Michael Roth
9 siblings, 0 replies; 11+ messages in thread
From: Michael Roth @ 2010-11-03 11:35 UTC (permalink / raw)
To: qemu-devel; +Cc: aliguori, agl, mdroth
Virtagent depends on basic qemu functions re-implemented by qemu-vp to
allow it to be used in output of qemu (in particular, to allow the
client code to be used for guest-to-host RPC calls). To build virtagent
into qemu we need to define these in terms of the built-in qemu
functions.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
Makefile.target | 2 +-
virtproxy-builtin.c | 30 ++++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletions(-)
create mode 100644 virtproxy-builtin.c
diff --git a/Makefile.target b/Makefile.target
index f8f4c13..d72b2be 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -164,7 +164,7 @@ endif #CONFIG_BSD_USER
# System emulator target
ifdef CONFIG_SOFTMMU
-obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o virtagent.o virtagent-common.o
+obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o virtagent.o virtagent-common.o virtproxy-builtin.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
diff --git a/virtproxy-builtin.c b/virtproxy-builtin.c
new file mode 100644
index 0000000..c3416b3
--- /dev/null
+++ b/virtproxy-builtin.c
@@ -0,0 +1,30 @@
+/*
+ * virt-proxy - host/guest communication layer builtin definitions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+/* the following are functions we define in terms of qemu when linked
+ * against qemu/vl.c. these will be added on an as-needed basis
+ */
+
+#include "qemu-char.h"
+#include "virtproxy.h"
+
+int vp_set_fd_handler(int fd,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque)
+{
+ return qemu_set_fd_handler(fd, fd_read, fd_write, opaque);
+}
+
+
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread