summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2011-07-15 10:43:36 +0100
committerRichard W.M. Jones <rjones@redhat.com>2011-07-15 14:11:46 +0100
commit9ec9c97ce2ba46e56b304d3a367c86adc703160d (patch)
tree69c7ea6ab8a6664d95c8573863536f54284931c7 /src
parentf173543fd207bdc254a5eb75180d82ef25eacae9 (diff)
downloadlibguestfs-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.h5
-rw-r--r--src/guestfs.c6
-rw-r--r--src/guestfs.pod39
-rw-r--r--src/proto.c27
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;
}