+/* Initial delay before sending notification messages, and
+ * the period at which we send them thereafter. These times
+ * are in microseconds.
+ */
+#define NOTIFICATION_INITIAL_DELAY 2000000
+#define NOTIFICATION_PERIOD 333333
+
+void
+notify_progress (uint64_t position, uint64_t total)
+{
+ struct timeval now_t;
+ gettimeofday (&now_t, NULL);
+
+ /* Always send a notification at 100%. This simplifies callers by
+ * allowing them to 'finish' the progress bar at 100% without
+ * needing special code.
+ */
+ if (count_progress > 0 && position == total)
+ goto send;
+
+ /* Calculate time in microseconds since the last progress message
+ * was sent out (or since the start of the call).
+ */
+ int64_t last_us, now_us, elapsed_us;
+ last_us =
+ (int64_t) last_progress_t.tv_sec * 1000000 + last_progress_t.tv_usec;
+ now_us = (int64_t) now_t.tv_sec * 1000000 + now_t.tv_usec;
+ elapsed_us = now_us - last_us;
+
+ /* Rate limit. */
+ if ((count_progress == 0 && elapsed_us < NOTIFICATION_INITIAL_DELAY) ||
+ (count_progress > 0 && elapsed_us < NOTIFICATION_PERIOD))
+ return;
+
+ send:
+ /* We're going to send a message now ... */
+ count_progress++;
+ last_progress_t = now_t;
+
+ /* Send the header word. */
+ XDR xdr;
+ char buf[128];
+ uint32_t i = GUESTFS_PROGRESS_FLAG;
+ size_t len;
+ xdrmem_create (&xdr, buf, 4, XDR_ENCODE);
+ xdr_u_int (&xdr, &i);
+ xdr_destroy (&xdr);
+
+ if (xwrite (sock, buf, 4) == -1) {
+ fprintf (stderr, "xwrite failed\n");
+ exit (EXIT_FAILURE);
+ }
+
+ guestfs_progress message = {
+ .proc = proc_nr,
+ .serial = serial,
+ .position = position,
+ .total = total,
+ };
+
+ xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
+ if (!xdr_guestfs_progress (&xdr, &message)) {
+ fprintf (stderr, "xdr_guestfs_progress: failed to encode message\n");
+ xdr_destroy (&xdr);
+ return;
+ }
+ len = xdr_getpos (&xdr);
+ xdr_destroy (&xdr);
+
+ if (xwrite (sock, buf, len) == -1) {
+ fprintf (stderr, "xwrite failed\n");
+ exit (EXIT_FAILURE);
+ }