X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Fguestfs.c;h=47af20ad0b4484cd319b21b796166462a9786006;hb=316bbc36662c0df6b3d0ad48790e0b551a291df6;hp=7e883035bd95854b4711047ef56f3ae3139647c5;hpb=163e399a001a300a53a4c99887742608d23e09ad;p=libguestfs.git diff --git a/src/guestfs.c b/src/guestfs.c index 7e88303..47af20a 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -54,6 +54,7 @@ #endif #include "guestfs.h" +#include "guestfs_protocol.h" static void error (guestfs_h *g, const char *fs, ...); static void perrorf (guestfs_h *g, const char *fs, ...); @@ -64,7 +65,7 @@ static char *safe_strdup (guestfs_h *g, const char *str); static void default_error_cb (guestfs_h *g, void *data, const char *msg); static void stdout_event (void *data, int watch, int fd, int events); static void sock_read_event (void *data, int watch, int fd, int events); -//static void sock_write_event (void *data, int watch, int fd, int events); +static void sock_write_event (void *data, int watch, int fd, int events); static int select_add_handle (guestfs_h *g, int fd, int events, guestfs_handle_event_cb cb, void *data); static int select_remove_handle (guestfs_h *g, int watch); @@ -75,6 +76,7 @@ static void select_main_loop_quit (guestfs_h *g); #define UNIX_PATH_MAX 108 +/* Also in guestfsd.c */ #define VMCHANNEL_PORT 6666 #define VMCHANNEL_ADDR "10.0.2.4" @@ -137,7 +139,9 @@ struct guestfs_h char *msg_in; int msg_in_size, msg_in_allocated; char *msg_out; - int msg_out_size; + int msg_out_size, msg_out_pos; + + int msg_next_serial; }; guestfs_h * @@ -166,6 +170,11 @@ guestfs_create (void) str = getenv ("LIBGUESTFS_DEBUG"); g->verbose = str != NULL && strcmp (str, "1") == 0; + /* Start with large serial numbers so they are easy to spot + * inside the protocol. + */ + g->msg_next_serial = 0x00123400; + return g; } @@ -488,7 +497,8 @@ guestfs_launch (guestfs_h *g) "console=ttyS0 guestfs=%s:%d", VMCHANNEL_ADDR, VMCHANNEL_PORT); add_cmdline (g, "-m"); - add_cmdline (g, "384"); /* XXX Choose best size. */ + add_cmdline (g, "384"); /* XXX Choose best size. */ + add_cmdline (g, "-no-kqemu"); /* Avoids a warning. */ add_cmdline (g, "-kernel"); add_cmdline (g, (char *) kernel); add_cmdline (g, "-initrd"); @@ -586,7 +596,8 @@ guestfs_launch (guestfs_h *g) if ((r == -1 && errno == EINPROGRESS) || r == 0) goto connected; - perrorf (g, "connect"); + if (errno != ENOENT) + perrorf (g, "connect"); tries--; } @@ -602,6 +613,7 @@ guestfs_launch (guestfs_h *g) free (g->msg_out); g->msg_out = NULL; g->msg_out_size = 0; + g->msg_out_pos = 0; g->stdout_watch = main_loop.add_handle (g, g->fd[1], @@ -614,9 +626,7 @@ guestfs_launch (guestfs_h *g) g->sock_watch = main_loop.add_handle (g, g->sock, - GUESTFS_HANDLE_READABLE | - GUESTFS_HANDLE_HANGUP | - GUESTFS_HANDLE_ERROR, + GUESTFS_HANDLE_READABLE, sock_read_event, g); if (g->sock_watch == -1) { error (g, "could not watch daemon communications socket"); @@ -792,7 +802,7 @@ sock_read_event (void *data, int watch, int fd, int events) if (g->verbose) fprintf (stderr, - "sock_event: %p g->state = %d, fd = %d, events = 0x%x\n", + "sock_read_event: %p g->state = %d, fd = %d, events = 0x%x\n", g, g->state, fd, events); if (g->sock != fd) { @@ -833,7 +843,7 @@ sock_read_event (void *data, int watch, int fd, int events) * starts up it sends a "magic" value (longer than any possible * message). Check for this. */ - if (len == 0xf5f5f5f5) { + if (len == 0xf5f55ff5) { if (g->state != LAUNCHING) error (g, "received magic signature from guestfsd, but in state %d", g->state); @@ -851,13 +861,20 @@ sock_read_event (void *data, int watch, int fd, int events) goto cleanup; } - if (g->msg_in_size < len) return; /* Need more of this message. */ + /* If this happens, it's pretty bad and we've probably lost synchronization.*/ + if (len > GUESTFS_MESSAGE_MAX) { + error (g, "message length (%u) > maximum possible size (%d)", + len, GUESTFS_MESSAGE_MAX); + goto cleanup; + } + + if (g->msg_in_size-4 < len) return; /* Need more of this message. */ /* This should not happen, and if it does it probably means we've * lost all hope of synchronization. */ - if (g->msg_in_size > len) { - error (g, "len = %d, but msg_in_size = %d", len, g->msg_in_size); + if (g->msg_in_size-4 > len) { + error (g, "len = %d, but msg_in_size-4 = %d", len, g->msg_in_size-4); goto cleanup; } @@ -887,6 +904,192 @@ sock_read_event (void *data, int watch, int fd, int events) xdr_destroy (&xdr); } +/* The function is called whenever we can write something on the + * guestfsd (daemon inside the guest) communication socket. + */ +static void +sock_write_event (void *data, int watch, int fd, int events) +{ + guestfs_h *g = (guestfs_h *) data; + int n; + + if (g->verbose) + fprintf (stderr, + "sock_write_event: %p g->state = %d, fd = %d, events = 0x%x\n", + g, g->state, fd, events); + + if (g->sock != fd) { + error (g, "sock_write_event: internal error: %d != %d", g->sock, fd); + return; + } + + if (g->state != BUSY) { + error (g, "sock_write_event: state %d != BUSY", g->state); + return; + } + + if (g->verbose) + fprintf (stderr, "sock_write_event: writing %d bytes ...\n", + g->msg_out_size - g->msg_out_pos); + + n = write (g->sock, g->msg_out + g->msg_out_pos, + g->msg_out_size - g->msg_out_pos); + if (n == -1) { + if (errno != EAGAIN) + perrorf (g, "write"); + return; + } + + if (g->verbose) + fprintf (stderr, "sock_write_event: wrote %d bytes\n", n); + + g->msg_out_pos += n; + + /* More to write? */ + if (g->msg_out_pos < g->msg_out_size) + return; + + if (g->verbose) + fprintf (stderr, "sock_write_event: done writing, switching back to reading events\n"); + + free (g->msg_out); + g->msg_out_pos = g->msg_out_size = 0; + + if (main_loop.remove_handle (g, g->sock_watch) == -1) { + error (g, "remove_handle failed in sock_write_event"); + return; + } + g->sock_watch = + main_loop.add_handle (g, g->sock, + GUESTFS_HANDLE_READABLE, + sock_read_event, g); + if (g->sock_watch == -1) { + error (g, "add_handle failed in sock_write_event"); + return; + } +} + +/* Dispatch a call to the remote daemon. This function just queues + * the call in msg_out, to be sent when we next enter the main loop. + * Returns -1 for error, or the message serial number. + */ +static int +dispatch (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args) +{ + char buffer[GUESTFS_MESSAGE_MAX]; + struct guestfs_message_header hdr; + XDR xdr; + unsigned len; + int serial = g->msg_next_serial++; + + if (g->state != READY) { + error (g, "dispatch: state %d != READY", g->state); + return -1; + } + + /* Serialize the header. */ + hdr.prog = GUESTFS_PROGRAM; + hdr.vers = GUESTFS_PROTOCOL_VERSION; + hdr.proc = proc_nr; + hdr.direction = GUESTFS_DIRECTION_CALL; + hdr.serial = serial; + hdr.status = GUESTFS_STATUS_OK; + + xdrmem_create (&xdr, buffer, sizeof buffer, XDR_ENCODE); + if (!xdr_guestfs_message_header (&xdr, &hdr)) { + error (g, "xdr_guestfs_message_header failed"); + return -1; + } + + /* Serialize the args. If any, because some message types + * have no parameters. + */ + if (xdrp) { + if (!(*xdrp) (&xdr, args)) { + error (g, "dispatch failed to marshal args"); + return -1; + } + } + + len = xdr_getpos (&xdr); + xdr_destroy (&xdr); + + /* Allocate the outgoing message buffer. */ + g->msg_out = safe_malloc (g, len + 4); + + g->msg_out_size = len + 4; + g->msg_out_pos = 0; + g->state = BUSY; + + xdrmem_create (&xdr, g->msg_out, 4, XDR_ENCODE); + if (!xdr_uint32_t (&xdr, &len)) { + error (g, "xdr_uint32_t failed in dispatch"); + goto cleanup1; + } + + memcpy (g->msg_out + 4, buffer, len); + + /* Change the handle to sock_write_event. */ + if (main_loop.remove_handle (g, g->sock_watch) == -1) { + error (g, "remove_handle failed in dispatch"); + goto cleanup1; + } + g->sock_watch = + main_loop.add_handle (g, g->sock, + GUESTFS_HANDLE_WRITABLE, + sock_write_event, g); + if (g->sock_watch == -1) { + error (g, "add_handle failed in dispatch"); + goto cleanup1; + } + + return serial; + + cleanup1: + free (g->msg_out); + g->msg_out = NULL; + g->msg_out_size = 0; + g->state = READY; + return -1; +} + +/* Check the return message from a call for validity. */ +static int +check_reply_header (guestfs_h *g, + const struct guestfs_message_header *hdr, + int proc_nr, int serial) +{ + if (hdr->prog != GUESTFS_PROGRAM) { + error (g, "wrong program (%d/%d)", hdr->prog, GUESTFS_PROGRAM); + return -1; + } + if (hdr->vers != GUESTFS_PROTOCOL_VERSION) { + error (g, "wrong protocol version (%d/%d)", + hdr->vers, GUESTFS_PROTOCOL_VERSION); + return -1; + } + if (hdr->direction != GUESTFS_DIRECTION_REPLY) { + error (g, "unexpected message direction (%d/%d)", + hdr->direction, GUESTFS_DIRECTION_REPLY); + return -1; + } + if (hdr->proc != proc_nr) { + error (g, "unexpected procedure number (%d/%d)", hdr->proc, proc_nr); + return -1; + } + if (hdr->serial != serial) { + error (g, "unexpected serial (%d/%d)", hdr->serial, serial); + return -1; + } + + return 0; +} + +/* The high-level actions are autogenerated by generator.ml. Include + * them here. + */ +#include "guestfs-actions.c" + /* This is the default main loop implementation, using select(2). */ struct handle_cb_data { @@ -899,6 +1102,7 @@ static fd_set wset; static fd_set xset; static int select_init_done = 0; static int max_fd = -1; +static int nr_fds = 0; static struct handle_cb_data *handle_cb_data = NULL; static void @@ -962,6 +1166,8 @@ select_add_handle (guestfs_h *g, int fd, int events, handle_cb_data[fd].cb = cb; handle_cb_data[fd].data = data; + nr_fds++; + /* Any integer >= 0 can be the handle, and this is as good as any ... */ return fd; } @@ -991,6 +1197,8 @@ select_remove_handle (guestfs_h *g, int fd) sizeof (struct handle_cb_data) * (max_fd+1)); } + nr_fds--; + return 0; } @@ -1024,6 +1232,11 @@ select_main_loop_run (guestfs_h *g) old_level = level++; while (level > old_level) { + if (nr_fds == 0) { + level = old_level; + break; + } + rset2 = rset; wset2 = wset; xset2 = xset;