@@ -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
@@ -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 = "",
@@ -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;
+}
@@ -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 */
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(-)