Currently, commands run through guest-exec are "silent" until they finish running. This is fine for short lived commands. But for commands that take a while, this is a bad user experience.
Usually long running programs know that they will run for a while. To improve user experience, they will typically print some kind of status to output at a regular interval. So that the user knows that their command isn't just hanging. This commit adds support for an optional stream-output parameter to guest-exec. This causes subsequent calls to guest-exec-status to return all buffered output. This allows downstream applications to be able to relay "status" to the end user. If stream-output is requested, it is up to the guest-exec-status caller to keep track of the last seen output position and slice the returned output appropriately. This is fairly trivial for a client to do. And it is a more reliable design than having QGA internally keep track of position -- for the cases that the caller "loses" a response. Signed-off-by: Daniel Xu <d...@dxuuu.xyz> --- qga/commands.c | 12 ++++++++++++ qga/qapi-schema.json | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/qga/commands.c b/qga/commands.c index ce172edd2d..8cabc2460f 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -97,6 +97,7 @@ struct GuestExecInfo { int64_t pid_numeric; gint status; bool has_output; + bool stream_output; bool finished; GuestExecIOData in; GuestExecIOData out; @@ -218,6 +219,15 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) QTAILQ_REMOVE(&guest_exec_state.processes, gei, next); g_free(gei); + } else if (gei->stream_output) { + if (gei->out.length > 0) { + ges->out_data = g_base64_encode(gei->out.data, gei->out.length); + ges->has_out_truncated = gei->out.truncated; + } + if (gei->err.length > 0) { + ges->err_data = g_base64_encode(gei->err.data, gei->err.length); + ges->has_err_truncated = gei->err.truncated; + } } return ges; @@ -410,6 +420,7 @@ GuestExec *qmp_guest_exec(const char *path, bool has_env, strList *env, const char *input_data, GuestExecCaptureOutput *capture_output, + bool has_stream_output, bool stream_output, Error **errp) { GPid pid; @@ -485,6 +496,7 @@ GuestExec *qmp_guest_exec(const char *path, gei = guest_exec_info_add(pid); gei->has_output = has_output; + gei->stream_output = has_stream_output && stream_output; g_child_watch_add(pid, guest_exec_child_watch, gei); if (input_data) { diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index b720dd4379..0a76e35082 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1315,13 +1315,18 @@ # @capture-output: bool flag to enable capture of stdout/stderr of # running process. defaults to false. # +# @stream-output: causes future guest-exec-status calls to always +# return current captured output rather than waiting to return +# it all when the command exits. defaults to false. (since: 8.2) +# # Returns: PID on success. # # Since: 2.5 ## { 'command': 'guest-exec', 'data': { 'path': 'str', '*arg': ['str'], '*env': ['str'], - '*input-data': 'str', '*capture-output': 'GuestExecCaptureOutput' }, + '*input-data': 'str', '*capture-output': 'GuestExecCaptureOutput', + '*stream-output': 'bool' }, 'returns': 'GuestExec' } -- 2.41.0