/* libguestfs
- * Copyright (C) 2009-2010 Red Hat Inc.
+ * Copyright (C) 2009-2011 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* functions send_to_daemon and recv_from_daemon.
*/
+/* This is only used on the debug path, to generate a one-line
+ * printable summary of a protocol message. 'workspace' is scratch
+ * space used to format the message, and it must be at least
+ * MAX_MESSAGE_SUMMARY bytes in size.
+ */
+#define MAX_MESSAGE_SUMMARY 200 /* >= 5 * (4 * 3 + 2) + a few bytes overhead */
+
static int
xwrite (int fd, const void *v_buf, size_t len)
{
return 0;
}
+static const char *
+message_summary (const void *buf, size_t n, char *workspace)
+{
+ const unsigned char *cbuf = buf;
+ size_t i = 0;
+ char *p = workspace;
+ int truncate = 0;
+
+ /* Print only up to 5 x 32 bits of the message. That is enough to
+ * cover the message length, and the first four fields of the
+ * message header (prog, vers, proc, direction).
+ */
+ if (n > 5 * 4) {
+ n = 5 * 4;
+ truncate = 1;
+ }
+
+ while (n > 0) {
+ sprintf (p, "%02x ", cbuf[i]);
+ p += 3;
+ n--;
+ i++;
+
+ if ((i & 3) == 0) {
+ strcpy (p, "| ");
+ p += 2;
+ }
+ }
+
+ if (truncate)
+ strcpy (p, "...");
+
+ return workspace;
+}
+
int
guestfs___set_busy (guestfs_h *g)
{
static void
child_cleanup (guestfs_h *g)
{
- if (g->verbose)
- fprintf (stderr, "child_cleanup: %p: child process died\n", g);
+ debug (g, "child_cleanup: %p: child process died", g);
/*if (g->pid > 0) kill (g->pid, SIGTERM);*/
if (g->recoverypid > 0) kill (g->recoverypid, 9);
waitpid (g->pid, NULL, 0);
if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
- close (g->fd[0]);
- close (g->fd[1]);
+ if (g->fd[0] >= 0) close (g->fd[0]);
+ if (g->fd[1] >= 0) close (g->fd[1]);
close (g->sock);
g->fd[0] = -1;
g->fd[1] = -1;
g->recoverypid = 0;
memset (&g->launch_t, 0, sizeof g->launch_t);
g->state = CONFIG;
- if (g->subprocess_quit_cb)
- g->subprocess_quit_cb (g, g->subprocess_quit_cb_data);
+ guestfs___call_callbacks_void (g, GUESTFS_EVENT_SUBPROCESS_QUIT);
}
static int
int n;
#if 0
- if (g->verbose)
- fprintf (stderr,
- "read_log_message_or_eof: %p g->state = %d, fd = %d\n",
- g, g->state, fd);
+ debug (g, "read_log_message_or_eof: %p g->state = %d, fd = %d",
+ g, g->state, fd);
#endif
/* 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. */
return -1;
}
- /* In verbose mode, copy all log messages to stderr. */
- if (g->verbose)
- ignore_value (write (STDERR_FILENO, buf, n));
-
/* It's an actual log message, send it upwards if anyone is listening. */
- if (g->log_message_cb)
- g->log_message_cb (g, g->log_message_cb_data, buf, n);
+ 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;
}
+/* Convenient wrapper to generate a progress message callback. */
+void
+guestfs___progress_message_callback (guestfs_h *g,
+ const guestfs_progress *message)
+{
+ uint64_t array[4];
+
+ array[0] = message->proc;
+ array[1] = message->serial;
+ array[2] = message->position;
+ array[3] = message->total;
+
+ guestfs___call_callbacks_array (g, GUESTFS_EVENT_PROGRESS,
+ array, sizeof array / sizeof array[0]);
+}
+
static int
check_for_daemon_cancellation_or_eof (guestfs_h *g, int fd)
{
+ char summary[MAX_MESSAGE_SUMMARY];
char buf[4];
ssize_t n;
uint32_t flag;
XDR xdr;
- if (g->verbose)
- fprintf (stderr,
- "check_for_daemon_cancellation_or_eof: %p g->state = %d, fd = %d\n",
- g, g->state, fd);
-
n = really_read_from_socket (g, fd, buf, 4);
if (n == -1)
return -1;
return -1;
}
+ debug (g, "check_for_daemon_cancellation_or_eof: %s",
+ message_summary (buf, 4, summary));
+
xdrmem_create (&xdr, buf, 4, XDR_DECODE);
xdr_uint32_t (&xdr, &flag);
xdr_destroy (&xdr);
return -1;
}
- if (g->state == BUSY && g->progress_cb) {
+ if (g->state == BUSY) {
guestfs_progress message;
xdrmem_create (&xdr, buf, PROGRESS_MESSAGE_SIZE, XDR_DECODE);
xdr_guestfs_progress (&xdr, &message);
xdr_destroy (&xdr);
- g->progress_cb (g, g->progress_cb_data,
- message.proc, message.serial,
- message.position, message.total);
+ guestfs___progress_message_callback (g, &message);
}
return 0;
const char *buf = v_buf;
fd_set rset, rset2;
fd_set wset, wset2;
+ char summary[MAX_MESSAGE_SUMMARY];
- if (g->verbose)
- fprintf (stderr,
- "send_to_daemon: %p g->state = %d, n = %zu\n", g, g->state, n);
+ debug (g, "send_to_daemon: %zu bytes: %s", n,
+ message_summary (v_buf, n, summary));
FD_ZERO (&rset);
FD_ZERO (&wset);
- FD_SET (g->fd[1], &rset); /* Read qemu stdout for log messages & EOF. */
+ if (g->fd[1] >= 0) /* Read qemu stdout for log messages & EOF. */
+ FD_SET (g->fd[1], &rset);
FD_SET (g->sock, &rset); /* Read socket for cancellation & EOF. */
FD_SET (g->sock, &wset); /* Write to socket to send the data. */
return -1;
}
- if (FD_ISSET (g->fd[1], &rset2)) {
+ if (g->fd[1] >= 0 && FD_ISSET (g->fd[1], &rset2)) {
if (read_log_message_or_eof (g, g->fd[1], 0) == -1)
return -1;
}
if (FD_ISSET (g->sock, &rset2)) {
r = check_for_daemon_cancellation_or_eof (g, g->sock);
- if (r < 0)
- return r;
+ if (r == -1)
+ return r;
+ if (r == -2) {
+ /* Daemon sent cancel message. But to maintain
+ * synchronization we must write out the remainder of the
+ * write buffer before we return (RHBZ#576879).
+ */
+ if (xwrite (g->sock, buf, n) == -1) {
+ perrorf (g, "write");
+ return -1;
+ }
+ return -2; /* cancelled */
+ }
}
if (FD_ISSET (g->sock, &wset2)) {
r = write (g->sock, buf, n);
int
guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn)
{
+ char summary[MAX_MESSAGE_SUMMARY];
fd_set rset, rset2;
- if (g->verbose)
- fprintf (stderr,
- "recv_from_daemon: %p g->state = %d, size_rtn = %p, buf_rtn = %p\n",
- g, g->state, size_rtn, buf_rtn);
-
FD_ZERO (&rset);
- FD_SET (g->fd[1], &rset); /* Read qemu stdout for log messages & EOF. */
+ if (g->fd[1] >= 0) /* Read qemu stdout for log messages & EOF. */
+ FD_SET (g->fd[1], &rset);
FD_SET (g->sock, &rset); /* Read socket for data & EOF. */
int max_fd = MAX (g->sock, g->fd[1]);
return -1;
}
- if (FD_ISSET (g->fd[1], &rset2)) {
+ if (g->fd[1] >= 0 && FD_ISSET (g->fd[1], &rset2)) {
if (read_log_message_or_eof (g, g->fd[1], 0) == -1) {
free (*buf_rtn);
*buf_rtn = NULL;
g->state);
else {
g->state = READY;
- if (g->launch_done_cb)
- g->launch_done_cb (g, g->launch_done_cb_data);
+ guestfs___call_callbacks_void (g, GUESTFS_EVENT_LAUNCH_DONE);
}
+ debug (g, "recv_from_daemon: received GUESTFS_LAUNCH_FLAG");
return 0;
}
- else if (*size_rtn == GUESTFS_CANCEL_FLAG)
+ else if (*size_rtn == GUESTFS_CANCEL_FLAG) {
+ debug (g, "recv_from_daemon: received GUESTFS_CANCEL_FLAG");
return 0;
+ }
else if (*size_rtn == GUESTFS_PROGRESS_FLAG)
/*FALLTHROUGH*/;
/* If this happens, it's pretty bad and we've probably lost
#endif
if (*size_rtn == GUESTFS_PROGRESS_FLAG) {
- if (g->state == BUSY && g->progress_cb) {
+ if (g->state == BUSY) {
guestfs_progress message;
XDR xdr;
xdrmem_create (&xdr, *buf_rtn, PROGRESS_MESSAGE_SIZE, XDR_DECODE);
xdr_guestfs_progress (&xdr, &message);
xdr_destroy (&xdr);
- g->progress_cb (g, g->progress_cb_data,
- message.proc, message.serial,
- message.position, message.total);
+ guestfs___progress_message_callback (g, &message);
}
free (*buf_rtn);
return guestfs___recv_from_daemon (g, size_rtn, buf_rtn);
}
+ debug (g, "recv_from_daemon: %" PRIu32 " bytes: %s", *size_rtn,
+ message_summary (*buf_rtn, *size_rtn, summary));
+
return 0;
}
{
fd_set rset, rset2;
- if (g->verbose)
- fprintf (stderr,
- "accept_from_daemon: %p g->state = %d\n", g, g->state);
+ debug (g, "accept_from_daemon: %p g->state = %d", g, g->state);
FD_ZERO (&rset);
- FD_SET (g->fd[1], &rset); /* Read qemu stdout for log messages & EOF. */
+ if (g->fd[1] >= 0) /* Read qemu stdout for log messages & EOF. */
+ FD_SET (g->fd[1], &rset);
FD_SET (g->sock, &rset); /* Read socket for accept. */
int max_fd = MAX (g->sock, g->fd[1]);
return -1;
}
- if (FD_ISSET (g->fd[1], &rset2)) {
+ if (g->fd[1] >= 0 && FD_ISSET (g->fd[1], &rset2)) {
if (read_log_message_or_eof (g, g->fd[1], 1) == -1)
return -1;
}
}
int
-guestfs___send (guestfs_h *g, int proc_nr, uint64_t progress_hint,
+guestfs___send (guestfs_h *g, int proc_nr,
+ uint64_t progress_hint, uint64_t optargs_bitmask,
xdrproc_t xdrp, char *args)
{
struct guestfs_message_header hdr;
hdr.serial = serial;
hdr.status = GUESTFS_STATUS_OK;
hdr.progress_hint = progress_hint;
- hdr.optargs_bitmask = 0;
+ hdr.optargs_bitmask = optargs_bitmask;
if (!xdr_guestfs_message_header (&xdr, &hdr)) {
error (g, _("xdr_guestfs_message_header failed"));
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;
}
/* Did the daemon send a cancellation message? */
if (r == -2) {
- if (g->verbose)
- fprintf (stderr, "got daemon cancellation\n");
+ debug (g, "got daemon cancellation");
return -2;
}
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 */
char fbuf[4];
uint32_t flag = GUESTFS_CANCEL_FLAG;
- if (g->verbose)
- fprintf (stderr, "%s: waiting for daemon to acknowledge cancellation\n",
- __func__);
+ debug (g, "%s: waiting for daemon to acknowledge cancellation",
+ __func__);
xdrmem_create (&xdr, fbuf, sizeof fbuf, XDR_ENCODE);
xdr_uint32_t (&xdr, &flag);