diff options
-rw-r--r-- | daemon/configure.ac | 4 | ||||
-rw-r--r-- | daemon/daemon.h | 17 | ||||
-rw-r--r-- | daemon/guestfsd.c | 1 | ||||
-rw-r--r-- | daemon/proto.c | 115 | ||||
-rw-r--r-- | generator/generator_xdr.ml | 10 | ||||
-rw-r--r-- | src/guestfs.pod | 14 |
6 files changed, 158 insertions, 3 deletions
diff --git a/daemon/configure.ac b/daemon/configure.ac index 7d817d7e..e5eb89bf 100644 --- a/daemon/configure.ac +++ b/daemon/configure.ac @@ -1,5 +1,5 @@ # libguestfs-daemon -# Copyright (C) 2009-2010 Red Hat Inc. +# Copyright (C) 2009-2011 Red Hat Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -188,7 +188,9 @@ AC_CHECK_FUNCS([\ posix_fallocate \ realpath \ removexattr \ + setitimer \ setxattr \ + sigaction \ statvfs \ sync]) diff --git a/daemon/daemon.h b/daemon/daemon.h index 3a67758b..40a087de 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -174,6 +174,23 @@ extern void reply (xdrproc_t xdrp, char *ret); */ extern void notify_progress (uint64_t position, uint64_t total); +/* Pulse mode progress messages. + * + * Call pulse_mode_start to start sending progress messages. + * + * Call pulse_mode_end along the ordinary exit path (ie. before a + * reply message is sent). + * + * Call pulse_mode_cancel along all error paths *before* any reply is + * sent. pulse_mode_cancel does not modify errno, so it is safe to + * call it before reply_with_perror. + * + * Pulse mode and ordinary notify_progress must not be mixed. + */ +extern void pulse_mode_start (void); +extern void pulse_mode_end (void); +extern void pulse_mode_cancel (void); + /* Helper for functions that need a root filesystem mounted. * NB. Cannot be used for FileIn functions. */ diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index 1c695eb6..ac8750c1 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -679,6 +679,7 @@ commandrvf (char **stdoutput, char **stderror, int flags, } if (pid == 0) { /* Child process running the command. */ + signal (SIGALRM, SIG_DFL); signal (SIGPIPE, SIG_DFL); close (0); if (flag_copy_stdin) { diff --git a/daemon/proto.c b/daemon/proto.c index 32137bbf..6b7a1877 100644 --- a/daemon/proto.c +++ b/daemon/proto.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <stdarg.h> #include <string.h> +#include <signal.h> #include <inttypes.h> #include <unistd.h> #include <errno.h> @@ -661,3 +662,117 @@ notify_progress (uint64_t position, uint64_t total) exit (EXIT_FAILURE); } } + +/* "Pulse mode" progress messages. */ + +#if defined(HAVE_SETITIMER) && defined(HAVE_SIGACTION) + +static void async_safe_send_pulse (int sig); + +void +pulse_mode_start (void) +{ + struct sigaction act; + struct itimerval it; + + memset (&act, 0, sizeof act); + act.sa_handler = async_safe_send_pulse; + act.sa_flags = SA_RESTART; + + if (sigaction (SIGALRM, &act, NULL) == -1) { + perror ("pulse_mode_start: sigaction"); + return; + } + + it.it_value.tv_sec = NOTIFICATION_INITIAL_DELAY / 1000000; + it.it_value.tv_usec = NOTIFICATION_INITIAL_DELAY % 1000000; + it.it_interval.tv_sec = NOTIFICATION_PERIOD / 1000000; + it.it_interval.tv_usec = NOTIFICATION_PERIOD % 1000000; + + if (setitimer (ITIMER_REAL, &it, NULL) == -1) + perror ("pulse_mode_start: setitimer"); +} + +void +pulse_mode_end (void) +{ + pulse_mode_cancel (); /* Cancel the itimer. */ + + notify_progress (1, 1); +} + +void +pulse_mode_cancel (void) +{ + int err = errno; /* Function must preserve errno. */ + struct itimerval it; + struct sigaction act; + + /* Setting it_value to zero cancels the itimer. */ + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 0; + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + + if (setitimer (ITIMER_REAL, &it, NULL) == -1) + perror ("pulse_mode_cancel: setitimer"); + + memset (&act, 0, sizeof act); + act.sa_handler = SIG_DFL; + + if (sigaction (SIGALRM, &act, NULL) == -1) + perror ("pulse_mode_cancel: sigaction"); + + errno = err; +} + +/* Send a position = 0, total = 1 (pulse mode) message. The tricky + * part is we have to do it without invoking any non-async-safe + * functions (see signal(7) for a list). Therefore, KISS. + */ +static void +async_safe_send_pulse (int sig) +{ + /* XDR is a RFC ... */ + unsigned char msg[] = { + (GUESTFS_PROGRESS_FLAG & 0xff000000) >> 24, + (GUESTFS_PROGRESS_FLAG & 0x00ff0000) >> 16, + (GUESTFS_PROGRESS_FLAG & 0x0000ff00) >> 8, + GUESTFS_PROGRESS_FLAG & 0x000000ff, + (proc_nr & 0xff000000) >> 24, + (proc_nr & 0x00ff0000) >> 16, + (proc_nr & 0x0000ff00) >> 8, + proc_nr & 0x000000ff, + (serial & 0xff000000) >> 24, + (serial & 0x00ff0000) >> 16, + (serial & 0x0000ff00) >> 8, + serial & 0x000000ff, + /* 64 bit position = 0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 64 bit total = 1 */ 0, 0, 0, 0, 0, 0, 0, 1 + }; + + if (xwrite (sock, msg, sizeof msg) == -1) + exit (EXIT_FAILURE); +} + +#else /* !HAVE_SETITIMER || !HAVE_SIGACTION */ + +void +pulse_mode_start (void) +{ + /* empty */ +} + +void +pulse_mode_end (void) +{ + /* empty */ +} + +void +pulse_mode_cancel (void) +{ + /* empty */ +} + +#endif /* !HAVE_SETITIMER || !HAVE_SIGACTION */ diff --git a/generator/generator_xdr.ml b/generator/generator_xdr.ml index 5714c803..07f3ff97 100644 --- a/generator/generator_xdr.ml +++ b/generator/generator_xdr.ml @@ -1,5 +1,5 @@ (* libguestfs - * Copyright (C) 2009-2010 Red Hat Inc. + * Copyright (C) 2009-2011 Red Hat Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -214,8 +214,14 @@ struct guestfs_chunk { * 'position' and 'total' have undefined units; however they may * have meaning for some calls. * - * NB. guestfs___recv_from_daemon assumes the XDR-encoded + * Notes: + * + * (1) guestfs___recv_from_daemon assumes the XDR-encoded * structure is 24 bytes long. + * + * (2) daemon/proto.c:async_safe_send_pulse assumes the progress + * message is laid out precisely in this way. So if you change + * this then you'd better change that function as well. */ struct guestfs_progress { guestfs_procedure proc; /* @0: GUESTFS_PROC_x */ diff --git a/src/guestfs.pod b/src/guestfs.pod index 91c6b330..eecf96de 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -1786,6 +1786,20 @@ 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. +=item * + +For some calls we are unable to estimate the progress of the call, but +we can still generate progress messages to indicate activity. This is +known as "pulse mode", and is directly supported by certain progress +bar implementations (eg. GtkProgressBar). + +For these calls, zero or more progress messages are generated with +C<position = 0> and C<total = 1>, followed by a final message with +C<position = total = 1>. + +As noted above, if the call fails with an error then the final message +may not be generated. + =back The callback also receives the procedure number (C<proc_nr>) and |