mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
guest agent: add guest agent RPCs/commands
This adds the initial set of QMP/QAPI commands provided by the guest
agent:
guest-sync
guest-ping
guest-info
guest-shutdown
guest-file-open
guest-file-read
guest-file-write
guest-file-seek
guest-file-flush
guest-file-close
guest-fsfreeze-freeze
guest-fsfreeze-thaw
guest-fsfreeze-status
The input/output specification for these commands are documented in the
schema.
Example usage:
host:
qemu -device virtio-serial \
-chardev socket,path=/tmp/vs0.sock,server,nowait,id=qga0 \
-device virtserialport,chardev=qga0,name=org.qemu.quest_agent.0
...
echo "{'execute':'guest-info'}" | socat stdio unix-connect:/tmp/qga0.sock
guest:
qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
-p /var/run/qemu-guest-agent.pid -d
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Luiz Capitulino <lcapitulino@gmail.com>
This commit is contained in:
committed by
Luiz Capitulino
parent
48ff7a625b
commit
e3d4d25206
@@ -164,7 +164,7 @@ check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjs
|
||||
|
||||
$(qapi-obj-y): $(GENERATED_HEADERS)
|
||||
qapi-dir := qapi-generated
|
||||
test-visitor.o test-qmp-commands.o: QEMU_CFLAGS += -I $(qapi-dir)
|
||||
test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
|
||||
|
||||
$(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h
|
||||
$(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
|
||||
@@ -176,15 +176,25 @@ $(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c
|
||||
$(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
|
||||
$(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
|
||||
|
||||
$(qapi-dir)/qga-qapi-types.c: $(qapi-dir)/qga-qapi-types.h
|
||||
$(qapi-dir)/qga-qapi-types.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py
|
||||
$(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
$(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h
|
||||
$(qapi-dir)/qga-qapi-visit.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py
|
||||
$(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
$(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py
|
||||
$(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
|
||||
|
||||
test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
|
||||
test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
|
||||
|
||||
test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
|
||||
test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
|
||||
|
||||
QGALIB=qga/guest-agent-command-state.o
|
||||
QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o
|
||||
|
||||
qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
|
||||
qemu-ga.o: $(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c) $(qapi-obj-y)
|
||||
qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o
|
||||
|
||||
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
|
||||
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
# *-*- Mode: Python -*-*
|
||||
|
||||
##
|
||||
# @guest-sync:
|
||||
#
|
||||
# Echo back a unique integer value
|
||||
#
|
||||
# This is used by clients talking to the guest agent over the
|
||||
# wire to ensure the stream is in sync and doesn't contain stale
|
||||
# data from previous client. All guest agent responses should be
|
||||
# ignored until the provided unique integer value is returned,
|
||||
# and it is up to the client to handle stale whole or
|
||||
# partially-delivered JSON text in such a way that this response
|
||||
# can be obtained.
|
||||
#
|
||||
# Such clients should also preceed this command
|
||||
# with a 0xFF byte to make such the guest agent flushes any
|
||||
# partially read JSON data from a previous session.
|
||||
#
|
||||
# @id: randomly generated 64-bit integer
|
||||
#
|
||||
# Returns: The unique integer id passed in by the client
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-sync'
|
||||
'data': { 'id': 'int' },
|
||||
'returns': 'int' }
|
||||
|
||||
##
|
||||
# @guest-ping:
|
||||
#
|
||||
# Ping the guest agent, a non-error return implies success
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-ping' }
|
||||
|
||||
##
|
||||
# @guest-info:
|
||||
#
|
||||
# Get some information about the guest agent.
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestAgentInfo', 'data': {'version': 'str'} }
|
||||
{ 'command': 'guest-info',
|
||||
'returns': 'GuestAgentInfo' }
|
||||
|
||||
##
|
||||
# @guest-shutdown:
|
||||
#
|
||||
# Initiate guest-activated shutdown. Note: this is an asynchronous
|
||||
# shutdown request, with no guaruntee of successful shutdown. Errors
|
||||
# will be logged to guest's syslog.
|
||||
#
|
||||
# @mode: #optional "halt", "powerdown" (default), or "reboot"
|
||||
#
|
||||
# Returns: Nothing on success
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } }
|
||||
|
||||
##
|
||||
# @guest-file-open:
|
||||
#
|
||||
# Open a file in the guest and retrieve a file handle for it
|
||||
#
|
||||
# @filepath: 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.
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-file-open',
|
||||
'data': { 'path': 'str', '*mode': 'str' },
|
||||
'returns': 'int' }
|
||||
|
||||
##
|
||||
# @guest-file-close:
|
||||
#
|
||||
# Close an open file in the guest
|
||||
#
|
||||
# @handle: filehandle returned by guest-file-open
|
||||
#
|
||||
# Returns: Nothing on success.
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-file-close',
|
||||
'data': { 'handle': 'int' } }
|
||||
|
||||
##
|
||||
# @guest-file-read:
|
||||
#
|
||||
# Read from an open file in the guest. Data will be base64-encoded
|
||||
#
|
||||
# @handle: filehandle returned by guest-file-open
|
||||
#
|
||||
# @count: #optional maximum number of bytes to read (default is 4KB)
|
||||
#
|
||||
# Returns: GuestFileRead on success. Note: count is number of bytes read
|
||||
# *before* base64 encoding bytes read.
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileRead',
|
||||
'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
|
||||
|
||||
{ 'command': 'guest-file-read',
|
||||
'data': { 'handle': 'int', '*count': 'int' },
|
||||
'returns': 'GuestFileRead' }
|
||||
|
||||
##
|
||||
# @guest-file-write:
|
||||
#
|
||||
# Write to an open file in the guest.
|
||||
#
|
||||
# @handle: filehandle returned by guest-file-open
|
||||
#
|
||||
# @buf-b64: base64-encoded string representing data to be written
|
||||
#
|
||||
# @count: #optional bytes to write (actual bytes, after base64-decode),
|
||||
# default is all content in buf-b64 buffer after base64 decoding
|
||||
#
|
||||
# Returns: GuestFileWrite on success. Note: count is the number of bytes
|
||||
# base64-decoded bytes written
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileWrite',
|
||||
'data': { 'count': 'int', 'eof': 'bool' } }
|
||||
{ 'command': 'guest-file-write',
|
||||
'data': { 'handle': 'int', 'buf-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.
|
||||
#
|
||||
# @handle: 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.
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileSeek',
|
||||
'data': { 'position': 'int', 'eof': 'bool' } }
|
||||
|
||||
{ 'command': 'guest-file-seek',
|
||||
'data': { 'handle': 'int', 'offset': 'int', 'whence': 'int' },
|
||||
'returns': 'GuestFileSeek' }
|
||||
|
||||
##
|
||||
# @guest-file-flush:
|
||||
#
|
||||
# Write file changes bufferred in userspace to disk/kernel buffers
|
||||
#
|
||||
# @handle: filehandle returned by guest-file-open
|
||||
#
|
||||
# Returns: Nothing on success.
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-file-flush',
|
||||
'data': { 'handle': 'int' } }
|
||||
|
||||
##
|
||||
# @guest-fsfreeze-status:
|
||||
#
|
||||
# Get guest fsfreeze state. error state indicates failure to thaw 1 or more
|
||||
# previously frozen filesystems, or failure to open a previously cached
|
||||
# filesytem (filesystem unmounted/directory changes, etc).
|
||||
#
|
||||
# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'enum': 'GuestFsfreezeStatus',
|
||||
'data': [ 'thawed', 'frozen', 'error' ] }
|
||||
{ 'command': 'guest-fsfreeze-status',
|
||||
'returns': 'GuestFsfreezeStatus' }
|
||||
|
||||
##
|
||||
# @guest-fsfreeze-freeze:
|
||||
#
|
||||
# Sync and freeze all non-network guest filesystems
|
||||
#
|
||||
# Returns: Number of file systems frozen on success
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-fsfreeze-freeze',
|
||||
'returns': 'int' }
|
||||
|
||||
##
|
||||
# @guest-fsfreeze-thaw:
|
||||
#
|
||||
# Unfreeze frozen guest fileystems
|
||||
#
|
||||
# Returns: Number of file systems thawed
|
||||
# If error, -1 (unknown error) or -errno
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-fsfreeze-thaw',
|
||||
'returns': 'int' }
|
||||
@@ -636,6 +636,9 @@ int main(int argc, char **argv)
|
||||
g_log_set_default_handler(ga_log, s);
|
||||
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
|
||||
s->logging_enabled = true;
|
||||
s->command_state = ga_command_state_new();
|
||||
ga_command_state_init(s, s->command_state);
|
||||
ga_command_state_init_all(s->command_state);
|
||||
ga_state = s;
|
||||
|
||||
module_call_init(MODULE_INIT_QAPI);
|
||||
@@ -644,6 +647,7 @@ int main(int argc, char **argv)
|
||||
|
||||
g_main_loop_run(ga_state->main_loop);
|
||||
|
||||
ga_command_state_cleanup_all(ga_state->command_state);
|
||||
unlink(pidfile);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -218,6 +218,14 @@ static const QErrorStringTable qerror_table[] = {
|
||||
.error_fmt = QERR_VNC_SERVER_FAILED,
|
||||
.desc = "Could not start VNC server on %(target)",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_QGA_LOGGING_FAILED,
|
||||
.desc = "Guest agent failed to log non-optional log statement",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_QGA_COMMAND_FAILED,
|
||||
.desc = "Guest agent command failed, error was '%(message)'",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -184,4 +184,10 @@ QError *qobject_to_qerror(const QObject *obj);
|
||||
#define QERR_FEATURE_DISABLED \
|
||||
"{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }"
|
||||
|
||||
#define QERR_QGA_LOGGING_FAILED \
|
||||
"{ 'class': 'QgaLoggingFailed', 'data': {} }"
|
||||
|
||||
#define QERR_QGA_COMMAND_FAILED \
|
||||
"{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }"
|
||||
|
||||
#endif /* QERROR_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,10 +14,12 @@
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define QGA_VERSION "1.0"
|
||||
#define QGA_READ_COUNT_DEFAULT 4 << 10
|
||||
|
||||
typedef struct GAState GAState;
|
||||
typedef struct GACommandState GACommandState;
|
||||
|
||||
void ga_command_state_init(GAState *s, GACommandState *cs);
|
||||
void ga_command_state_add(GACommandState *cs,
|
||||
void (*init)(void),
|
||||
void (*cleanup)(void));
|
||||
|
||||
Reference in New Issue
Block a user