X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fgenerator.ml;h=34b22410ba36f2848a40b82f94e37a977877f759;hp=77bcc47e33245825cbf5119a734090f86baa97c8;hb=170f262f0413de843af62b968f6d12c1c476ae7f;hpb=1765330e07a48dc6f7bdef7007f69ebe606fa731 diff --git a/src/generator.ml b/src/generator.ml index 77bcc47..34b2241 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -334,7 +334,71 @@ C is defined and set to C<1>."); [], "get verbose mode", "\ -This returns the verbose messages flag.") +This returns the verbose messages flag."); + + ("is_ready", (RBool "ready", []), -1, [], + [], + "is ready to accept commands", + "\ +This returns true iff this handle is ready to accept commands +(in the C state). + +For more information on states, see L."); + + ("is_config", (RBool "config", []), -1, [], + [], + "is in configuration state", + "\ +This returns true iff this handle is being configured +(in the C state). + +For more information on states, see L."); + + ("is_launching", (RBool "launching", []), -1, [], + [], + "is launching subprocess", + "\ +This returns true iff this handle is launching the subprocess +(in the C state). + +For more information on states, see L."); + + ("is_busy", (RBool "busy", []), -1, [], + [], + "is busy processing a command", + "\ +This returns true iff this handle is busy processing a command +(in the C state). + +For more information on states, see L."); + + ("get_state", (RInt "state", []), -1, [], + [], + "get the current state", + "\ +This returns the current state as an opaque integer. This is +only useful for printing debug and internal error messages. + +For more information on states, see L."); + + ("set_busy", (RErr, []), -1, [NotInFish], + [], + "set state to busy", + "\ +This sets the state to C. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + + ("set_ready", (RErr, []), -1, [NotInFish], + [], + "set state to ready", + "\ +This sets the state to C. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + ] let daemon_functions = [ @@ -1208,7 +1272,6 @@ Reread the partition table on C. This uses the L command."); -(* ("upload", (RErr, [FileIn "filename"; String "remotefilename"]), 66, [], [], "upload a file from the local machine", @@ -1218,7 +1281,7 @@ filesystem. C can also be a named pipe. -See also C."); +See also C."); ("download", (RErr, [String "remotefilename"; FileOut "filename"]), 67, [], [], @@ -1229,8 +1292,7 @@ on the local machine. C can also be a named pipe. -See also C, C."); -*) +See also C, C."); ] @@ -1881,9 +1943,17 @@ and generate_xdr () = (* Message header, etc. *) pr "\ +/* The communication protocol is now documented in the guestfs(3) + * manpage. + */ + const GUESTFS_PROGRAM = 0x2000F5F5; const GUESTFS_PROTOCOL_VERSION = 1; +/* These constants must be larger than any possible message length. */ +const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5; +const GUESTFS_CANCEL_FLAG = 0xffffeeee; + enum guestfs_message_direction { GUESTFS_DIRECTION_CALL = 0, /* client -> daemon */ GUESTFS_DIRECTION_REPLY = 1 /* daemon -> client */ @@ -1897,22 +1967,9 @@ enum guestfs_message_status { const GUESTFS_ERROR_LEN = 256; struct guestfs_message_error { - string error; /* error message */ + string error_message; }; -/* For normal requests and replies (not involving any FileIn or - * FileOut parameters), the protocol is: - * - * For requests: - * total length (header + args, but not including length word itself) - * header - * guestfs_foo_args struct - * For replies: - * total length (as above) - * header - * guestfs_foo_ret struct - */ - struct guestfs_message_header { unsigned prog; /* GUESTFS_PROGRAM */ unsigned vers; /* GUESTFS_PROTOCOL_VERSION */ @@ -1922,23 +1979,6 @@ struct guestfs_message_header { guestfs_message_status status; }; -/* Chunked encoding used to transfer files, for FileIn and FileOut - * parameters. - * - * For requests which have >= 1 FileIn parameter: - * length of header + args (but not length word itself, and not chunks) - * header - * guestfs_foo_args struct - * sequence of chunks for FileIn param #0 - * sequence of chunks for FileIn param #1 etc - * - * For replies which have >= 1 FileOut parameter: - * length of header + ret (but not length word itself, and not chunks) - * header - * guestfs_foo_ret struct - * sequence of chunks for FileOut param #0 - * sequence of chunks for FileOut param #1 etc - */ const GUESTFS_MAX_CHUNK_SIZE = 8192; struct guestfs_chunk { @@ -2022,19 +2062,88 @@ and generate_actions_h () = and generate_client_actions () = generate_header CStyle LGPLv2; + pr "\ +#include +#include + +#include \"guestfs.h\" +#include \"guestfs_protocol.h\" + +#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 + +/* 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; +} + +/* Check we are in the right state to run a high-level action. */ +static int +check_state (guestfs_h *g, const char *caller) +{ + if (!guestfs_is_ready (g)) { + if (guestfs_is_config (g)) + error (g, \"%%s: call launch() before using this function\", + caller); + else if (guestfs_is_launching (g)) + error (g, \"%%s: call wait_ready() before using this function\", + caller); + else + error (g, \"%%s called from the wrong state, %%d != READY\", + caller, guestfs_get_state (g)); + return -1; + } + return 0; +} + +"; + (* Client-side stubs for each function. *) List.iter ( fun (shortname, style, _, _, _, _, _) -> let name = "guestfs_" ^ shortname in - (* Generate the state struct which stores the high-level - * state between callback functions. The callback(s) are: - * _cb_header_sent header was sent - * _cb_file_sent FileIn file was sent - * _cb_reply_received reply received + (* Generate the context struct which stores the high-level + * state between callback functions. *) - pr "struct %s_state {\n" shortname; - pr " int cb_done;\n"; + pr "struct %s_ctx {\n" shortname; + pr " /* This flag is set by the callbacks, so we know we've done\n"; + pr " * the callbacks as expected, and in the right sequence.\n"; + pr " * 0 = not called, 1 = send called,\n"; + pr " * 1001 = reply called.\n"; + pr " */\n"; + pr " int cb_sequence;\n"; pr " struct guestfs_message_header hdr;\n"; pr " struct guestfs_message_error err;\n"; (match fst style with @@ -2052,18 +2161,22 @@ and generate_client_actions () = pr "};\n"; pr "\n"; - (* Generate the callback function. *) - pr "static void %s_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname; + (* Generate the reply callback function. *) + pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname; pr "{\n"; - pr " struct %s_state *state = (struct %s_state *) data;\n" shortname shortname; + 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 " if (!xdr_guestfs_message_header (xdr, &state->hdr)) {\n"; - pr " error (g, \"%s: failed to parse reply header\");\n" name; + 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"; pr " }\n"; - pr " if (state->hdr.status == GUESTFS_STATUS_ERROR) {\n"; - pr " if (!xdr_guestfs_message_error (xdr, &state->err)) {\n"; - pr " error (g, \"%s: failed to parse reply error\");\n" name; + pr " if (ctx->hdr.status == GUESTFS_STATUS_ERROR) {\n"; + pr " if (!xdr_guestfs_message_error (xdr, &ctx->err)) {\n"; + pr " error (g, \"%%s: failed to parse reply error\", \"%s\");\n" + name; pr " return;\n"; pr " }\n"; pr " goto done;\n"; @@ -2079,15 +2192,14 @@ and generate_client_actions () = | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _ | RHashtable _ -> - pr " if (!xdr_%s_ret (xdr, &state->ret)) {\n" name; - pr " error (g, \"%s: failed to parse reply\");\n" name; + pr " if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name; + pr " error (g, \"%%s: failed to parse reply\", \"%s\");\n" name; pr " return;\n"; pr " }\n"; ); pr " done:\n"; - pr " state->cb_done = 1;\n"; - pr " g->main_loop->main_loop_quit (g->main_loop, g);\n"; + pr " ctx->cb_sequence = 1001;\n"; pr "}\n\n"; (* Generate the action stub. *) @@ -2112,29 +2224,20 @@ and generate_client_actions () = | _ -> pr " struct %s_args args;\n" name ); - pr " struct %s_state state;\n" shortname; + pr " struct %s_ctx ctx;\n" shortname; + pr " guestfs_main_loop *ml = guestfs_get_main_loop (g);\n"; pr " int serial;\n"; pr "\n"; - pr " if (g->state != READY) {\n"; - pr " if (g->state == CONFIG)\n"; - pr " error (g, \"%%s: call launch() before using this function\",\n"; - pr " \"%s\");\n" name; - pr " else if (g->state == LAUNCHING)\n"; - pr " error (g, \"%%s: call wait_ready() before using this function\",\n"; - pr " \"%s\");\n" name; - pr " else\n"; - pr " error (g, \"%%s called from the wrong state, %%d != READY\",\n"; - pr " \"%s\", g->state);\n" name; - pr " return %s;\n" error_code; - pr " }\n"; + pr " if (check_state (g, \"%s\") == -1) return %s;\n" name error_code; + pr " guestfs_set_busy (g);\n"; pr "\n"; - pr " memset (&state, 0, sizeof state);\n"; + 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 = dispatch (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 ( @@ -2152,45 +2255,60 @@ and generate_client_actions () = pr " args.%s = %s;\n" n n | FileIn _ | FileOut _ -> () ) args; - pr " serial = dispatch (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" + pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n" name; ); - pr " if (serial == -1)\n"; + pr " if (serial == -1) {\n"; + pr " guestfs_set_ready (g);\n"; pr " return %s;\n" error_code; + pr " }\n"; pr "\n"; - (* Send any additional files requested. *) + (* Send any additional files (FileIn) requested. *) List.iter ( function | FileIn n -> - pr " if (send_file (g, %s) == -1)\n" n; - pr " return %s;\n" error_code; + pr " {\n"; + pr " int r;\n"; + pr "\n"; + pr " r = guestfs__send_file_sync (g, %s);\n" n; + pr " if (r == -1) {\n"; + pr " guestfs_set_ready (g);\n"; + pr " return %s;\n" error_code; + pr " }\n"; + pr " if (r == -2) /* daemon cancelled */\n"; + pr " goto read_reply;\n"; + pr " }\n"; pr "\n"; | _ -> () ) (snd style); (* Wait for the reply from the remote end. *) - pr " state.cb_done = 0;\n"; - pr " g->reply_cb_internal = %s_cb;\n" shortname; - pr " g->reply_cb_internal_data = &state;\n"; - pr " (void) g->main_loop->main_loop_run (g->main_loop, g);\n"; - pr " g->reply_cb_internal = NULL;\n"; - pr " g->reply_cb_internal_data = NULL;\n"; - pr " if (!state.cb_done) {\n"; - pr " error (g, \"%s failed, see earlier error messages\");\n" name; + pr " read_reply:\n"; + pr " guestfs__switch_to_receiving (g);\n"; + pr " ctx.cb_sequence = 0;\n"; + pr " guestfs_set_reply_callback (g, %s_reply_cb, &ctx);\n" shortname; + pr " (void) ml->main_loop_run (ml, g);\n"; + pr " guestfs_set_reply_callback (g, NULL, NULL);\n"; + pr " if (ctx.cb_sequence != 1001) {\n"; + pr " error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name; + pr " guestfs_set_ready (g);\n"; pr " return %s;\n" error_code; pr " }\n"; pr "\n"; - pr " if (check_reply_header (g, &state.hdr, GUESTFS_PROC_%s, serial) == -1)\n" + pr " if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1) {\n" (String.uppercase shortname); + pr " guestfs_set_ready (g);\n"; pr " return %s;\n" error_code; + pr " }\n"; pr "\n"; - pr " if (state.hdr.status == GUESTFS_STATUS_ERROR) {\n"; - pr " error (g, \"%%s\", state.err.error);\n"; + pr " if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {\n"; + pr " error (g, \"%%s\", ctx.err.error_message);\n"; + pr " guestfs_set_ready (g);\n"; pr " return %s;\n" error_code; pr " }\n"; pr "\n"; @@ -2199,35 +2317,39 @@ and generate_client_actions () = List.iter ( function | FileOut n -> - pr " if (receive_file (g, %s) == -1)\n" n; + pr " if (guestfs__receive_file_sync (g, %s) == -1) {\n" n; + pr " guestfs_set_ready (g);\n"; pr " return %s;\n" error_code; + pr " }\n"; pr "\n"; | _ -> () ) (snd style); + pr " guestfs_set_ready (g);\n"; + (match fst style with | RErr -> pr " return 0;\n" | RInt n | RInt64 n | RBool n -> - pr " return state.ret.%s;\n" n + pr " return ctx.ret.%s;\n" n | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" | RString n -> - pr " return state.ret.%s; /* caller will free */\n" n + pr " return ctx.ret.%s; /* caller will free */\n" n | RStringList n | RHashtable n -> pr " /* caller will free this, but we need to add a NULL entry */\n"; - pr " state.ret.%s.%s_val =" n n; - pr " safe_realloc (g, state.ret.%s.%s_val,\n" n n; - pr " sizeof (char *) * (state.ret.%s.%s_len + 1));\n" + pr " ctx.ret.%s.%s_val =\n" n n; + pr " safe_realloc (g, ctx.ret.%s.%s_val,\n" n n; + pr " sizeof (char *) * (ctx.ret.%s.%s_len + 1));\n" n n; - pr " state.ret.%s.%s_val[state.ret.%s.%s_len] = NULL;\n" n n n n; - pr " return state.ret.%s.%s_val;\n" n n + pr " ctx.ret.%s.%s_val[ctx.ret.%s.%s_len] = NULL;\n" n n n n; + pr " return ctx.ret.%s.%s_val;\n" n n | RIntBool _ -> pr " /* caller with free this */\n"; - pr " return safe_memdup (g, &state.ret, sizeof (state.ret));\n" + pr " return safe_memdup (g, &ctx.ret, sizeof (ctx.ret));\n" | RPVList n | RVGList n | RLVList n | RStat n | RStatVFS n -> pr " /* caller will free this */\n"; - pr " return safe_memdup (g, &state.ret.%s, sizeof (state.ret.%s));\n" n n + pr " return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n ); pr "}\n\n"