diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2011-07-15 10:43:36 +0100 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2011-07-15 14:11:46 +0100 |
commit | 9ec9c97ce2ba46e56b304d3a367c86adc703160d (patch) | |
tree | 69c7ea6ab8a6664d95c8573863536f54284931c7 /src | |
parent | f173543fd207bdc254a5eb75180d82ef25eacae9 (diff) | |
download | libguestfs-9ec9c97ce2ba46e56b304d3a367c86adc703160d.tar.gz libguestfs-9ec9c97ce2ba46e56b304d3a367c86adc703160d.tar.xz libguestfs-9ec9c97ce2ba46e56b304d3a367c86adc703160d.zip |
Add user cancellation to the C API.
This allows long transfers (FileIn and FileOut operations) to be
cancelled by calling the signal and thread safe guestfs_user_cancel
function.
Most of this commit consists of a multithreaded program that tests
user cancellation of uploads and downloads.
Diffstat (limited to 'src')
-rw-r--r-- | src/guestfs-internal.h | 5 | ||||
-rw-r--r-- | src/guestfs.c | 6 | ||||
-rw-r--r-- | src/guestfs.pod | 39 | ||||
-rw-r--r-- | src/proto.c | 27 |
4 files changed, 70 insertions, 7 deletions
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index e2ffdf30..99a04045 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -200,6 +200,11 @@ struct guestfs_h FILE *trace_fp; char *trace_buf; size_t trace_len; + + /* User cancelled transfer. Not signal-atomic, but it doesn't + * matter for this case because we only care if it is != 0. + */ + int user_cancel; }; /* Per-filesystem data stored for inspect_os. */ diff --git a/src/guestfs.c b/src/guestfs.c index e2b71594..1fa3c0ae 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -578,6 +578,12 @@ guestfs_get_error_handler (guestfs_h *g, void **data_rtn) return g->error_cb; } +void +guestfs_user_cancel (guestfs_h *g) +{ + g->user_cancel = 1; +} + int guestfs__set_verbose (guestfs_h *g, int v) { diff --git a/src/guestfs.pod b/src/guestfs.pod index 842b0e4f..341321f1 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -1949,6 +1949,45 @@ this example, messages are directed to syslog: syslog (priority, "event 0x%lx: %s", event, buf); } +=head1 CANCELLING LONG TRANSFERS + +Some operations can be cancelled by the caller while they are in +progress. Currently only operations that involve uploading or +downloading data can be cancelled (technically: operations that have +C<FileIn> or C<FileOut> parameters in the generator). + +=head2 guestfs_user_cancel + + void guestfs_user_cancel (guestfs_h *g); + +C<guestfs_user_cancel> cancels the current upload or download +operation. + +Unlike most other libguestfs calls, this function is signal safe and +thread safe. You can call it from a signal handler or from another +thread, without needing to do any locking. + +The transfer that was in progress (if there is one) will stop shortly +afterwards, and will return an error. The errno (see +L</guestfs_last_errno>) is set to C<EINTR>, so you can test for this +to find out if the operation was cancelled or failed because of +another error. + +No cleanup is performed: for example, if a file was being uploaded +then after cancellation there may be a partially uploaded file. It is +the caller's responsibility to clean up if necessary. + +There are two common places that you might call C<guestfs_user_cancel>. + +In an interactive text-based program, you might call it from a +C<SIGINT> signal handler so that pressing C<^C> cancels the current +operation. (You also need to call L</guestfs_set_pgroup> so that +child processes don't receive the C<^C> signal). + +In a graphical program, when the main thread is displaying a progress +bar with a cancel button, wire up the cancel button to call this +function. + =head1 PRIVATE DATA AREA You can attach named pieces of private data to the libguestfs handle, diff --git a/src/proto.c b/src/proto.c index d8b10486..be7fbdcf 100644 --- a/src/proto.c +++ b/src/proto.c @@ -872,7 +872,6 @@ guestfs___send (guestfs_h *g, int proc_nr, return -1; } -static int cancel = 0; /* XXX Implement file cancellation. */ static int send_file_chunk (guestfs_h *g, int cancel, const char *buf, size_t len); static int send_file_data (guestfs_h *g, const char *buf, size_t len); static int send_file_cancellation (guestfs_h *g); @@ -888,7 +887,9 @@ int guestfs___send_file (guestfs_h *g, const char *filename) { char buf[GUESTFS_MAX_CHUNK_SIZE]; - int fd, r, err; + int fd, r = 0, err; + + g->user_cancel = 0; fd = open (filename, O_RDONLY); if (fd == -1) { @@ -898,7 +899,7 @@ guestfs___send_file (guestfs_h *g, const char *filename) } /* Send file in chunked encoding. */ - while (!cancel) { + while (!g->user_cancel) { r = read (fd, buf, sizeof buf); if (r == -1 && (errno == EINTR || errno == EAGAIN)) continue; @@ -911,13 +912,15 @@ guestfs___send_file (guestfs_h *g, const char *filename) } } - if (cancel) { /* cancel from either end */ + if (r == -1) { + perrorf (g, "read: %s", filename); send_file_cancellation (g); return -1; } - if (r == -1) { - perrorf (g, "read: %s", filename); + if (g->user_cancel) { + error (g, _("operation cancelled by user")); + g->last_errnum = EINTR; send_file_cancellation (g); return -1; } @@ -1116,6 +1119,8 @@ guestfs___recv_file (guestfs_h *g, const char *filename) void *buf; int fd, r; + g->user_cancel = 0; + fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666); if (fd == -1) { perrorf (g, "open: %s", filename); @@ -1130,6 +1135,9 @@ guestfs___recv_file (guestfs_h *g, const char *filename) goto cancel; } free (buf); + + if (g->user_cancel) + goto cancel; } if (r == -1) { @@ -1205,7 +1213,12 @@ receive_file_data (guestfs_h *g, void **buf_r) free (buf); if (chunk.cancel) { - error (g, _("file receive cancelled by daemon")); + if (g->user_cancel) { + error (g, _("operation cancelled by user")); + g->last_errnum = EINTR; + } + else + error (g, _("file receive cancelled by daemon")); free (chunk.data.data_val); return -1; } |