X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Fproto.c;h=be7fbdcf74a93920a2383abaa64d51c56d159342;hb=eb209a3664906257b3dc87b6f7b04ff54e0d1288;hp=2ca24c29c009212b3bf5f852991bbf27f07a2328;hpb=c7368ce167d6dbfd3e69ba208301c5af3f17a8a1;p=libguestfs.git diff --git a/src/proto.c b/src/proto.c index 2ca24c2..be7fbdc 100644 --- a/src/proto.c +++ b/src/proto.c @@ -251,8 +251,15 @@ read_log_message_or_eof (guestfs_h *g, int fd, int error_if_eof) /* QEMU's console emulates a 16550A serial port. The real 16550A * device has a small FIFO buffer (16 bytes) which means here we see * lots of small reads of 1-16 bytes in length, usually single - * bytes. + * bytes. Sleeping here for a very brief period groups reads + * together (so we usually get a few lines of output at once) and + * improves overall throughput, as well as making the event + * interface a bit more sane for callers. With a virtio-serial + * based console (not yet implemented) we may be able to remove + * this. XXX */ + usleep (1000); + n = read (fd, buf, sizeof buf); if (n == 0) { /* Hopefully this indicates the qemu child process has died. */ @@ -278,6 +285,24 @@ read_log_message_or_eof (guestfs_h *g, int fd, int error_if_eof) /* It's an actual log message, send it upwards if anyone is listening. */ guestfs___call_callbacks_message (g, GUESTFS_EVENT_APPLIANCE, buf, n); + /* This is a gross hack. See the comment above + * guestfs___launch_send_progress. + */ + if (g->state == LAUNCHING) { + const char *sentinel; + size_t len; + + sentinel = "Linux version"; /* kernel up */ + len = strlen (sentinel); + if (memmem (buf, n, sentinel, len) != NULL) + guestfs___launch_send_progress (g, 6); + + sentinel = "Starting /init script"; /* /init running */ + len = strlen (sentinel); + if (memmem (buf, n, sentinel, len) != NULL) + guestfs___launch_send_progress (g, 9); + } + return 0; } @@ -326,8 +351,10 @@ really_read_from_socket (guestfs_h *g, int sock, char *buf, size_t n) return (ssize_t) got; } -static void -send_progress_message (guestfs_h *g, const guestfs_progress *message) +/* Convenient wrapper to generate a progress message callback. */ +void +guestfs___progress_message_callback (guestfs_h *g, + const guestfs_progress *message) { uint64_t array[4]; @@ -384,7 +411,7 @@ check_for_daemon_cancellation_or_eof (guestfs_h *g, int fd) xdr_guestfs_progress (&xdr, &message); xdr_destroy (&xdr); - send_progress_message (g, &message); + guestfs___progress_message_callback (g, &message); } return 0; @@ -504,6 +531,25 @@ guestfs___send_to_daemon (guestfs_h *g, const void *v_buf, size_t n) * will not see GUESTFS_PROGRESS_FLAG. */ +static inline void +unexpected_end_of_file_from_daemon_error (guestfs_h *g) +{ +#define UNEXPEOF_ERROR "unexpected end of file when reading from daemon.\n" +#define UNEXPEOF_TEST_TOOL \ + "Or you can run 'libguestfs-test-tool' and post the complete output into\n" \ + "a bug report or message to the libguestfs mailing list." + if (!g->verbose) + error (g, _(UNEXPEOF_ERROR +"This usually means the libguestfs appliance failed to start up. Please\n" +"enable debugging (LIBGUESTFS_DEBUG=1) and rerun the command, then look at\n" +"the debug messages output prior to this error.\n" +UNEXPEOF_TEST_TOOL)); + else + error (g, _(UNEXPEOF_ERROR +"See earlier debug messages.\n" +UNEXPEOF_TEST_TOOL)); +} + int guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) { @@ -569,7 +615,7 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) return -1; } if (r == 0) { - error (g, _("unexpected end of file when reading from daemon")); + unexpected_end_of_file_from_daemon_error (g); child_cleanup (g); return -1; } @@ -632,7 +678,7 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) return -1; } if (r == 0) { - error (g, _("unexpected end of file when reading from daemon")); + unexpected_end_of_file_from_daemon_error (g); child_cleanup (g); free (*buf_rtn); *buf_rtn = NULL; @@ -674,7 +720,7 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) xdr_guestfs_progress (&xdr, &message); xdr_destroy (&xdr); - send_progress_message (g, &message); + guestfs___progress_message_callback (g, &message); } free (*buf_rtn); @@ -826,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); @@ -842,20 +887,19 @@ 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) { perrorf (g, "open: %s", filename); send_file_cancellation (g); - /* Daemon sees cancellation and won't reply, so caller can - * just return here. - */ return -1; } /* 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; @@ -868,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; } @@ -1034,6 +1080,34 @@ guestfs___recv (guestfs_h *g, const char *fn, return 0; } +/* Same as guestfs___recv, but it discards the reply message. */ +int +guestfs___recv_discard (guestfs_h *g, const char *fn) +{ + void *buf; + uint32_t size; + int r; + + again: + r = guestfs___recv_from_daemon (g, &size, &buf); + if (r == -1) + return -1; + + /* This can happen if a cancellation happens right at the end + * of us sending a FileIn parameter to the daemon. Discard. The + * daemon should send us an error message next. + */ + if (size == GUESTFS_CANCEL_FLAG) + goto again; + + if (size == GUESTFS_LAUNCH_FLAG) { + error (g, "%s: received unexpected launch flag from daemon when expecting reply", fn); + return -1; + } + + return 0; +} + /* Receive a file. */ /* Returns -1 = error, 0 = EOF, > 0 = more data */ @@ -1045,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); @@ -1059,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) { @@ -1134,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; }