diff options
author | Richard Jones <rjones@redhat.com> | 2010-08-28 10:33:24 +0100 |
---|---|---|
committer | Richard Jones <rjones@redhat.com> | 2010-08-31 19:27:34 +0100 |
commit | e776a46ffcbede6d9b030dbc8f6ab32500b325ec (patch) | |
tree | 0ccab8e3751abf91b83f987c76b18e4f1cf00ed5 /src | |
parent | a8a44cecbadfd21c7f0483f8c1cdb355d08960a4 (diff) | |
download | libguestfs-e776a46ffcbede6d9b030dbc8f6ab32500b325ec.tar.gz libguestfs-e776a46ffcbede6d9b030dbc8f6ab32500b325ec.tar.xz libguestfs-e776a46ffcbede6d9b030dbc8f6ab32500b325ec.zip |
Implement progress messages in the daemon and library.
This implements progress notification messages in the daemon, and
adds a callback in the library to handle them.
No calls are changed so far, so in fact no progress messages can
be generated by this commit.
For more details, see:
https://www.redhat.com/archives/libguestfs/2010-July/msg00003.html
https://www.redhat.com/archives/libguestfs/2010-July/msg00024.html
Diffstat (limited to 'src')
-rwxr-xr-x | src/generator.ml | 21 | ||||
-rw-r--r-- | src/guestfs-internal.h | 2 | ||||
-rw-r--r-- | src/guestfs.c | 8 | ||||
-rw-r--r-- | src/guestfs.h | 5 | ||||
-rw-r--r-- | src/guestfs.pod | 50 | ||||
-rw-r--r-- | src/proto.c | 47 |
6 files changed, 128 insertions, 5 deletions
diff --git a/src/generator.ml b/src/generator.ml index c25c8712..bbf313a7 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -6327,11 +6327,12 @@ and generate_xdr () = */ const GUESTFS_PROGRAM = 0x2000F5F5; -const GUESTFS_PROTOCOL_VERSION = 1; +const GUESTFS_PROTOCOL_VERSION = 2; /* These constants must be larger than any possible message length. */ const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5; const GUESTFS_CANCEL_FLAG = 0xffffeeee; +const GUESTFS_PROGRESS_FLAG = 0xffff5555; enum guestfs_message_direction { GUESTFS_DIRECTION_CALL = 0, /* client -> daemon */ @@ -6370,6 +6371,23 @@ struct guestfs_chunk { /* data size is 0 bytes if the transfer has finished successfully */ opaque data<GUESTFS_MAX_CHUNK_SIZE>; }; + +/* Progress notifications. Daemon self-limits these messages to + * at most one per second. The daemon can send these messages + * at any time, and the caller should discard unexpected messages. + * 'position' and 'total' have undefined units; however they may + * have meaning for some calls. + * + * NB. guestfs___recv_from_daemon assumes the XDR-encoded + * structure is 24 bytes long. + */ +struct guestfs_progress { + guestfs_procedure proc; /* @0: GUESTFS_PROC_x */ + unsigned serial; /* @4: message serial number */ + unsigned hyper position; /* @8: 0 <= position <= total */ + unsigned hyper total; /* @16: total size of operation */ + /* @24: size of structure */ +}; " (* Generate the guestfs-structs.h file. *) @@ -6869,6 +6887,7 @@ and generate_linker_script () = "guestfs_set_launch_done_callback"; "guestfs_set_log_message_callback"; "guestfs_set_out_of_memory_handler"; + "guestfs_set_progress_callback"; "guestfs_set_subprocess_quit_callback"; (* Unofficial parts of the API: the bindings code use these diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index e37c9c26..32a6c2a6 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -122,6 +122,8 @@ struct guestfs_h void * launch_done_cb_data; guestfs_close_cb close_cb; void * close_cb_data; + guestfs_progress_cb progress_cb; + void * progress_cb_data; int msg_next_serial; diff --git a/src/guestfs.c b/src/guestfs.c index eaacd395..206347e5 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -645,3 +645,11 @@ guestfs_set_close_callback (guestfs_h *g, g->close_cb = cb; g->close_cb_data = opaque; } + +void +guestfs_set_progress_callback (guestfs_h *g, + guestfs_progress_cb cb, void *opaque) +{ + g->progress_cb = cb; + g->progress_cb_data = opaque; +} diff --git a/src/guestfs.h b/src/guestfs.h index 3cff4849..ec88f228 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -34,6 +34,8 @@ extern "C" { #endif +#include <stdint.h> + typedef struct guestfs_h guestfs_h; /*--- Connection management ---*/ @@ -57,14 +59,15 @@ typedef void (*guestfs_log_message_cb) (guestfs_h *g, void *data, char *buf, int typedef void (*guestfs_subprocess_quit_cb) (guestfs_h *g, void *data); typedef void (*guestfs_launch_done_cb) (guestfs_h *g, void *data); typedef void (*guestfs_close_cb) (guestfs_h *g, void *data); +typedef void (*guestfs_progress_cb) (guestfs_h *g, void *data, int proc_nr, int serial, uint64_t position, uint64_t total); extern void guestfs_set_log_message_callback (guestfs_h *g, guestfs_log_message_cb cb, void *opaque); extern void guestfs_set_subprocess_quit_callback (guestfs_h *g, guestfs_subprocess_quit_cb cb, void *opaque); extern void guestfs_set_launch_done_callback (guestfs_h *g, guestfs_launch_done_cb cb, void *opaque); extern void guestfs_set_close_callback (guestfs_h *g, guestfs_close_cb cb, void *opaque); +extern void guestfs_set_progress_callback (guestfs_h *g, guestfs_progress_cb cb, void *opaque); /*--- Structures and actions ---*/ -#include <stdint.h> #include <rpc/types.h> #include <rpc/xdr.h> #include <guestfs-structs.h> diff --git a/src/guestfs.pod b/src/guestfs.pod index 590c768d..6a956edb 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -1186,6 +1186,56 @@ languages (eg. if your HLL interpreter has already been cleaned up by the time this is called, and if your callback then jumps into some HLL function). +=head2 guestfs_set_progress_callback + + typedef void (*guestfs_progress_cb) (guestfs_h *g, void *opaque, + int proc_nr, int serial, + uint64_t position, uint64_t total); + void guestfs_set_progress_callback (guestfs_h *g, + guestfs_progress_cb cb, + void *opaque); + +Some long-running operations can generate progress messages. If +this callback is registered, then it will be called each time a +progress message is generated (usually two seconds after the +operation started, and three times per second thereafter until +it completes, although the frequency may change in future versions). + +The callback receives two numbers: C<position> and C<total>. +The units of C<total> are not defined, although for some +operations C<total> may relate in some way to the amount of +data to be transferred (eg. in bytes or megabytes), and +C<position> may be the portion which has been transferred. + +The only defined and stable parts of the API are: + +=over 4 + +=item * + +The callback can display to the user some type of progress bar or +indicator which shows the ratio of C<position>:C<total>. + +=item * + +0 E<lt>= C<position> E<lt>= C<total> + +=item * + +If any progress notification is sent during a call, then a final +progress notification is always sent when C<position> = C<total>. + +This is to simplify caller code, so callers can easily set the +progress indicator to "100%" at the end of the operation, without +requiring special code to detect this case. + +=back + +The callback also receives the procedure number and serial number of +the call. These are only useful for debugging protocol issues, and +the callback can normally ignore them. The callback may want to +print these numbers in error messages or debugging messages. + =head1 BLOCK DEVICE NAMING In the kernel there is now quite a profusion of schemata for naming diff --git a/src/proto.c b/src/proto.c index ad173c6b..5d924e86 100644 --- a/src/proto.c +++ b/src/proto.c @@ -373,7 +373,15 @@ guestfs___send_to_daemon (guestfs_h *g, const void *v_buf, size_t n) * * It also checks for EOF (qemu died) and passes that up through the * child_cleanup function above. + * + * Progress notifications are handled transparently by this function. + * If the callback exists, it is called. The caller of this function + * will not see GUESTFS_PROGRESS_FLAG. */ + +/* Size of guestfs_progress message on the wire. */ +#define PROGRESS_MESSAGE_SIZE 24 + int guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) { @@ -400,7 +408,13 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) */ ssize_t nr = -4; - while (nr < (ssize_t) *size_rtn) { + for (;;) { + ssize_t message_size = + *size_rtn != GUESTFS_PROGRESS_FLAG ? + *size_rtn : PROGRESS_MESSAGE_SIZE; + if (nr >= message_size) + break; + rset2 = rset; int r = select (max_fd+1, &rset2, NULL, NULL, NULL); if (r == -1) { @@ -450,6 +464,11 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) xdr_uint32_t (&xdr, size_rtn); xdr_destroy (&xdr); + /* *size_rtn changed, recalculate message_size */ + message_size = + *size_rtn != GUESTFS_PROGRESS_FLAG ? + *size_rtn : PROGRESS_MESSAGE_SIZE; + if (*size_rtn == GUESTFS_LAUNCH_FLAG) { if (g->state != LAUNCHING) error (g, _("received magic signature from guestfsd, but in state %d"), @@ -463,6 +482,8 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) } else if (*size_rtn == GUESTFS_CANCEL_FLAG) return 0; + else if (*size_rtn == GUESTFS_PROGRESS_FLAG) + /*FALLTHROUGH*/; /* If this happens, it's pretty bad and we've probably lost * synchronization. */ @@ -473,11 +494,11 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) } /* Allocate the complete buffer, size now known. */ - *buf_rtn = safe_malloc (g, *size_rtn); + *buf_rtn = safe_malloc (g, message_size); /*FALLTHROUGH*/ } - size_t sizetoread = *size_rtn - nr; + size_t sizetoread = message_size - nr; if (sizetoread > BUFSIZ) sizetoread = BUFSIZ; r = read (g->sock, (char *) (*buf_rtn) + nr, sizetoread); @@ -524,6 +545,26 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) } #endif + if (*size_rtn == GUESTFS_PROGRESS_FLAG) { + if (g->state == BUSY && g->progress_cb) { + guestfs_progress message; + XDR xdr; + xdrmem_create (&xdr, *buf_rtn, PROGRESS_MESSAGE_SIZE, XDR_DECODE); + xdr_guestfs_progress (&xdr, &message); + xdr_destroy (&xdr); + + g->progress_cb (g, g->progress_cb_data, + message.proc, message.serial, + message.position, message.total); + } + + free (*buf_rtn); + *buf_rtn = NULL; + + /* Process next message. */ + return guestfs___recv_from_daemon (g, size_rtn, buf_rtn); + } + return 0; } |