reply_with_error ("unexpected message status (%d)", hdr.status);
goto cont;
}
- /* This version of the daemon does not understand optional arguments
- * at all. When we fix this, we will remove the next conditional.
- */
- if (hdr.optargs_bitmask != 0) {
- reply_with_error ("optargs_bitmask != 0 (%" PRIu64 ")",
- hdr.optargs_bitmask);
- goto cont;
- }
proc_nr = hdr.proc;
serial = hdr.serial;
(* Client-side stubs for each function. *)
List.iter (
fun (shortname, (ret, args, optargs as style), _, _, _, _, _) ->
- if optargs <> [] then
- failwithf "optargs not yet implemented for daemon functions";
-
let name = "guestfs_" ^ shortname in
let error_code = error_code_of ret in
(* Generate the action stub. *)
if optargs = [] then
generate_prototype ~extern:false ~semicolon:false ~newline:true
- ~handle:"g" name style
+ ~handle:"g" ~prefix:"guestfs_" shortname style
else
generate_prototype ~extern:false ~semicolon:false ~newline:true
- ~handle:"g" ~suffix:"_argv" ~optarg_proto:Argv name style;
+ ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv"
+ ~optarg_proto:Argv shortname style;
pr "{\n";
pr "\n";
(* Send the main header and arguments. *)
- (match args with
- | [] ->
- pr " serial = guestfs___send (g, GUESTFS_PROC_%s, progress_hint,\n"
- (String.uppercase shortname);
- pr " NULL, NULL);\n"
- | args ->
- List.iter (
- function
- | Pathname n | Device n | Dev_or_Path n | String n | Key n ->
- pr " args.%s = (char *) %s;\n" n n
- | OptString n ->
- pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n
- | StringList n | DeviceList n ->
- pr " args.%s.%s_val = (char **) %s;\n" n n n;
- pr " for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
- | Bool n ->
- pr " args.%s = %s;\n" n n
- | Int n ->
- pr " args.%s = %s;\n" n n
+ if args = [] && optargs = [] then (
+ pr " serial = guestfs___send (g, GUESTFS_PROC_%s, progress_hint, 0,\n"
+ (String.uppercase shortname);
+ pr " NULL, NULL);\n"
+ ) else (
+ List.iter (
+ function
+ | Pathname n | Device n | Dev_or_Path n | String n | Key n ->
+ pr " args.%s = (char *) %s;\n" n n
+ | OptString n ->
+ pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n
+ | StringList n | DeviceList n ->
+ pr " args.%s.%s_val = (char **) %s;\n" n n n;
+ pr " for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
+ | Bool n ->
+ pr " args.%s = %s;\n" n n
+ | Int n ->
+ pr " args.%s = %s;\n" n n
+ | Int64 n ->
+ pr " args.%s = %s;\n" n n
+ | FileIn _ | FileOut _ -> ()
+ | BufferIn n ->
+ pr " /* Just catch grossly large sizes. XDR encoding will make this precise. */\n";
+ pr " if (%s_size >= GUESTFS_MESSAGE_MAX) {\n" n;
+ trace_return_error ~indent:4 style;
+ pr " error (g, \"%%s: size of input buffer too large\", \"%s\");\n"
+ shortname;
+ pr " guestfs___end_busy (g);\n";
+ pr " return %s;\n" error_code;
+ pr " }\n";
+ pr " args.%s.%s_val = (char *) %s;\n" n n n;
+ pr " args.%s.%s_len = %s_size;\n" n n n
+ | Pointer _ -> assert false
+ ) args;
+
+ List.iter (
+ fun argt ->
+ let n = name_of_argt argt in
+ let uc_shortname = String.uppercase shortname in
+ let uc_n = String.uppercase n in
+ pr " if ((optargs->bitmask & GUESTFS_%s_%s_BITMASK))\n"
+ uc_shortname uc_n;
+ (match argt with
+ | Bool n
+ | Int n
| Int64 n ->
- pr " args.%s = %s;\n" n n
- | FileIn _ | FileOut _ -> ()
- | BufferIn n ->
- pr " /* Just catch grossly large sizes. XDR encoding will make this precise. */\n";
- pr " if (%s_size >= GUESTFS_MESSAGE_MAX) {\n" n;
- trace_return_error ~indent:4 style;
- pr " error (g, \"%%s: size of input buffer too large\", \"%s\");\n"
- shortname;
- pr " guestfs___end_busy (g);\n";
- pr " return %s;\n" error_code;
- pr " }\n";
- pr " args.%s.%s_val = (char *) %s;\n" n n n;
- pr " args.%s.%s_len = %s_size;\n" n n n
- | Pointer _ -> assert false
- ) args;
- pr " serial = guestfs___send (g, GUESTFS_PROC_%s, progress_hint,\n"
- (String.uppercase shortname);
- pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n"
- name;
+ pr " args.%s = optargs->%s;\n" n n;
+ pr " else\n";
+ pr " args.%s = 0;\n" n
+ | String n ->
+ pr " args.%s = (char *) %s;\n" n n;
+ pr " else\n";
+ pr " args.%s = (char *) \"\";\n" n
+ | _ -> assert false
+ )
+ ) optargs;
+
+ pr " serial = guestfs___send (g, GUESTFS_PROC_%s,\n"
+ (String.uppercase shortname);
+ pr " progress_hint, %s,\n"
+ (if optargs <> [] then "optargs->bitmask" else "0");
+ pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n"
+ name;
);
pr " if (serial == -1) {\n";
pr " guestfs___end_busy (g);\n";
| [] -> assert false
| name :: args ->
(* Look up the command to find out what args/ret it has. *)
- let style =
+ let style_ret, style_args, style_optargs =
try
let _, style, _, _, _, _, _ =
List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
with Not_found ->
failwithf "%s: in test, command %s was not found" test_name name in
- (* If the call has optional args, fold them all together. We cannot
- * test partial optional args yet.
- *)
- let style =
- let ret, args, optargs = style in
- ret, args@optargs in
-
- if List.length (snd style) <> List.length args then
- failwithf "%s: in test, wrong number of args given to %s"
- test_name name;
+ (* Match up the arguments strings and argument types. *)
+ let args, optargs =
+ let rec loop argts args =
+ match argts, args with
+ | (t::ts), (s::ss) ->
+ let args, rest = loop ts ss in
+ ((t, s) :: args), rest
+ | [], ss -> [], ss
+ | ts, [] ->
+ failwithf "%s: in test, too few args given to function %s"
+ test_name name
+ in
+ let args, optargs = loop style_args args in
+ let optargs, rest = loop style_optargs optargs in
+ if rest <> [] then
+ failwithf "%s: in test, too many args given to function %s"
+ test_name name;
+ args, optargs in
pr " {\n";
| Pointer _, _ ->
(* Difficult to make these pointers in order to run a test. *)
assert false
- ) (List.combine (snd style) args);
+ ) args;
+
+ (* Currently can only deal with a complete, in-order list of optargs. *)
+ if optargs <> [] then (
+ pr " struct guestfs_%s_argv optargs;\n" name;
+ let len = List.length style_optargs in
+ let bitmask = Int64.pred (Int64.shift_left 1L len) in
+ pr " optargs.bitmask = UINT64_C(0x%Lx);\n" bitmask;
+ List.iter (
+ function
+ | Bool n, arg
+ | Int n, arg
+ | Int64 n, arg ->
+ pr " optargs.%s = %s;\n" n arg
+ | String n, arg ->
+ pr " optargs.%s = \"%s\";\n" n (c_quote arg);
+ | _ -> assert false
+ ) optargs;
+ );
let error_code =
- match fst style with
+ match style_ret with
| RErr | RInt _ | RBool _ -> pr " int r;\n"; "-1"
| RInt64 _ -> pr " int64_t r;\n"; "-1"
| RConstString _ | RConstOptString _ ->
"NULL" in
pr " suppress_error = %d;\n" (if expect_error then 1 else 0);
- pr " r = guestfs_%s (g" name;
+ if optargs = [] then
+ pr " r = guestfs_%s (g" name
+ else
+ pr " r = guestfs_%s_argv (g" name;
(* Generate the parameters. *)
List.iter (
| Bool _, arg ->
let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
| Pointer _, _ -> assert false
- ) (List.combine (snd style) args);
+ ) args;
- (match fst style with
+ (match style_ret with
| RBufferOut _ -> pr ", &size"
| _ -> ()
);
+ if optargs <> [] then
+ pr ", &optargs";
+
pr ");\n";
if not expect_error then
| Some f -> f ()
);
- (match fst style with
+ (match style_ret with
| RErr | RInt _ | RInt64 _ | RBool _
| RConstString _ | RConstOptString _ -> ()
| RString _ | RBufferOut _ -> pr " free (r);\n"
pr "\n";
List.iter (
- fun (name, style, _, _, _, _, _) ->
+ function
+ | shortname, (_, _, (_::_ as optargs)), _, _, _, _, _ ->
+ iteri (
+ fun i arg ->
+ let uc_shortname = String.uppercase shortname in
+ let n = name_of_argt arg in
+ let uc_n = String.uppercase n in
+ pr "#define GUESTFS_%s_%s_BITMASK (UINT64_C(1)<<%d)\n"
+ uc_shortname uc_n i
+ ) optargs
+ | _ -> ()
+ ) daemon_functions;
+
+ List.iter (
+ fun (name, (ret, args, optargs), _, _, _, _, _) ->
+ let style = ret, args @ optargs, [] in
generate_prototype
~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
name style;
List.iter (
fun (name, (ret, args, optargs), _, _, _, _, _) ->
- if optargs <> [] then
- failwithf "optional arguments not supported in the daemon yet";
-
(* Generate server-side stubs. *)
pr "static void %s_stub (XDR *xdr_in)\n" name;
pr "{\n";
pr " char *r;\n";
"NULL" in
- (match args with
- | [] -> ()
- | args ->
- pr " struct guestfs_%s_args args;\n" name;
- List.iter (
- function
- | Device n | Dev_or_Path n
- | Pathname n
- | String n
- | Key n -> ()
- | OptString n -> pr " char *%s;\n" n
- | StringList n | DeviceList n -> pr " char **%s;\n" n
- | Bool n -> pr " int %s;\n" n
- | Int n -> pr " int %s;\n" n
- | Int64 n -> pr " int64_t %s;\n" n
- | FileIn _ | FileOut _ -> ()
- | BufferIn n ->
- pr " const char *%s;\n" n;
- pr " size_t %s_size;\n" n
- | Pointer _ -> assert false
- ) args
+ if args <> [] || optargs <> [] then (
+ pr " struct guestfs_%s_args args;\n" name;
+ List.iter (
+ function
+ | Device n | Dev_or_Path n
+ | Pathname n
+ | String n
+ | Key n -> ()
+ | OptString n -> pr " char *%s;\n" n
+ | StringList n | DeviceList n -> pr " char **%s;\n" n
+ | Bool n -> pr " int %s;\n" n
+ | Int n -> pr " int %s;\n" n
+ | Int64 n -> pr " int64_t %s;\n" n
+ | FileIn _ | FileOut _ -> ()
+ | BufferIn n ->
+ pr " const char *%s;\n" n;
+ pr " size_t %s_size;\n" n
+ | Pointer _ -> assert false
+ ) (args @ optargs)
);
pr "\n";
let is_filein =
List.exists (function FileIn _ -> true | _ -> false) args in
- (match args with
- | [] -> ()
- | args ->
- pr " memset (&args, 0, sizeof args);\n";
- pr "\n";
- pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
- if is_filein then
- pr " if (cancel_receive () != -2)\n";
- pr " reply_with_error (\"daemon failed to decode procedure arguments\");\n";
- pr " goto done;\n";
- pr " }\n";
- let pr_args n =
- pr " char *%s = args.%s;\n" n n
- in
- let pr_list_handling_code n =
- pr " %s = realloc (args.%s.%s_val,\n" n n n;
- pr " sizeof (char *) * (args.%s.%s_len+1));\n" n n;
- pr " if (%s == NULL) {\n" n;
- if is_filein then
- pr " if (cancel_receive () != -2)\n";
- pr " reply_with_perror (\"realloc\");\n";
- pr " goto done;\n";
- pr " }\n";
- pr " %s[args.%s.%s_len] = NULL;\n" n n n;
- pr " args.%s.%s_val = %s;\n" n n n;
- in
- List.iter (
- function
- | Pathname n ->
- pr_args n;
- pr " ABS_PATH (%s, %s, goto done);\n"
- n (if is_filein then "cancel_receive ()" else "0");
- | Device n ->
- pr_args n;
- pr " RESOLVE_DEVICE (%s, %s, goto done);\n"
- n (if is_filein then "cancel_receive ()" else "0");
- | Dev_or_Path n ->
- pr_args n;
- pr " REQUIRE_ROOT_OR_RESOLVE_DEVICE (%s, %s, goto done);\n"
- n (if is_filein then "cancel_receive ()" else "0");
- | String n | Key n -> pr_args n
- | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n
- | StringList n ->
- pr_list_handling_code n;
- | DeviceList n ->
- pr_list_handling_code n;
- pr " /* Ensure that each is a device,\n";
- pr " * and perform device name translation.\n";
- pr " */\n";
- pr " {\n";
- pr " size_t i;\n";
- pr " for (i = 0; %s[i] != NULL; ++i)\n" n;
- pr " RESOLVE_DEVICE (%s[i], %s, goto done);\n" n
- (if is_filein then "cancel_receive ()" else "0");
- pr " }\n";
- | Bool n -> pr " %s = args.%s;\n" n n
- | Int n -> pr " %s = args.%s;\n" n n
- | Int64 n -> pr " %s = args.%s;\n" n n
- | FileIn _ | FileOut _ -> ()
- | BufferIn n ->
- pr " %s = args.%s.%s_val;\n" n n n;
- pr " %s_size = args.%s.%s_len;\n" n n n
- | Pointer _ -> assert false
- ) args;
- pr "\n"
+ (* Reject unknown optional arguments. *)
+ if optargs <> [] then (
+ let len = List.length optargs in
+ let mask = Int64.lognot (Int64.pred (Int64.shift_left 1L len)) in
+ pr " if (optargs_bitmask & UINT64_C(0x%Lx)) {\n" mask;
+ if is_filein then
+ pr " if (cancel_receive () != -2)\n";
+ pr " reply_with_error (\"unknown option in optional arguments bitmask (this can happen if a program is compiled against a newer version of libguestfs, then run against an older version of the daemon)\");\n";
+ pr " goto done;\n";
+ pr " }\n";
+ pr "\n"
+ );
+
+ (* Decode arguments. *)
+ if args <> [] || optargs <> [] then (
+ pr " memset (&args, 0, sizeof args);\n";
+ pr "\n";
+ pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
+ if is_filein then
+ pr " if (cancel_receive () != -2)\n";
+ pr " reply_with_error (\"daemon failed to decode procedure arguments\");\n";
+ pr " goto done;\n";
+ pr " }\n";
+ let pr_args n =
+ pr " char *%s = args.%s;\n" n n
+ in
+ let pr_list_handling_code n =
+ pr " %s = realloc (args.%s.%s_val,\n" n n n;
+ pr " sizeof (char *) * (args.%s.%s_len+1));\n" n n;
+ pr " if (%s == NULL) {\n" n;
+ if is_filein then
+ pr " if (cancel_receive () != -2)\n";
+ pr " reply_with_perror (\"realloc\");\n";
+ pr " goto done;\n";
+ pr " }\n";
+ pr " %s[args.%s.%s_len] = NULL;\n" n n n;
+ pr " args.%s.%s_val = %s;\n" n n n;
+ in
+ List.iter (
+ function
+ | Pathname n ->
+ pr_args n;
+ pr " ABS_PATH (%s, %s, goto done);\n"
+ n (if is_filein then "cancel_receive ()" else "0");
+ | Device n ->
+ pr_args n;
+ pr " RESOLVE_DEVICE (%s, %s, goto done);\n"
+ n (if is_filein then "cancel_receive ()" else "0");
+ | Dev_or_Path n ->
+ pr_args n;
+ pr " REQUIRE_ROOT_OR_RESOLVE_DEVICE (%s, %s, goto done);\n"
+ n (if is_filein then "cancel_receive ()" else "0");
+ | String n | Key n -> pr_args n
+ | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n
+ | StringList n ->
+ pr_list_handling_code n;
+ | DeviceList n ->
+ pr_list_handling_code n;
+ pr " /* Ensure that each is a device,\n";
+ pr " * and perform device name translation.\n";
+ pr " */\n";
+ pr " {\n";
+ pr " size_t i;\n";
+ pr " for (i = 0; %s[i] != NULL; ++i)\n" n;
+ pr " RESOLVE_DEVICE (%s[i], %s, goto done);\n" n
+ (if is_filein then "cancel_receive ()" else "0");
+ pr " }\n";
+ | Bool n -> pr " %s = args.%s;\n" n n
+ | Int n -> pr " %s = args.%s;\n" n n
+ | Int64 n -> pr " %s = args.%s;\n" n n
+ | FileIn _ | FileOut _ -> ()
+ | BufferIn n ->
+ pr " %s = args.%s.%s_val;\n" n n n;
+ pr " %s_size = args.%s.%s_len;\n" n n n
+ | Pointer _ -> assert false
+ ) (args @ optargs);
+ pr "\n"
);
(* this is used at least for do_equal *)
(* Don't want to call the impl with any FileIn or FileOut
* parameters, since these go "outside" the RPC protocol.
*)
- let args' =
- List.filter (function FileIn _ | FileOut _ -> false | _ -> true) args in
- pr " r = do_%s " name;
- generate_c_call_args (ret, args', optargs);
- pr ";\n";
+ let () =
+ let args' =
+ List.filter
+ (function FileIn _ | FileOut _ -> false | _ -> true) args in
+ let style = ret, args' @ optargs, [] in
+ pr " r = do_%s " name;
+ generate_c_call_args style;
+ pr ";\n" in
(match ret with
| RErr | RInt _ | RInt64 _ | RBool _
List.iter (
fun (shortname, (ret, args, optargs), _, _, _, _, _) ->
- if optargs <> [] then
- failwithf "optional arguments not supported in XDR yet";
-
let name = "guestfs_" ^ shortname in
- (match args with
+ (* Ordinary arguments and optional arguments are concatenated
+ * together in the XDR args struct. The optargs_bitmask field
+ * in the header controls which optional arguments are
+ * meaningful.
+ *)
+ (match args @ optargs with
| [] -> ()
| args ->
pr "struct %s_args {\n" name;
extern void guestfs___free_inspect_info (guestfs_h *g);
extern int guestfs___set_busy (guestfs_h *g);
extern int guestfs___end_busy (guestfs_h *g);
-extern int guestfs___send (guestfs_h *g, int proc_nr, uint64_t progress_hint, xdrproc_t xdrp, char *args);
+extern int guestfs___send (guestfs_h *g, int proc_nr, uint64_t progress_hint, uint64_t optargs_bitmask, xdrproc_t xdrp, char *args);
extern int guestfs___recv (guestfs_h *g, const char *fn, struct guestfs_message_header *hdr, struct guestfs_message_error *err, xdrproc_t xdrp, char *ret);
extern int guestfs___send_file (guestfs_h *g, const char *filename);
extern int guestfs___recv_file (guestfs_h *g, const char *filename);
}
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"));