Implement _send_sync, _send_file_sync and _receive_file_sync.

This commit is contained in:
Richard Jones
2009-04-19 12:29:25 +01:00
parent bb07a7f858
commit 749708a7ec
5 changed files with 460 additions and 1471 deletions

View File

@@ -64,6 +64,13 @@ extern void main_loop (int sock);
extern void reply_with_error (const char *fs, ...);
extern void reply_with_perror (const char *fs, ...);
/* daemon functions that receive files (FileIn) should call
* receive_file for each FileIn parameter.
*/
#if 0
extern void receive_file ();
#endif
/* daemon functions that return files (FileOut) should call
* reply, then send_file for each FileOut parameter.
*/

View File

@@ -2164,23 +2164,14 @@ check_state (guestfs_h *g, const char *caller)
pr "};\n";
pr "\n";
(* Generate the send callback function. *)
pr "static void %s_send_cb (guestfs_h *g, void *data)\n" shortname;
pr "{\n";
pr " guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
pr " struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
pr "\n";
pr " ctx->cb_sequence = 1;\n";
pr " ml->main_loop_quit (ml, g);\n";
pr "}\n";
pr "\n";
(* Generate the reply callback function. *)
pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
pr "{\n";
pr " guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
pr " struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
pr "\n";
pr " ml->main_loop_quit (ml, g);\n";
pr "\n";
pr " if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {\n";
pr " error (g, \"%%s: failed to parse reply header\", \"%s\");\n" name;
pr " return;\n";
@@ -2212,7 +2203,6 @@ check_state (guestfs_h *g, const char *caller)
pr " done:\n";
pr " ctx->cb_sequence = 1001;\n";
pr " ml->main_loop_quit (ml, g);\n";
pr "}\n\n";
(* Generate the action stub. *)
@@ -2246,10 +2236,10 @@ check_state (guestfs_h *g, const char *caller)
pr " memset (&ctx, 0, sizeof ctx);\n";
pr "\n";
(* Dispatch the main header and arguments. *)
(* Send the main header and arguments. *)
(match snd style with
| [] ->
pr " serial = guestfs__send (g, GUESTFS_PROC_%s, NULL, NULL);\n"
pr " serial = guestfs__send_sync (g, GUESTFS_PROC_%s, NULL, NULL);\n"
(String.uppercase shortname)
| args ->
List.iter (
@@ -2267,7 +2257,7 @@ check_state (guestfs_h *g, const char *caller)
pr " args.%s = %s;\n" n n
| FileIn _ | FileOut _ -> ()
) args;
pr " serial = guestfs__send (g, GUESTFS_PROC_%s,\n"
pr " serial = guestfs__send_sync (g, GUESTFS_PROC_%s,\n"
(String.uppercase shortname);
pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n"
name;
@@ -2276,22 +2266,11 @@ check_state (guestfs_h *g, const char *caller)
pr " return %s;\n" error_code;
pr "\n";
(* Send the request. *)
pr " ctx.cb_sequence = 0;\n";
pr " guestfs_set_send_callback (g, %s_send_cb, &ctx);\n" shortname;
pr " (void) ml->main_loop_run (ml, g);\n";
pr " guestfs_set_send_callback (g, NULL, NULL);\n";
pr " if (ctx.cb_sequence != 1) {\n";
pr " error (g, \"%%s send failed, see earlier error messages\", \"%s\");\n" name;
pr " return %s;\n" error_code;
pr " }\n";
pr "\n";
(* Send any additional files (FileIn) requested. *)
List.iter (
function
| FileIn n ->
pr " if (guestfs__send_file_sync (ml, g, %s) == -1)\n" n;
pr " if (guestfs__send_file_sync (g, %s) == -1)\n" n;
pr " return %s;\n" error_code;
pr "\n";
| _ -> ()
@@ -2324,7 +2303,7 @@ check_state (guestfs_h *g, const char *caller)
List.iter (
function
| FileOut n ->
pr " if (guestfs__receive_file_sync (ml, g, %s) == -1)\n" n;
pr " if (guestfs__receive_file_sync (g, %s) == -1)\n" n;
pr " return %s;\n" error_code;
pr "\n";
| _ -> ()

File diff suppressed because it is too large Load Diff

View File

@@ -416,6 +416,24 @@ guestfs_safe_memdup (guestfs_h *g, void *ptr, size_t size)
return p;
}
static int
xwrite (int fd, const void *buf, size_t len)
{
int r;
while (len > 0) {
r = write (fd, buf, len);
if (r == -1) {
perror ("write");
return -1;
}
buf += r;
len -= r;
}
return 0;
}
void
guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_cb cb)
{
@@ -1305,19 +1323,80 @@ 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.
/* Change the daemon socket handler so that we are now writing.
* This sets the handle to sock_write_event.
*/
int
guestfs__send (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args)
guestfs__switch_to_sending (guestfs_h *g)
{
if (g->sock_watch >= 0) {
if (g->main_loop->remove_handle (g->main_loop, g, g->sock_watch) == -1) {
error (g, "remove_handle failed");
g->sock_watch = -1;
return -1;
}
}
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");
return -1;
}
return 0;
}
int
guestfs__switch_to_receiving (guestfs_h *g)
{
if (g->sock_watch >= 0) {
if (g->main_loop->remove_handle (g->main_loop, g, g->sock_watch) == -1) {
error (g, "remove_handle failed");
g->sock_watch = -1;
return -1;
}
}
g->sock_watch =
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");
return -1;
}
return 0;
}
/* Dispatch a call (len + header + args) to the remote daemon,
* synchronously (ie. using the guest's main loop to wait until
* it has been sent). Returns -1 for error, or the serial
* number of the message.
*/
static void
send_cb (guestfs_h *g, void *data)
{
guestfs_main_loop *ml = guestfs_get_main_loop (g);
*((int *)data) = 1;
ml->main_loop_quit (ml, g);
}
int
guestfs__send_sync (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++;
int sent;
guestfs_main_loop *ml = guestfs_get_main_loop (g);
if (g->state != READY) {
error (g, "dispatch: state %d != READY", g->state);
@@ -1369,6 +1448,15 @@ guestfs__send (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args)
if (guestfs__switch_to_sending (g) == -1)
goto cleanup1;
sent = 0;
guestfs_set_send_callback (g, send_cb, &sent);
if (ml->main_loop_run (ml, g) == -1)
goto cleanup1;
if (sent != 1) {
error (g, "send failed, see earlier error messages");
goto cleanup1;
}
return serial;
cleanup1:
@@ -1379,74 +1467,15 @@ guestfs__send (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args)
return -1;
}
/* Change the daemon socket handler so that we are now writing.
* This sets the handle to sock_write_event.
*/
int
guestfs__switch_to_sending (guestfs_h *g)
{
if (g->sock_watch >= 0) {
if (g->main_loop->remove_handle (g->main_loop, g, g->sock_watch) == -1) {
error (g, "remove_handle failed");
g->sock_watch = -1;
return -1;
}
}
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");
return -1;
}
return 0;
}
int
guestfs__switch_to_receiving (guestfs_h *g)
{
if (g->sock_watch >= 0) {
if (g->main_loop->remove_handle (g->main_loop, g, g->sock_watch) == -1) {
error (g, "remove_handle failed");
g->sock_watch = -1;
return -1;
}
}
g->sock_watch =
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");
return -1;
}
return 0;
}
int
guestfs__send_file_sync (guestfs_main_loop *ml, guestfs_h *g,
const char *filename)
{
return -1;
}
int
guestfs__receive_file_sync (guestfs_main_loop *ml, guestfs_h *g,
const char *filename)
{
return -1;
}
#if 0
static int cancel = 0; /* XXX Implement file cancellation. */
static int send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t len);
static int send_file_data_sync (guestfs_h *g, const char *buf, size_t len);
static int send_file_cancellation_sync (guestfs_h *g);
static int send_file_complete_sync (guestfs_h *g);
/* Synchronously send a file. */
int
guestfs__send_file (guestfs_h *g, const char *filename)
guestfs__send_file_sync (guestfs_h *g, const char *filename)
{
char buf[GUESTFS_MAX_CHUNK_SIZE];
int fd, r;
@@ -1454,7 +1483,7 @@ guestfs__send_file (guestfs_h *g, const char *filename)
fd = open (filename, O_RDONLY);
if (fd == -1) {
perrorf (g, "open: %s", filename);
send_file_cancellation (g);
send_file_cancellation_sync (g);
/* Daemon sees cancellation and won't reply, so caller can
* just return here.
*/
@@ -1463,18 +1492,18 @@ guestfs__send_file (guestfs_h *g, const char *filename)
/* Send file in chunked encoding. */
while (!cancel && (r = read (fd, buf, sizeof buf)) > 0) {
if (send_file_data (g, buf, r) == -1)
if (send_file_data_sync (g, buf, r) == -1)
return -1;
}
if (cancel) {
send_file_cancellation (g);
send_file_cancellation_sync (g);
return -1;
}
if (r == -1) {
perrorf (g, "read: %s", filename);
send_file_cancellation (g);
send_file_cancellation_sync (g);
return -1;
}
@@ -1483,20 +1512,48 @@ guestfs__send_file (guestfs_h *g, const char *filename)
*/
if (close (fd) == -1) {
perrorf (g, "close: %s", filename);
send_file_cancellation (g);
send_file_cancellation_sync (g);
return -1;
}
return send_file_complete (g);
return send_file_complete_sync (g);
}
/* Send a chunk, cancellation or end of file, wait for it to go. */
/* Send a chunk of file data. */
static int
send_file_chunk (guestfs_h *g, int cancel, const char *buf, size_t len)
send_file_data_sync (guestfs_h *g, const char *buf, size_t len)
{
return send_file_chunk_sync (g, 0, buf, len);
}
/* Send a cancellation message. */
static int
send_file_cancellation_sync (guestfs_h *g)
{
char buf[1];
return send_file_chunk_sync (g, 1, buf, 0);
}
/* Send a file complete chunk. */
static int
send_file_complete_sync (guestfs_h *g)
{
char buf[1];
return send_file_chunk_sync (g, 0, buf, 0);
}
/* Send a chunk, cancellation or end of file, synchronously (ie. wait
* for it to go).
*/
static int
send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t len)
{
void *data;
unsigned datalen;
int sent;
guestfs_chunk chunk;
XDR xdr;
guestfs_main_loop *ml = guestfs_get_main_loop (g);
if (g->state != BUSY) {
error (g, "send_file_chunk: state %d != READY", g->state);
@@ -1516,20 +1573,26 @@ send_file_chunk (guestfs_h *g, int cancel, const char *buf, size_t len)
return -1;
}
chunkdatalen = xdr_getpos (&xdr);
datalen = xdr_getpos (&xdr);
xdr_destroy (&xdr);
len = xdr_getpos (&xdr);
xdr_destroy (&xdr);
data = safe_realloc (g, data, len);
data = safe_realloc (g, data, datalen);
g->msg_out = data;
g->msg_out_size = len;
g->msg_out_size = datalen;
g->msg_out_pos = 0;
if (guestfs__switch_to_sending (g) == -1)
goto cleanup1;
sent = 0;
guestfs_set_send_callback (g, send_cb, &sent);
if (ml->main_loop_run (ml, g) == -1)
goto cleanup1;
if (sent != 1) {
error (g, "send file chunk failed, see earlier error messages");
goto cleanup1;
}
return 0;
cleanup1:
@@ -1540,29 +1603,104 @@ send_file_chunk (guestfs_h *g, int cancel, const char *buf, size_t len)
return -1;
}
/* Send a chunk of file data. */
static int
send_file_data (guestfs_h *g, const char *buf, size_t len)
/* Synchronously receive a file.
* XXX No way to cancel file receives. We would need to send an
* error to the daemon and have it see this and stop sending.
*/
static int receive_file_data_sync (guestfs_h *g, void **buf);
int
guestfs__receive_file_sync (guestfs_h *g, const char *filename)
{
return send_file_chunk (g, 0, buf, len);
void *buf;
int fd, r;
fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY);
if (fd == -1) {
perrorf (g, "open: %s", filename);
return -1;
}
/* Receive the file in chunked encoding. */
while ((r = receive_file_data_sync (g, &buf)) > 0) {
if (xwrite (fd, buf, r) == -1) {
free (buf);
return -1;
}
free (buf);
}
if (r == -1) {
error (g, "%s: error in chunked encoding", filename);
return -1;
}
if (close (fd) == -1) {
perrorf (g, "close: %s", filename);
return -1;
}
return 0;
}
/* Send a cancellation message. */
static int
send_file_cancellation (guestfs_h *g)
struct receive_file_ctx {
int code;
void **buf;
};
static void
receive_file_cb (guestfs_h *g, void *data, XDR *xdr)
{
char buf[1];
return send_file_chunk (g, 1, buf, 0);
guestfs_main_loop *ml = guestfs_get_main_loop (g);
struct receive_file_ctx *ctx = (struct receive_file_ctx *) data;
guestfs_chunk chunk;
ml->main_loop_quit (ml, g);
if (!xdr_guestfs_chunk (xdr, &chunk)) {
error (g, "failed to parse file chunk");
ctx->code = -1;
return;
}
if (chunk.cancel) {
error (g, "file receive cancelled by daemon");
ctx->code = -2;
return;
}
if (chunk.data.data_len == 0) { /* end of transfer */
ctx->code = 0;
return;
}
ctx->code = chunk.data.data_len;
*ctx->buf = chunk.data.data_val; /* caller frees */
}
/* Send a file complete chunk. */
/* Receive a chunk of file data. */
static int
send_file_complete (guestfs_h *g)
receive_file_data_sync (guestfs_h *g, void **buf)
{
char buf[0];
return send_file_chunk (g, 0, buf, 0);
struct receive_file_ctx ctx;
guestfs_main_loop *ml = guestfs_get_main_loop (g);
ctx.code = -3;
ctx.buf = buf;
guestfs_set_reply_callback (g, receive_file_cb, &ctx);
(void) ml->main_loop_run (ml, g);
guestfs_set_reply_callback (g, NULL, NULL);
switch (ctx.code) {
case 0: /* end of file */
return 0;
case -1: case -2:
return -1;
case -3:
error (g, "failed to call receive_file_cb");
return -1;
default: /* received n bytes of data */
return ctx.code;
}
}
#endif
/* This is the default main loop implementation, using select(2). */

View File

@@ -74,11 +74,15 @@ extern void *guestfs_safe_realloc (guestfs_h *g, void *ptr, int nbytes);
extern char *guestfs_safe_strdup (guestfs_h *g, const char *str);
extern void *guestfs_safe_memdup (guestfs_h *g, void *ptr, size_t size);
extern int guestfs__send (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args);
extern int guestfs__switch_to_sending (guestfs_h *g);
extern int guestfs__switch_to_receiving (guestfs_h *g);
extern int guestfs__send_file_sync (guestfs_main_loop *ml, guestfs_h *g, const char *filename);
extern int guestfs__receive_file_sync (guestfs_main_loop *ml, guestfs_h *g, const char *filename);
/* These *_sync calls wait until the action is performed, using the
* main loop. We should implement asynchronous versions too.
*/
extern int guestfs__send_sync (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args);
extern int guestfs__send_file_sync (guestfs_h *g, const char *filename);
extern int guestfs__receive_file_sync (guestfs_h *g, const char *filename);
/* Main loop. */
#define GUESTFS_HANDLE_READABLE 0x1