Since it's rather tedious to write the dispatchers for functions that
return an array of typed parameters (which are rather common) let's add
some rpcgen code to generate them.
---
src/remote/remote_protocol.x | 5 +++++
src/rpc/gendispatch.pl | 45 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 0170c0c..cec6bd2 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -414,6 +414,11 @@ struct remote_domain_disk_error {
* insert@<offset> comment to indicate the offset in the parameter list of
* the function to be called.
*
+ * For cases where the API allocates memory and fills the arguments (mostly
+ * typed parameters) a similar comment indicates the type and offset
+ * of the variable to be filled with the count of returned elements.
+ * alloc@<offset>@unsigned int@<count offset>
+ *
* Dynamic opaque and remote_nonnull_string arrays can be annotated with an
* optional typecast */
diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl
index 5564b2e..173189c 100755
--- a/src/rpc/gendispatch.pl
+++ b/src/rpc/gendispatch.pl
@@ -862,6 +862,25 @@ elsif ($mode eq "server") {
$single_ret_var = $2;
$single_ret_by_ref = 0;
$single_ret_check = " == NULL";
+ } elsif ($ret_member =~ m/^remote_typed_param
(\S+)<(\S+)>;\s*\/\*\s*alloc@(\d+)@([^@]+)@(\d+)\s*\*\//) {
+ push(@vars_list, "virTypedParameterPtr $1 = NULL");
+ push(@vars_list, "$4 $1_len = 0");
+
+ $single_ret_by_ref = 1;
+ $single_ret_var = undef;
+
+ splice(@args_list, int($3), 0, "&$1");
+ splice(@args_list, int($5), 0, "&$1_len");
+
+ push(@ret_list, "if (virTypedParamsSerialize($1, $1_len,\n"
.
+ "
(virTypedParameterRemotePtr *) &ret->$1.$1_val,\n" .
+ "
&ret->$1.$1_len,\n" .
+ "
VIR_TYPED_PARAM_STRING_OKAY) < 0)\n" .
+ " goto cleanup;\n");
+
+ push(@free_list, " virTypedParamsFree($1, $1_len);");
+ push(@free_list_on_error,
"virTypedParamsRemoteFree((virTypedParameterRemotePtr)
ret->params.params_val,\n" .
+ "
ret->params.params_len);\n");
} elsif ($ret_member =~ m/^(\/)?\*/) {
# ignore comments
} else {
@@ -1422,6 +1441,7 @@ elsif ($mode eq "client") {
my $modern_ret_as_list = 0;
my $modern_ret_struct_name = "undefined";
my $modern_ret_var_type = "undefined";
+ my @custom_error_cleanup = ();
if ($rettype ne "void" and
scalar(@{$call->{ret_members}}) > 1) {
@@ -1519,6 +1539,23 @@ elsif ($mode eq "client") {
$single_ret_var = "vir${type_name}Ptr rv = NULL";
$single_ret_type = "vir${type_name}Ptr";
}
+ } elsif ($ret_member =~ m/^remote_typed_param
(\S+)<(\S+)>;\s*\/\*\s*alloc@(\d+)@([^@]+)@(\d+)\s*\*\//) {
+ # handle self allocating arrays of typed parameters
+ splice(@args_list, int($3), 0, ("virTypedParameterPtr
*$1"));
+ splice(@args_list, int($5), 0, ("$4 *n$1"));
+ push(@vars_list, "virTypedParameterPtr ret_params =
NULL");
+ push(@vars_list, "int ret_nparams = 0");
+ # virTypedParamsDeserialize allocates the array if @params is null
+ push(@ret_list2, "if
(virTypedParamsDeserialize((virTypedParameterRemotePtr) ret.$1.$1_val,\n" .
+ "
ret.$1.$1_len,\n" .
+ " $2,\n"
.
+ "
&ret_params,\n" .
+ "
&ret_nparams) < 0)\n" .
+ " goto cleanup;\n");
+ push(@ret_list2, "*$1 = ret_params;");
+ push(@ret_list2, "*n$1 = ret_nparams;");
+ push(@custom_error_cleanup, "virTypedParamsFree(ret_params,
ret_nparams);\n");
+ $single_ret_cleanup = 1;
} elsif ($ret_member =~ m/^remote_typed_param
(\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
splice(@args_list, int($3), 0, ("virTypedParameterPtr
$1"));
push(@ret_list2, "if
(virTypedParamsDeserialize((virTypedParameterRemotePtr) ret.$1.$1_val,\n" .
@@ -1530,7 +1567,7 @@ elsif ($mode eq "client") {
$single_ret_cleanup = 1;
} elsif ($ret_member =~ m/^remote_typed_param (\S+)<\S+>;/) {
# error out on unannotated arrays
- die "remote_typed_param array without insert@<offset>
annotation: $ret_member";
+ die "remote_typed_param array without insert@... or alloc@...
annotation: $ret_member";
} elsif ($ret_member =~ m/^int (\S+);/) {
my $arg_name = $1;
@@ -1876,6 +1913,12 @@ elsif ($mode eq "client") {
if ($single_ret_as_list or $single_ret_cleanup or $modern_ret_as_list) {
print "\n";
print "cleanup:\n";
+ if (@custom_error_cleanup) {
+ print " if (rv != 0) {\n";
+ print " ";
+ print join("\n ", @custom_error_cleanup);
+ print " }\n";
+ }
if ($modern_ret_as_list) {
print " if (tmp_results) {\n";
print " for (i = 0; i <
ret.$single_ret_list_name.${single_ret_list_name}_len; i++)\n";
--
2.8.3