/* 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. */
/* 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;
}
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];
xdr_guestfs_progress (&xdr, &message);
xdr_destroy (&xdr);
- send_progress_message (g, &message);
+ guestfs___progress_message_callback (g, &message);
}
return 0;
* 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)
{
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;
}
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;
xdr_guestfs_progress (&xdr, &message);
xdr_destroy (&xdr);
- send_progress_message (g, &message);
+ guestfs___progress_message_callback (g, &message);
}
free (*buf_rtn);
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);
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;
}
}
- 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;
}
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 */
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);
goto cancel;
}
free (buf);
+
+ if (g->user_cancel)
+ goto cancel;
}
if (r == -1) {
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;
}