X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fguestfs.c;h=89b16e2ed69f36aa7c24f7b5df128db783f8bbc7;hp=3492c624125aa840264f12b86c376a9fa76e6d97;hb=8e54ab289550a35747530e5b723fd35a22aa689a;hpb=fdeae81d81e979d74059f01350e94dbf37700075 diff --git a/src/guestfs.c b/src/guestfs.c index 3492c62..89b16e2 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -56,42 +56,86 @@ #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, ...); -static void *safe_malloc (guestfs_h *g, int nbytes); -static void *safe_realloc (guestfs_h *g, void *ptr, int nbytes); -static char *safe_strdup (guestfs_h *g, const char *str); +void guestfs_error (guestfs_h *g, const char *fs, ...); +void guestfs_perrorf (guestfs_h *g, const char *fs, ...); +void *guestfs_safe_malloc (guestfs_h *g, size_t nbytes); +void *guestfs_safe_realloc (guestfs_h *g, void *ptr, int nbytes); +char *guestfs_safe_strdup (guestfs_h *g, const char *str); +void *guestfs_safe_memdup (guestfs_h *g, void *ptr, size_t size); + +#define error guestfs_error +#define perrorf guestfs_perrorf +#define safe_malloc guestfs_safe_malloc +#define safe_realloc guestfs_safe_realloc +#define safe_strdup guestfs_safe_strdup +#define safe_memdup guestfs_safe_memdup 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 stdout_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, int watch, int fd, int events); +static void sock_read_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, int watch, int fd, int events); +static void sock_write_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, int watch, int fd, int events); static void close_handles (void); -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); -static int select_add_timeout (guestfs_h *g, int interval, guestfs_handle_timeout_cb cb, void *data); -static int select_remove_timeout (guestfs_h *g, int timer); -static void select_main_loop_run (guestfs_h *g); -static void select_main_loop_quit (guestfs_h *g); +static int select_add_handle (guestfs_main_loop *ml, guestfs_h *g, int fd, int events, guestfs_handle_event_cb cb, void *data); +static int select_remove_handle (guestfs_main_loop *ml, guestfs_h *g, int watch); +static int select_add_timeout (guestfs_main_loop *ml, guestfs_h *g, int interval, guestfs_handle_timeout_cb cb, void *data); +static int select_remove_timeout (guestfs_main_loop *ml, guestfs_h *g, int timer); +static int select_main_loop_run (guestfs_main_loop *ml, guestfs_h *g); +static int select_main_loop_quit (guestfs_main_loop *ml, guestfs_h *g); -#define UNIX_PATH_MAX 108 +/* Default select-based main loop. */ +struct select_handle_cb_data { + guestfs_handle_event_cb cb; + guestfs_h *g; + void *data; +}; -/* Also in guestfsd.c */ -#define VMCHANNEL_PORT 6666 -#define VMCHANNEL_ADDR "10.0.2.4" +struct select_main_loop { + /* NB. These fields must be the same as in struct guestfs_main_loop: */ + guestfs_add_handle_cb add_handle; + guestfs_remove_handle_cb remove_handle; + guestfs_add_timeout_cb add_timeout; + guestfs_remove_timeout_cb remove_timeout; + guestfs_main_loop_run_cb main_loop_run; + guestfs_main_loop_quit_cb main_loop_quit; + + /* Additional private data: */ + int is_running; + + fd_set rset; + fd_set wset; + fd_set xset; + + int max_fd; + int nr_fds; + struct select_handle_cb_data *handle_cb_data; +}; -/* Current main loop. */ -static guestfs_main_loop main_loop = { +/* Default main loop. */ +static struct select_main_loop default_main_loop = { .add_handle = select_add_handle, .remove_handle = select_remove_handle, .add_timeout = select_add_timeout, .remove_timeout = select_remove_timeout, .main_loop_run = select_main_loop_run, .main_loop_quit = select_main_loop_quit, + + /* XXX hopefully .rset, .wset, .xset are initialized to the empty + * set by the normal action of everything being initialized to zero. + */ + .is_running = 0, + .max_fd = -1, + .nr_fds = 0, + .handle_cb_data = NULL, }; +#define UNIX_PATH_MAX 108 + +/* Also in guestfsd.c */ +#define VMCHANNEL_PORT 6666 +#define VMCHANNEL_ADDR "10.0.2.4" + /* GuestFS handle and connection. */ enum state { CONFIG, LAUNCHING, READY, BUSY, NO_HANDLE }; @@ -120,10 +164,14 @@ struct guestfs_h const char *path; + char *last_error; + /* Callbacks. */ guestfs_abort_cb abort_cb; guestfs_error_handler_cb error_cb; void * error_cb_data; + guestfs_send_cb send_cb; + void * send_cb_data; guestfs_reply_cb reply_cb; void * reply_cb_data; guestfs_log_message_cb log_message_cb; @@ -133,14 +181,8 @@ struct guestfs_h guestfs_launch_done_cb launch_done_cb; void * launch_done_cb_data; - /* These callbacks are called before reply_cb and launch_done_cb, - * and are used to implement the high-level API without needing to - * interfere with callbacks that the user might have set. - */ - guestfs_reply_cb reply_cb_internal; - void * reply_cb_internal_data; - guestfs_launch_done_cb launch_done_cb_internal; - void * launch_done_cb_internal_data; + /* Main loop used by this handle. */ + guestfs_main_loop *main_loop; /* Messages sent and received from the daemon. */ char *msg_in; @@ -184,6 +226,8 @@ guestfs_create (void) g->path = str != NULL ? str : GUESTFS_DEFAULT_PATH; /* XXX We should probably make QEMU configurable as well. */ + g->main_loop = guestfs_get_default_main_loop (); + /* Start with large serial numbers so they are easy to spot * inside the protocol. */ @@ -263,6 +307,7 @@ guestfs_close (guestfs_h *g) } /* release mutex (XXX) */ + free (g->last_error); free (g); } @@ -273,38 +318,48 @@ close_handles (void) while (handles) guestfs_close (handles); } +const char * +guestfs_last_error (guestfs_h *g) +{ + return g->last_error; +} + +static void +set_last_error (guestfs_h *g, const char *msg) +{ + free (g->last_error); + g->last_error = strdup (msg); +} + static void default_error_cb (guestfs_h *g, void *data, const char *msg) { fprintf (stderr, "libguestfs: error: %s\n", msg); } -static void -error (guestfs_h *g, const char *fs, ...) +void +guestfs_error (guestfs_h *g, const char *fs, ...) { va_list args; char *msg; - if (!g->error_cb) return; - va_start (args, fs); vasprintf (&msg, fs, args); va_end (args); - g->error_cb (g, g->error_cb_data, msg); + if (g->error_cb) g->error_cb (g, g->error_cb_data, msg); + set_last_error (g, msg); free (msg); } -static void -perrorf (guestfs_h *g, const char *fs, ...) +void +guestfs_perrorf (guestfs_h *g, const char *fs, ...) { va_list args; char *msg; int err = errno; - if (!g->error_cb) return; - va_start (args, fs); vasprintf (&msg, fs, args); va_end (args); @@ -322,35 +377,45 @@ perrorf (guestfs_h *g, const char *fs, ...) strcat (msg, ": "); strcat (msg, buf); - g->error_cb (g, g->error_cb_data, msg); + if (g->error_cb) g->error_cb (g, g->error_cb_data, msg); + set_last_error (g, msg); free (msg); } -static void * -safe_malloc (guestfs_h *g, int nbytes) +void * +guestfs_safe_malloc (guestfs_h *g, size_t nbytes) { void *ptr = malloc (nbytes); if (!ptr) g->abort_cb (); return ptr; } -static void * -safe_realloc (guestfs_h *g, void *ptr, int nbytes) +void * +guestfs_safe_realloc (guestfs_h *g, void *ptr, int nbytes) { void *p = realloc (ptr, nbytes); if (!p) g->abort_cb (); return p; } -static char * -safe_strdup (guestfs_h *g, const char *str) +char * +guestfs_safe_strdup (guestfs_h *g, const char *str) { char *s = strdup (str); if (!s) g->abort_cb (); return s; } +void * +guestfs_safe_memdup (guestfs_h *g, void *ptr, size_t size) +{ + void *p = malloc (size); + if (!p) g->abort_cb (); + memcpy (p, ptr, size); + return p; +} + void guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_cb cb) { @@ -377,10 +442,11 @@ guestfs_get_error_handler (guestfs_h *g, void **data_rtn) return g->error_cb; } -void +int guestfs_set_verbose (guestfs_h *g, int v) { - g->verbose = v; + g->verbose = !!v; + return 0; } int @@ -389,10 +455,11 @@ guestfs_get_verbose (guestfs_h *g) return g->verbose; } -void +int guestfs_set_autosync (guestfs_h *g, int a) { - g->autosync = a; + g->autosync = !!a; + return 0; } int @@ -401,13 +468,14 @@ guestfs_get_autosync (guestfs_h *g) return g->autosync; } -void +int guestfs_set_path (guestfs_h *g, const char *path) { if (path == NULL) g->path = GUESTFS_DEFAULT_PATH; else g->path = path; + return 0; } const char * @@ -753,18 +821,18 @@ guestfs_launch (guestfs_h *g) g->msg_out_pos = 0; g->stdout_watch = - main_loop.add_handle (g, g->fd[1], - GUESTFS_HANDLE_READABLE, - stdout_event, g); + g->main_loop->add_handle (g->main_loop, g, g->fd[1], + GUESTFS_HANDLE_READABLE, + stdout_event, NULL); if (g->stdout_watch == -1) { error (g, "could not watch qemu stdout"); goto cleanup3; } g->sock_watch = - main_loop.add_handle (g, g->sock, - GUESTFS_HANDLE_READABLE, - sock_read_event, g); + g->main_loop->add_handle (g->main_loop, g, g->sock, + GUESTFS_HANDLE_READABLE, + sock_read_event, NULL); if (g->sock_watch == -1) { error (g, "could not watch daemon communications socket"); goto cleanup3; @@ -775,9 +843,9 @@ guestfs_launch (guestfs_h *g) cleanup3: if (g->stdout_watch >= 0) - main_loop.remove_handle (g, g->stdout_watch); + g->main_loop->remove_handle (g->main_loop, g, g->stdout_watch); if (g->sock_watch >= 0) - main_loop.remove_handle (g, g->sock_watch); + g->main_loop->remove_handle (g->main_loop, g, g->sock_watch); cleanup2: close (g->sock); @@ -804,14 +872,17 @@ guestfs_launch (guestfs_h *g) static void finish_wait_ready (guestfs_h *g, void *vp) { + if (g->verbose) + fprintf (stderr, "finish_wait_ready called, %p, vp = %p\n", g, vp); + *((int *)vp) = 1; - main_loop.main_loop_quit (g); + g->main_loop->main_loop_quit (g->main_loop, g); } int guestfs_wait_ready (guestfs_h *g) { - int r = 0; + int finished = 0, r; if (g->state == READY) return 0; @@ -825,13 +896,15 @@ guestfs_wait_ready (guestfs_h *g) return -1; } - g->launch_done_cb_internal = finish_wait_ready; - g->launch_done_cb_internal_data = &r; - main_loop.main_loop_run (g); - g->launch_done_cb_internal = NULL; - g->launch_done_cb_internal_data = NULL; + g->launch_done_cb = finish_wait_ready; + g->launch_done_cb_data = &finished; + r = g->main_loop->main_loop_run (g->main_loop, g); + g->launch_done_cb = NULL; + g->launch_done_cb_data = NULL; + + if (r == -1) return -1; - if (r != 1) { + if (finished != 1) { error (g, "guestfs_wait_ready failed, see earlier error messages"); return -1; } @@ -865,14 +938,76 @@ guestfs_kill_subprocess (guestfs_h *g) return 0; } +/* Access current state. */ +int +guestfs_is_config (guestfs_h *g) +{ + return g->state == CONFIG; +} + +int +guestfs_is_launching (guestfs_h *g) +{ + return g->state == LAUNCHING; +} + +int +guestfs_is_ready (guestfs_h *g) +{ + return g->state == READY; +} + +int +guestfs_is_busy (guestfs_h *g) +{ + return g->state == BUSY; +} + +int +guestfs_get_state (guestfs_h *g) +{ + return g->state; +} + +/* Structure-freeing functions. These rely on the fact that the + * structure format is identical to the XDR format. See note in + * generator.ml. + */ +void +guestfs_free_int_bool (struct guestfs_int_bool *x) +{ + free (x); +} + +void +guestfs_free_lvm_pv_list (struct guestfs_lvm_pv_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_pv_list, (char *) x); + free (x); +} + +void +guestfs_free_lvm_vg_list (struct guestfs_lvm_vg_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_vg_list, (char *) x); + free (x); +} + +void +guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_lv_list, (char *) x); + free (x); +} + /* This function is called whenever qemu prints something on stdout. * Qemu's stdout is also connected to the guest's serial console, so * we see kernel messages here too. */ static void -stdout_event (void *data, int watch, int fd, int events) +stdout_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, + int watch, int fd, int events) { - guestfs_h *g = (guestfs_h *) data; char buf[4096]; int n; @@ -896,9 +1031,9 @@ stdout_event (void *data, int watch, int fd, int events) /*kill (g->pid, SIGTERM);*/ waitpid (g->pid, NULL, 0); if (g->stdout_watch >= 0) - main_loop.remove_handle (g, g->stdout_watch); + g->main_loop->remove_handle (g->main_loop, g, g->stdout_watch); if (g->sock_watch >= 0) - main_loop.remove_handle (g, g->sock_watch); + g->main_loop->remove_handle (g->main_loop, g, g->sock_watch); close (g->fd[0]); close (g->fd[1]); close (g->sock); @@ -934,9 +1069,9 @@ stdout_event (void *data, int watch, int fd, int events) * guestfsd (daemon inside the guest) communication socket. */ static void -sock_read_event (void *data, int watch, int fd, int events) +sock_read_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, + int watch, int fd, int events) { - guestfs_h *g = (guestfs_h *) data; XDR xdr; unsigned len; int n; @@ -993,8 +1128,6 @@ sock_read_event (void *data, int watch, int fd, int events) g->msg_in_size); else { g->state = READY; - if (g->launch_done_cb_internal) - g->launch_done_cb_internal (g, g->launch_done_cb_internal_data); if (g->launch_done_cb) g->launch_done_cb (g, g->launch_done_cb_data); } @@ -1019,19 +1152,36 @@ sock_read_event (void *data, int watch, int fd, int events) goto cleanup; } + /* Got the full message, begin processing it. */ + if (g->verbose) { + int i, j; + + for (i = 0; i < g->msg_in_size; i += 16) { + printf ("%04x: ", i); + for (j = i; j < MIN (i+16, g->msg_in_size); ++j) + printf ("%02x ", (unsigned char) g->msg_in[j]); + for (; j < i+16; ++j) + printf (" "); + printf ("|"); + for (j = i; j < MIN (i+16, g->msg_in_size); ++j) + if (isprint (g->msg_in[j])) + printf ("%c", g->msg_in[j]); + else + printf ("."); + for (; j < i+16; ++j) + printf (" "); + printf ("|\n"); + } + } + /* Not in the expected state. */ if (g->state != BUSY) error (g, "state %d != BUSY", g->state); - /* Push the message up to the higher layer. Note that unlike - * launch_done_cb / launch_done_cb_internal, we only call at - * most one of the callback functions here. - */ + /* Push the message up to the higher layer. */ g->state = READY; - if (g->reply_cb_internal) - g->reply_cb_internal (g, g->reply_cb_internal_data, &xdr); - else if (g->reply_cb) - g->reply_cb (g, g->reply_cb, &xdr); + if (g->reply_cb) + g->reply_cb (g, g->reply_cb_data, &xdr); cleanup: /* Free the message buffer if it's grown excessively large. */ @@ -1049,9 +1199,9 @@ sock_read_event (void *data, int watch, int fd, int events) * guestfsd (daemon inside the guest) communication socket. */ static void -sock_write_event (void *data, int watch, int fd, int events) +sock_write_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, + int watch, int fd, int events) { - guestfs_h *g = (guestfs_h *) data; int n; if (g->verbose) @@ -1096,26 +1246,86 @@ sock_write_event (void *data, int watch, int fd, int events) free (g->msg_out); g->msg_out_pos = g->msg_out_size = 0; - if (main_loop.remove_handle (g, g->sock_watch) == -1) { + if (g->main_loop->remove_handle (g->main_loop, 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); + g->main_loop->add_handle (g->main_loop, g, g->sock, + GUESTFS_HANDLE_READABLE, + sock_read_event, NULL); 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. +void +guestfs_set_send_callback (guestfs_h *g, + guestfs_send_cb cb, void *opaque) +{ + g->send_cb = cb; + g->send_cb_data = opaque; +} + +void +guestfs_set_reply_callback (guestfs_h *g, + guestfs_reply_cb cb, void *opaque) +{ + g->reply_cb = cb; + g->reply_cb_data = opaque; +} + +void +guestfs_set_log_message_callback (guestfs_h *g, + guestfs_log_message_cb cb, void *opaque) +{ + g->log_message_cb = cb; + g->log_message_cb_data = opaque; +} + +void +guestfs_set_subprocess_quit_callback (guestfs_h *g, + guestfs_subprocess_quit_cb cb, void *opaque) +{ + g->subprocess_quit_cb = cb; + g->subprocess_quit_cb_data = opaque; +} + +void +guestfs_set_launch_done_callback (guestfs_h *g, + guestfs_launch_done_cb cb, void *opaque) +{ + g->launch_done_cb = cb; + g->launch_done_cb_data = opaque; +} + +/* Access to the handle's main loop and the default main loop. */ +void +guestfs_set_main_loop (guestfs_h *g, guestfs_main_loop *main_loop) +{ + g->main_loop = main_loop; +} + +guestfs_main_loop * +guestfs_get_main_loop (guestfs_h *g) +{ + return g->main_loop; +} + +guestfs_main_loop * +guestfs_get_default_main_loop (void) +{ + return (guestfs_main_loop *) &default_main_loop; +} + +/* Dispatch a call (len + header + args) 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) +int +guestfs__send (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args) { char buffer[GUESTFS_MESSAGE_MAX]; struct guestfs_message_header hdr; @@ -1171,14 +1381,14 @@ dispatch (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args) memcpy (g->msg_out + 4, buffer, len); /* Change the handle to sock_write_event. */ - if (main_loop.remove_handle (g, g->sock_watch) == -1) { + if (g->main_loop->remove_handle (g->main_loop, 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); + g->main_loop->add_handle (g->main_loop, g, g->sock, + GUESTFS_HANDLE_WRITABLE, + sock_write_event, NULL); if (g->sock_watch == -1) { error (g, "add_handle failed in dispatch"); goto cleanup1; @@ -1194,75 +1404,146 @@ dispatch (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args) return -1; } -/* Check the return message from a call for validity. */ +#if 0 +static int cancel = 0; /* XXX Implement file cancellation. */ + static int -check_reply_header (guestfs_h *g, - const struct guestfs_message_header *hdr, - int proc_nr, int serial) +send_file (guestfs_h *g, const char *filename) { - if (hdr->prog != GUESTFS_PROGRAM) { - error (g, "wrong program (%d/%d)", hdr->prog, GUESTFS_PROGRAM); + char buf[GUESTFS_MAX_CHUNK_SIZE]; + int fd, r; + + 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; } - if (hdr->vers != GUESTFS_PROTOCOL_VERSION) { - error (g, "wrong protocol version (%d/%d)", - hdr->vers, GUESTFS_PROTOCOL_VERSION); - return -1; + + /* Send file in chunked encoding. */ + while (!cancel && (r = read (fd, buf, sizeof buf)) > 0) { + if (send_file_data (g, buf, r) == -1) + return -1; } - if (hdr->direction != GUESTFS_DIRECTION_REPLY) { - error (g, "unexpected message direction (%d/%d)", - hdr->direction, GUESTFS_DIRECTION_REPLY); + + if (cancel) { + send_file_cancellation (g); return -1; } - if (hdr->proc != proc_nr) { - error (g, "unexpected procedure number (%d/%d)", hdr->proc, proc_nr); + + if (r == -1) { + perrorf (g, "read: %s", filename); + send_file_cancellation (g); return -1; } - if (hdr->serial != serial) { - error (g, "unexpected serial (%d/%d)", hdr->serial, serial); + + /* End of file, but before we send that, we need to close + * the file and check for errors. + */ + if (close (fd) == -1) { + perrorf (g, "close: %s", filename); + send_file_cancellation (g); return -1; } - return 0; + return send_file_complete (g); } -/* The high-level actions are autogenerated by generator.ml. Include - * them here. - */ -#include "guestfs-actions.c" +/* Send a chunk, cancellation or end of file, wait for it to go. */ +static int +send_file_chunk (guestfs_h *g, int cancel, const char *buf, size_t len) +{ + void *data; + guestfs_chunk chunk; + XDR xdr; -/* This is the default main loop implementation, using select(2). */ + if (g->state != BUSY) { + error (g, "send_file_chunk: state %d != READY", g->state); + return -1; + } -struct handle_cb_data { - guestfs_handle_event_cb cb; - void *data; -}; + /* Serialize the chunk. */ + chunk.cancel = cancel; + chunk.data.data_len = len; + chunk.data.data_val = (char *) buf; -static fd_set rset; -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; + data = safe_malloc (g, GUESTFS_MAX_CHUNK_SIZE + 48); + xdrmem_create (&xdr, data, GUESTFS_MAX_CHUNK_SIZE + 48, XDR_ENCODE); + if (xdr_guestfs_chunk (&xdr, &chunk)) { + error (g, "xdr_guestfs_chunk failed"); + free (data); + return -1; + } -static void -select_init (void) -{ - if (!select_init_done) { - FD_ZERO (&rset); - FD_ZERO (&wset); - FD_ZERO (&xset); + chunkdatalen = xdr_getpos (&xdr); + xdr_destroy (&xdr); + + len = xdr_getpos (&xdr); + xdr_destroy (&xdr); - select_init_done = 1; + data = safe_realloc (g, data, len); + g->msg_out = data; + g->msg_out_size = len; + g->msg_out_pos = 0; + + /* Change the handle to sock_write_event. */ + if (g->main_loop->remove_handle (g->main_loop, g, g->sock_watch) == -1) { + error (g, "remove_handle failed in dispatch"); + goto cleanup1; } + g->sock_watch = + g->main_loop->add_handle (g->main_loop, g, g->sock, + GUESTFS_HANDLE_WRITABLE, + sock_write_event, NULL); + if (g->sock_watch == -1) { + error (g, "add_handle failed in dispatch"); + goto cleanup1; + } + + return 0; + + cleanup1: + free (g->msg_out); + g->msg_out = NULL; + g->msg_out_size = 0; + g->state = READY; + return -1; } +/* Send a chunk of file data. */ static int -select_add_handle (guestfs_h *g, int fd, int events, +send_file_data (guestfs_h *g, const char *buf, size_t len) +{ + return send_file_chunk (g, 0, buf, len); +} + +/* Send a cancellation message. */ +static int +send_file_cancellation (guestfs_h *g) +{ + char buf[1]; + return send_file_chunk (g, 1, buf, 0); +} + +/* Send a file complete chunk. */ +static int +send_file_complete (guestfs_h *g) +{ + char buf[0]; + return send_file_chunk (g, 0, buf, 0); +} +#endif + +/* This is the default main loop implementation, using select(2). */ + +static int +select_add_handle (guestfs_main_loop *mlv, guestfs_h *g, int fd, int events, guestfs_handle_event_cb cb, void *data) { - select_init (); + struct select_main_loop *ml = (struct select_main_loop *) mlv; if (fd < 0 || fd >= FD_SETSIZE) { error (g, "fd %d is out of range", fd); @@ -1282,7 +1563,9 @@ select_add_handle (guestfs_h *g, int fd, int events, return -1; } - if (FD_ISSET (fd, &rset) || FD_ISSET (fd, &wset) || FD_ISSET (fd, &xset)) { + if (FD_ISSET (fd, &ml->rset) || + FD_ISSET (fd, &ml->wset) || + FD_ISSET (fd, &ml->xset)) { error (g, "fd %d is already registered", fd); return -1; } @@ -1293,102 +1576,111 @@ select_add_handle (guestfs_h *g, int fd, int events, } if ((events & GUESTFS_HANDLE_READABLE)) - FD_SET (fd, &rset); + FD_SET (fd, &ml->rset); if ((events & GUESTFS_HANDLE_WRITABLE)) - FD_SET (fd, &wset); + FD_SET (fd, &ml->wset); if ((events & GUESTFS_HANDLE_HANGUP) || (events & GUESTFS_HANDLE_ERROR)) - FD_SET (fd, &xset); + FD_SET (fd, &ml->xset); - if (fd > max_fd) { - max_fd = fd; - handle_cb_data = safe_realloc (g, handle_cb_data, - sizeof (struct handle_cb_data) * (max_fd+1)); + if (fd > ml->max_fd) { + ml->max_fd = fd; + ml->handle_cb_data = + safe_realloc (g, ml->handle_cb_data, + sizeof (struct select_handle_cb_data) * (ml->max_fd+1)); } - handle_cb_data[fd].cb = cb; - handle_cb_data[fd].data = data; + ml->handle_cb_data[fd].cb = cb; + ml->handle_cb_data[fd].g = g; + ml->handle_cb_data[fd].data = data; - nr_fds++; + ml->nr_fds++; /* Any integer >= 0 can be the handle, and this is as good as any ... */ return fd; } static int -select_remove_handle (guestfs_h *g, int fd) +select_remove_handle (guestfs_main_loop *mlv, guestfs_h *g, int fd) { - select_init (); + struct select_main_loop *ml = (struct select_main_loop *) mlv; if (fd < 0 || fd >= FD_SETSIZE) { error (g, "fd %d is out of range", fd); return -1; } - if (!FD_ISSET (fd, &rset) && !FD_ISSET (fd, &wset) && !FD_ISSET (fd, &xset)) { + if (!FD_ISSET (fd, &ml->rset) && + !FD_ISSET (fd, &ml->wset) && + !FD_ISSET (fd, &ml->xset)) { error (g, "fd %d was not registered", fd); return -1; } - FD_CLR (fd, &rset); - FD_CLR (fd, &wset); - FD_CLR (fd, &xset); + FD_CLR (fd, &ml->rset); + FD_CLR (fd, &ml->wset); + FD_CLR (fd, &ml->xset); - if (fd == max_fd) { - max_fd--; - handle_cb_data = safe_realloc (g, handle_cb_data, - sizeof (struct handle_cb_data) * (max_fd+1)); + if (fd == ml->max_fd) { + ml->max_fd--; + ml->handle_cb_data = + safe_realloc (g, ml->handle_cb_data, + sizeof (struct select_handle_cb_data) * (ml->max_fd+1)); } - nr_fds--; + ml->nr_fds--; return 0; } static int -select_add_timeout (guestfs_h *g, int interval, +select_add_timeout (guestfs_main_loop *mlv, guestfs_h *g, int interval, guestfs_handle_timeout_cb cb, void *data) { - select_init (); + //struct select_main_loop *ml = (struct select_main_loop *) mlv; abort (); /* XXX not implemented yet */ } static int -select_remove_timeout (guestfs_h *g, int timer) +select_remove_timeout (guestfs_main_loop *mlv, guestfs_h *g, int timer) { - select_init (); + //struct select_main_loop *ml = (struct select_main_loop *) mlv; abort (); /* XXX not implemented yet */ } -/* Note that main loops can be nested. */ -static int level = 0; - -static void -select_main_loop_run (guestfs_h *g) +/* The 'g' parameter is just used for error reporting. Events + * for multiple handles can be dispatched by running the main + * loop. + */ +static int +select_main_loop_run (guestfs_main_loop *mlv, guestfs_h *g) { - int old_level, fd, r, events; + struct select_main_loop *ml = (struct select_main_loop *) mlv; + int fd, r, events; fd_set rset2, wset2, xset2; - select_init (); + if (ml->is_running) { + error (g, "select_main_loop_run: this cannot be called recursively"); + return -1; + } + + ml->is_running = 1; - old_level = level++; - while (level > old_level) { - if (nr_fds == 0) { - level = old_level; + while (ml->is_running) { + if (ml->nr_fds == 0) break; - } - rset2 = rset; - wset2 = wset; - xset2 = xset; - r = select (max_fd+1, &rset2, &wset2, &xset2, NULL); + rset2 = ml->rset; + wset2 = ml->wset; + xset2 = ml->xset; + r = select (ml->max_fd+1, &rset2, &wset2, &xset2, NULL); if (r == -1) { perrorf (g, "select"); - level = old_level; - break; + ml->is_running = 0; + return -1; } - for (fd = 0; r > 0 && fd <= max_fd; ++fd) { + for (fd = 0; r > 0 && fd <= ml->max_fd; ++fd) { events = 0; if (FD_ISSET (fd, &rset2)) events |= GUESTFS_HANDLE_READABLE; @@ -1398,22 +1690,28 @@ select_main_loop_run (guestfs_h *g) events |= GUESTFS_HANDLE_ERROR | GUESTFS_HANDLE_HANGUP; if (events) { r--; - handle_cb_data[fd].cb (handle_cb_data[fd].data, - fd, fd, events); + ml->handle_cb_data[fd].cb ((guestfs_main_loop *) ml, + ml->handle_cb_data[fd].g, + ml->handle_cb_data[fd].data, + fd, fd, events); } } } + + ml->is_running = 0; + return 0; } -static void -select_main_loop_quit (guestfs_h *g) +static int +select_main_loop_quit (guestfs_main_loop *mlv, guestfs_h *g) { - select_init (); + struct select_main_loop *ml = (struct select_main_loop *) mlv; - if (level == 0) { - error (g, "cannot quit, we are not in a main loop"); - return; + if (!ml->is_running) { + error (g, "cannot quit, we are not running in a main loop"); + return -1; } - level--; + ml->is_running = 0; + return 0; }