Skip to content

Commit

Permalink
guest agent: add guest agent RPCs/commands
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mdroth authored and lcapitulino committed Jul 21, 2011
1 parent 48ff7a6 commit e3d4d25
Show file tree
Hide file tree
Showing 7 changed files with 768 additions and 3 deletions.
16 changes: 13 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
217 changes: 217 additions & 0 deletions qapi-schema-guest.json
Original file line number Diff line number Diff line change
@@ -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' }
4 changes: 4 additions & 0 deletions qemu-ga.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions qerror.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)'",
},
{}
};

Expand Down
6 changes: 6 additions & 0 deletions qerror.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Loading

0 comments on commit e3d4d25

Please sign in to comment.