[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC][PATCH v2 16/17] guest agent: add guest agent RPCs/com
From: |
Michael Roth |
Subject: |
[Qemu-devel] [RFC][PATCH v2 16/17] guest agent: add guest agent RPCs/commands |
Date: |
Mon, 18 Apr 2011 10:02:32 -0500 |
This adds the initial set of QMP/QAPI commands provided by the guest
agent:
guest-ping
guest-file-open
guest-file-read
guest-file-write
guest-file-seek
guest-file-close
The input/output specification for these commands are documented in the
schema.
Signed-off-by: Michael Roth <address@hidden>
---
qapi-schema.json | 133 ++++++++++++++++++++-
qga/guest-agent-commands.c | 284 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 415 insertions(+), 2 deletions(-)
create mode 100644 qga/guest-agent-commands.c
diff --git a/qapi-schema.json b/qapi-schema.json
index 5292938..e2f209d 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1387,10 +1387,139 @@
{ 'enum': 'VirtioNetTxStrategy', 'data': ['bh', 'timer'] }
{ 'type': 'GuestInfo', 'data': {'*name': 'str', '*pid': 'int'} }
+##
+# @guest-ping:
+#
+# Ping the guest agent, a non-error return implies success
+#
+# Since: 0.15.0
+##
{ 'command': 'guest-ping' }
-{ 'command': 'guest-view-file', 'data': { 'filename': 'str' },
- 'returns': 'str' }
+##
+# @guest-info:
+#
+# Get some information about the guest agent.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestAgentInfo', 'data': {'version': 'str', 'timeout_ms': 'int'} }
+{ 'command': 'guest-info',
+ 'returns': 'GuestAgentInfo' }
+
+##
+# @guest-shutdown:
+#
+# Initiate guest-activated shutdown
+#
+# @shutdown_mode: "halt", "powerdown", or "reboot"
+#
+# Returns: Nothing on success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-shutdown', 'data': { 'shutdown_mode': 'str' } }
+
+##
+# @guest-file-open:
+#
+# Open a file in the guest and retrieve a file handle for it
+#
+# @filename: Full path to the file in the guest to open.
+#
+# @mode: #optional open mode, as per fopen(), "r" is the default.
+#
+# Returns: Guest file handle on success.
+# If @filename cannot be opened, OpenFileFailed
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-open',
+ 'data': { 'filename': 'str', 'mode': 'str' },
+ 'returns': 'int' }
+
+##
+# @guest-file-read:
+#
+# Read from an open file in the guest
+#
+# @filehandle: filehandle returned by guest-file-open
+#
+# @count: maximum number of bytes to read
+#
+# Returns: GuestFileRead on success.
+# If @filehandle cannot be found, OpenFileFailed
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileRead',
+ 'data': { 'count': 'int', 'buf': 'str', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-read',
+ 'data': { 'filehandle': 'int', 'count': 'int' },
+ 'returns': 'GuestFileRead' }
+
+##
+# @guest-file-write:
+#
+# Write to an open file in the guest
+#
+# @filehandle: filehandle returned by guest-file-open
+#
+# @data_b64: base64-encoded string representing data to be written
+#
+# @count: bytes to write (actual bytes, after b64-decode)
+#
+# Returns: GuestFileWrite on success.
+# If @filehandle cannot be found, OpenFileFailed
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileWrite',
+ 'data': { 'count': 'int', 'eof': 'bool' } }
+{ 'command': 'guest-file-write',
+ 'data': { 'filehandle': 'int', 'data_b64': 'str', 'count': 'int' },
+ 'returns': 'GuestFileWrite' }
+
+##
+# @guest-file-seek:
+#
+# Seek to a position in the file, as with fseek(), and return the
+# current file position afterward. Also encapsulates ftell()'s
+# functionality, just Set offset=0, whence=SEEK_CUR.
+#
+# @filehandle: filehandle returned by guest-file-open
+#
+# @offset: bytes to skip over in the file stream
+#
+# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek()
+#
+# Returns: GuestFileSeek on success.
+# If @filename cannot be opened, OpenFileFailed
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileSeek',
+ 'data': { 'position': 'int', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-seek',
+ 'data': { 'filehandle': 'int', 'offset': 'int', 'whence': 'int' },
+ 'returns': 'GuestFileSeek' }
+
+##
+# @guest-file-close:
+#
+# Close an open file in the guest
+#
+# @filehandle: filehandle returned by guest-file-open
+#
+# Returns: Nothing on success.
+# If @filename cannot be opened, OpenFileFailed
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-close',
+ 'data': { 'filehandle': 'int' } }
{ 'type': 'ProbeProtocol', 'data': { 'unsafe': 'bool', 'filename': 'str' } }
diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
new file mode 100644
index 0000000..843ef36
--- /dev/null
+++ b/qga/guest-agent-commands.c
@@ -0,0 +1,284 @@
+/*
+ * QEMU Guest Agent commands
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <address@hidden>
+ *
+ * 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 <glib.h>
+#include "guest-agent.h"
+
+static bool enable_syslog = true;
+static GAState *ga_state;
+
+#define SLOG(msg, ...) do { \
+ if (enable_syslog) { \
+ g_log("syslog", G_LOG_LEVEL_INFO, msg, ## __VA_ARGS__); \
+ } \
+} while(0)
+
+void qga_guest_ping(Error **err)
+{
+ SLOG("guest-ping called");
+}
+
+struct GuestAgentInfo *qga_guest_info(Error **err)
+{
+ GuestAgentInfo *info = g_malloc0(sizeof(GuestInfo));
+
+ info->version = g_strdup(QGA_VERSION);
+ info->timeout_ms = ga_get_timeout(ga_state);
+
+ return info;
+}
+
+void qga_guest_shutdown(const char *shutdown_mode, Error **err)
+{
+ int ret;
+ const char *shutdown_flag;
+
+ SLOG("guest-shutdown called, shutdown_mode: %s", shutdown_mode);
+
+ if (strcmp(shutdown_mode, "halt") == 0) {
+ shutdown_flag = "-H";
+ } else if (strcmp(shutdown_mode, "powerdown") == 0) {
+ shutdown_flag = "-P";
+ } else if (strcmp(shutdown_mode, "reboot") == 0) {
+ shutdown_flag = "-r";
+ } else {
+ ret = -EINVAL;
+ error_set(err, QERR_INVALID_PARAMETER_VALUE, "shutdown_mode",
+ "halt|powerdown|reboot");
+ return;
+ }
+
+ ret = fork();
+ if (ret == 0) {
+ /* child, start the shutdown */
+ setsid();
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ sleep(5);
+ ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
+ "hypervisor initiated shutdown", (char*)NULL);
+ exit(!!ret);
+ } else if (ret < 0) {
+ error_set(err, QERR_UNDEFINED_ERROR);
+ }
+}
+
+typedef struct GuestFileHandle {
+ uint64_t id;
+ FILE *fh;
+} GuestFileHandle;
+
+static struct {
+ GSList *filehandles;
+ uint64_t last_id;
+} guest_file_state;
+
+static void guest_file_init(void)
+{
+ guest_file_state.filehandles = NULL;
+ guest_file_state.last_id = 0;
+}
+
+static void guest_file_cleanup(void)
+{
+ /* TODO: cleanup old array, close out any open filehandles */
+ guest_file_state.filehandles = NULL;
+ guest_file_state.last_id = 0;
+}
+
+static int64_t guest_file_handle_add(FILE *fh)
+{
+ GuestFileHandle *gfh;
+
+ gfh = g_malloc(sizeof(GuestFileHandle));
+ gfh->id = guest_file_state.last_id++;
+ gfh->fh = fh;
+ guest_file_state.filehandles = g_slist_append(guest_file_state.filehandles,
+ gfh);
+ return gfh->id;
+}
+
+static gint guest_file_handle_match(gconstpointer elem, gconstpointer id_p)
+{
+ const uint64_t *id = id_p;
+ const GuestFileHandle *gfh = elem;
+
+ g_assert(gfh);
+ return (gfh->id != *id);
+}
+
+static FILE *guest_file_handle_find(int64_t id)
+{
+ GSList *elem = g_slist_find_custom(guest_file_state.filehandles, &id,
+ guest_file_handle_match);
+ GuestFileHandle *gfh;
+
+ if (elem) {
+ g_assert(elem->data);
+ gfh = elem->data;
+ return gfh->fh;
+ }
+
+ return NULL;
+}
+
+static void guest_file_handle_remove(int64_t id)
+{
+ GSList *elem = g_slist_find_custom(guest_file_state.filehandles, &id,
+ guest_file_handle_match);
+ gpointer data = elem->data;
+
+ if (!data) {
+ return;
+ }
+ guest_file_state.filehandles = g_slist_remove(guest_file_state.filehandles,
+ data);
+ g_free(data);
+}
+
+int64_t qga_guest_file_open(const char *filename, const char *mode, Error
**err)
+{
+ FILE *fh;
+ int fd, ret;
+ int64_t id = -1;
+
+ SLOG("guest-file-open called, filename: %s, mode: %s", filename, mode);
+ fh = fopen(filename, mode);
+ if (!fh) {
+ error_set(err, QERR_OPEN_FILE_FAILED, filename);
+ goto out;
+ }
+
+ /* set fd non-blocking to avoid common use cases (like reading from a
+ * named pipe) from hanging the agent
+ */
+ fd = fileno(fh);
+ ret = fcntl(fd, F_GETFL);
+ ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
+ if (ret == -1) {
+ error_set(err, QERR_OPEN_FILE_FAILED, filename);
+ fclose(fh);
+ goto out;
+ }
+
+ id = guest_file_handle_add(fh);
+ SLOG("guest-file-open, filehandle: %ld", id);
+out:
+ return id;
+}
+
+struct GuestFileRead *qga_guest_file_read(int64_t filehandle, int64_t count,
+ Error **err)
+{
+ GuestFileRead *read_data;
+ guchar *buf;
+ FILE *fh = guest_file_handle_find(filehandle);
+ size_t read_count;
+
+ if (!fh) {
+ error_set(err, QERR_FD_NOT_FOUND, "filehandle");
+ return NULL;
+ }
+
+ read_data = g_malloc0(sizeof(GuestFileRead));
+ buf = g_malloc(count);
+
+ read_count = fread(buf, 1, count, fh);
+ buf[read_count] = 0;
+ read_data->count = read_count;
+ read_data->eof = feof(fh);
+ if (read_count) {
+ read_data->buf = g_base64_encode(buf, read_count);
+ }
+ g_free(buf);
+ /* clear error and eof. error is generally due to EAGAIN from non-blocking
+ * mode, and no real way to differenitate from a real error since we only
+ * get boolean error flag from ferror()
+ */
+ clearerr(fh);
+
+ return read_data;
+}
+
+GuestFileWrite *qga_guest_file_write(int64_t filehandle, const char *data_b64,
+ int64_t count, Error **err)
+{
+ GuestFileWrite *write_data;
+ guchar *data;
+ gsize data_len;
+ int write_count;
+ FILE *fh = guest_file_handle_find(filehandle);
+
+ if (!fh) {
+ error_set(err, QERR_FD_NOT_FOUND, "filehandle");
+ return NULL;
+ }
+
+ write_data = g_malloc0(sizeof(GuestFileWrite));
+ data = g_base64_decode(data_b64, &data_len);
+ write_count = fwrite(data, 1, MIN(count, data_len), fh);
+ write_data->count = write_count;
+ write_data->eof = feof(fh);
+ g_free(data);
+ clearerr(fh);
+
+ return write_data;
+}
+
+struct GuestFileSeek *qga_guest_file_seek(int64_t filehandle, int64_t offset,
+ int64_t whence, Error **err)
+{
+ GuestFileSeek *seek_data;
+ FILE *fh = guest_file_handle_find(filehandle);
+ int ret;
+
+ if (!fh) {
+ error_set(err, QERR_FD_NOT_FOUND, "filehandle");
+ return NULL;
+ }
+
+ seek_data = g_malloc0(sizeof(GuestFileRead));
+ ret = fseek(fh, offset, whence);
+ if (ret == -1) {
+ error_set(err, QERR_UNDEFINED_ERROR);
+ g_free(seek_data);
+ return NULL;
+ }
+ seek_data->position = ftell(fh);
+ seek_data->eof = feof(fh);
+ clearerr(fh);
+
+ return seek_data;
+}
+
+void qga_guest_file_close(int64_t filehandle, Error **err)
+{
+ FILE *fh = guest_file_handle_find(filehandle);
+
+ SLOG("guest-file-close called, filehandle: %ld", filehandle);
+ if (!fh) {
+ error_set(err, QERR_FD_NOT_FOUND, "filehandle");
+ return;
+ }
+
+ fclose(fh);
+ guest_file_handle_remove(filehandle);
+}
+
+/* register init/cleanup routines for stateful command groups */
+void ga_command_state_init(GAState *s, GACommandState *cs)
+{
+ ga_state = s;
+ ga_command_state_add(cs, &guest_file_init, &guest_file_cleanup);
+}
--
1.7.0.4
- [Qemu-devel] [RFC][PATCH v2 02/17] json-streamer: add handling for JSON_ERROR token/state, (continued)
- [Qemu-devel] [RFC][PATCH v2 02/17] json-streamer: add handling for JSON_ERROR token/state, Michael Roth, 2011/04/18
- [Qemu-devel] [RFC][PATCH v2 01/17] json-lexer: make lexer error-recovery more deterministic, Michael Roth, 2011/04/18
- [Qemu-devel] [RFC][PATCH v2 05/17] qapi: fix handling for null-return async callbacks, Michael Roth, 2011/04/18
- [Qemu-devel] [RFC][PATCH v2 03/17] json-parser: add handling for NULL token list, Michael Roth, 2011/04/18
- [Qemu-devel] [RFC][PATCH v2 08/17] qapi: fix Error usage in qemu-sockets.c, Michael Roth, 2011/04/18
- [Qemu-devel] [RFC][PATCH v2 12/17] guest agent: worker thread class, Michael Roth, 2011/04/18
- [Qemu-devel] [RFC][PATCH v2 16/17] guest agent: add guest agent RPCs/commands,
Michael Roth <=
- [Qemu-devel] [RFC][PATCH v2 15/17] guest agent: qemu-ga daemon, Michael Roth, 2011/04/18
- Re: [Qemu-devel] [RFC][PATCH v2 15/17] guest agent: qemu-ga daemon, Jes Sorensen, 2011/04/21
- Re: [Qemu-devel] [RFC][PATCH v2 15/17] guest agent: qemu-ga daemon, Michael Roth, 2011/04/22
- Re: [Qemu-devel] [RFC][PATCH v2 15/17] guest agent: qemu-ga daemon, Ian Molton, 2011/04/22
- Re: [Qemu-devel] [RFC][PATCH v2 15/17] guest agent: qemu-ga daemon, Jes Sorensen, 2011/04/22
- Re: [Qemu-devel] [RFC][PATCH v2 15/17] guest agent: qemu-ga daemon, Ian Molton, 2011/04/25
- Re: [Qemu-devel] [RFC][PATCH v2 15/17] guest agent: qemu-ga daemon, Jes Sorensen, 2011/04/26
[Qemu-devel] [RFC][PATCH v2 13/17] guest agent: command state class, Michael Roth, 2011/04/18
[Qemu-devel] [RFC][PATCH v2 14/17] guest agent: core marshal/dispatch interfaces, Michael Roth, 2011/04/18
[Qemu-devel] [RFC][PATCH v2 17/17] guest agent: build qemu-ga, add QEMU-wide gio dep, Michael Roth, 2011/04/18