summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2010-08-28 10:33:24 +0100
committerRichard Jones <rjones@redhat.com>2010-08-31 19:27:34 +0100
commite776a46ffcbede6d9b030dbc8f6ab32500b325ec (patch)
tree0ccab8e3751abf91b83f987c76b18e4f1cf00ed5 /src
parenta8a44cecbadfd21c7f0483f8c1cdb355d08960a4 (diff)
downloadlibguestfs-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-xsrc/generator.ml21
-rw-r--r--src/guestfs-internal.h2
-rw-r--r--src/guestfs.c8
-rw-r--r--src/guestfs.h5
-rw-r--r--src/guestfs.pod50
-rw-r--r--src/proto.c47
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;
}