gendispatch.pl 64.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
#!/usr/bin/perl -w
#
# This script parses remote_protocol.x or qemu_protocol.x and produces lots of
# boilerplate code for both ends of the remote connection.
#
# The first non-option argument specifies the prefix to be searched for, and
# output to, the boilerplate code.  The second non-option argument is the
# file you want to operate on.  For instance, to generate the dispatch table
# for both remote_protocol.x and qemu_protocol.x, you would run the
# following:
#
12
# gendispatch.pl -t remote ../src/remote/remote_protocol.x
13
# gendispatch.pl -t qemu ../src/remote/qemu_protocol.x
14 15
#
# By Richard Jones <rjones@redhat.com>
16
# Extended by Matthias Bolte <matthias.bolte@googlemail.com>
17 18 19 20 21 22

use strict;

use Getopt::Std;

# Command line options.
23 24
our ($opt_p, $opt_t, $opt_a, $opt_r, $opt_d, $opt_b, $opt_k);
getopts ('ptardbk');
25

26 27 28 29
my $structprefix = shift or die "missing prefix argument";
my $protocol = shift or die "missing protocol argument";
my @autogen;

30 31 32 33 34 35 36
my $procprefix = uc $structprefix;

# Convert name_of_call to NameOfCall.
sub name_to_ProcName {
    my $name = shift;
    my @elems = split /_/, $name;
    @elems = map ucfirst, @elems;
37
    @elems = map { $_ =~ s/Nwfilter/NWFilter/; $_ =~ s/Xml/XML/;
38
                   $_ =~ s/Uri/URI/; $_ =~ s/Uuid/UUID/; $_ =~ s/Id/ID/;
39
                   $_ =~ s/Mac/MAC/; $_ =~ s/Cpu/CPU/; $_ =~ s/Os/OS/;
40
                   $_ =~ s/Nmi/NMI/; $_ } @elems;
41 42 43 44 45
    join "", @elems
}

# Read the input file (usually remote_protocol.x) and form an
# opinion about the name, args and return type of each RPC.
46
my ($name, $ProcName, $id, $flags, %calls, @calls);
47

48
my $collect_args_members = 0;
49
my $collect_ret_members = 0;
50 51
my $last_name;

52 53 54
open PROTOCOL, "<$protocol" or die "cannot open $protocol: $!";

while (<PROTOCOL>) {
55 56 57 58 59 60
    if ($collect_args_members) {
        if (/^};/) {
            $collect_args_members = 0;
        } elsif ($_ =~ m/^\s*(.*\S)\s*$/) {
            push(@{$calls{$name}->{args_members}}, $1);
        }
61 62 63 64 65 66
    } elsif ($collect_ret_members) {
        if (/^};/) {
            $collect_ret_members = 0;
        } elsif ($_ =~ m/^\s*(.*\S)\s*$/) {
            push(@{$calls{$name}->{ret_members}}, $1);
        }
67
    } elsif (/^struct ${structprefix}_(.*)_args/) {
68 69 70 71 72 73 74 75 76 77 78
        $name = $1;
        $ProcName = name_to_ProcName ($name);

        die "duplicate definition of ${structprefix}_${name}_args"
            if exists $calls{$name};

        $calls{$name} = {
            name => $name,
            ProcName => $ProcName,
            UC_NAME => uc $name,
            args => "${structprefix}_${name}_args",
79 80
            args_members => [],
            ret => "void"
81 82
        };

83
        $collect_args_members = 1;
84
        $collect_ret_members = 0;
85
        $last_name = $name;
86
    } elsif (/^struct ${structprefix}_(.*)_ret\s+{(.*)$/) {
87
        $name = $1;
88
        $flags = $2;
89 90 91 92 93 94 95 96 97 98
        $ProcName = name_to_ProcName ($name);

        if (exists $calls{$name}) {
            $calls{$name}->{ret} = "${structprefix}_${name}_ret";
        } else {
            $calls{$name} = {
                name => $name,
                ProcName => $ProcName,
                UC_NAME => uc $name,
                args => "void",
99 100
                ret => "${structprefix}_${name}_ret",
                ret_members => []
101 102
            }
        }
103

104 105 106 107 108 109 110 111
        if ($flags ne "" and ($opt_b or $opt_k)) {
            if (!($flags =~ m/^\s*\/\*\s*insert@(\d+)\s*\*\/\s*$/)) {
                die "invalid generator flags for $calls{$name}->{ret}";
            }

            $calls{$name}->{ret_offset} = int($1);
        }

112
        $collect_args_members = 0;
113 114
        $collect_ret_members = 1;
        $last_name = $name;
115 116 117 118 119 120 121 122 123
    } elsif (/^struct ${structprefix}_(.*)_msg/) {
        $name = $1;
        $ProcName = name_to_ProcName ($name);

        $calls{$name} = {
            name => $name,
            ProcName => $ProcName,
            UC_NAME => uc $name,
            msg => "${structprefix}_${name}_msg"
124 125 126
        };

        $collect_args_members = 0;
127
        $collect_ret_members = 0;
128
    } elsif (/^\s*${procprefix}_PROC_(.*?)\s*=\s*(\d+)\s*,?(.*)$/) {
129 130
        $name = lc $1;
        $id = $2;
131
        $flags = $3;
132 133
        $ProcName = name_to_ProcName ($name);

134 135 136 137 138 139 140 141 142 143 144 145 146 147
        if (!exists $calls{$name}) {
            # that the argument and return value cases have not yet added
            # this procedure to the calls hash means that it has no arguments
            # and no return value. add it to the calls hash now because all
            # procedures have to be listed in the calls hash
            $calls{$name} = {
                name => $name,
                ProcName => $ProcName,
                UC_NAME => uc $name,
                args => "void",
                ret => "void"
            }
        }

148
        if ($opt_b or $opt_k) {
149
            if (!($flags =~ m/^\s*\/\*\s*(\S+)\s+(\S+)\s*(\|.*)?\s+(priority:(\S+))?\s*\*\/\s*$/)) {
150 151 152
                die "invalid generator flags for ${procprefix}_PROC_${name}"
            }

153 154
            my $genmode = $opt_b ? $1 : $2;
            my $genflags = $3;
155
            my $priority = defined $5 ? $5 : "low";
156

157
            if ($genmode eq "autogen") {
158
                push(@autogen, $ProcName);
159
            } elsif ($genmode eq "skipgen") {
160 161 162 163
                # ignore it
            } else {
                die "invalid generator flags for ${procprefix}_PROC_${name}"
            }
164 165 166 167 168 169 170 171 172 173 174

            if (defined $genflags and $genflags ne "") {
                if ($genflags =~ m/^\|\s*(read|write)stream@(\d+)\s*$/) {
                    $calls{$name}->{streamflag} = $1;
                    $calls{$name}->{streamoffset} = int($2);
                } else {
                    die "invalid generator flags for ${procprefix}_PROC_${name}"
                }
            } else {
                $calls{$name}->{streamflag} = "none";
            }
175 176 177 178 179 180 181 182 183 184

            # for now, we distinguish only two levels of prioroty:
            # low (0) and high (1)
            if ($priority eq "high") {
                $calls{$name}->{priority} = 1;
            } elsif ($priority eq "low") {
                $calls{$name}->{priority} = 0;
            } else {
                die "invalid priority ${priority} for ${procprefix}_PROC_${name}"
            }
185 186
        }

187
        $calls[$id] = $calls{$name};
188 189

        $collect_args_members = 0;
190
        $collect_ret_members = 0;
191 192
    } else {
        $collect_args_members = 0;
193
        $collect_ret_members = 0;
194 195 196
    }
}

197 198
close(PROTOCOL);

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
# this hash contains the procedures that are allowed to map [unsigned] hyper
# to [unsigned] long for legacy reasons in their signature and return type.
# this list is fixed. new procedures and public APIs have to map [unsigned]
# hyper to [unsigned] long long
my $long_legacy = {
    DomainGetMaxMemory          => { ret => { memory => 1 } },
    DomainGetInfo               => { ret => { maxMem => 1, memory => 1 } },
    DomainMigrate               => { arg => { flags => 1, resource => 1 } },
    DomainMigrate2              => { arg => { flags => 1, resource => 1 } },
    DomainMigrateBegin3         => { arg => { flags => 1, resource => 1 } },
    DomainMigrateConfirm3       => { arg => { flags => 1, resource => 1 } },
    DomainMigrateDirect         => { arg => { flags => 1, resource => 1 } },
    DomainMigrateFinish         => { arg => { flags => 1 } },
    DomainMigrateFinish2        => { arg => { flags => 1 } },
    DomainMigrateFinish3        => { arg => { flags => 1 } },
    DomainMigratePeer2Peer      => { arg => { flags => 1, resource => 1 } },
    DomainMigratePerform        => { arg => { flags => 1, resource => 1 } },
    DomainMigratePerform3       => { arg => { flags => 1, resource => 1 } },
    DomainMigratePrepare        => { arg => { flags => 1, resource => 1 } },
    DomainMigratePrepare2       => { arg => { flags => 1, resource => 1 } },
    DomainMigratePrepare3       => { arg => { flags => 1, resource => 1 } },
    DomainMigratePrepareTunnel  => { arg => { flags => 1, resource => 1 } },
    DomainMigratePrepareTunnel3 => { arg => { flags => 1, resource => 1 } },
    DomainMigrateToURI          => { arg => { flags => 1, resource => 1 } },
    DomainMigrateToURI2         => { arg => { flags => 1, resource => 1 } },
    DomainMigrateVersion1       => { arg => { flags => 1, resource => 1 } },
    DomainMigrateVersion2       => { arg => { flags => 1, resource => 1 } },
    DomainMigrateVersion3       => { arg => { flags => 1, resource => 1 } },
    DomainMigrateSetMaxSpeed    => { arg => { bandwidth => 1 } },
    DomainSetMaxMemory          => { arg => { memory => 1 } },
    DomainSetMemory             => { arg => { memory => 1 } },
    DomainSetMemoryFlags        => { arg => { memory => 1 } },
    GetLibVersion               => { ret => { lib_ver => 1 } },
    GetVersion                  => { ret => { hv_ver => 1 } },
    NodeGetInfo                 => { ret => { memory => 1 } },
234 235
    DomainBlockPull             => { arg => { bandwidth => 1 } },
    DomainBlockJobSetSpeed      => { arg => { bandwidth => 1 } },
236
    DomainMigrateGetMaxSpeed    => { ret => { bandwidth => 1 } },
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
};

sub hyper_to_long
{
    my $proc_name = shift;
    my $ret_or_arg = shift;
    my $member = shift;

    if ($long_legacy->{$proc_name} and
        $long_legacy->{$proc_name}->{$ret_or_arg} and
        $long_legacy->{$proc_name}->{$ret_or_arg}->{$member}) {
        return 1;
    } else {
        return 0
    }
}

254 255 256 257
#----------------------------------------------------------------------
# Output

print <<__EOF__;
258
/* Automatically generated by gendispatch.pl.
259 260 261 262
 * Do not edit this file.  Any changes you make will be lost.
 */
__EOF__

263 264 265 266
if (!$opt_b and !$opt_k) {
    print "\n";
}

267 268 269 270 271 272 273
# Debugging.
if ($opt_d) {
    my @keys = sort (keys %calls);
    foreach (@keys) {
        print "$_:\n";
        print "        name $calls{$_}->{name} ($calls{$_}->{ProcName})\n";
        print "        $calls{$_}->{args} -> $calls{$_}->{ret}\n";
274
        print "        priority -> $calls{$_}->{priority}\n";
275 276 277
    }
}

278
# Bodies for dispatch functions ("remote_dispatch_bodies.h").
279
elsif ($opt_b) {
280
    my %generate = map { $_ => 1 } @autogen;
281 282 283
    my @keys = sort (keys %calls);

    foreach (@keys) {
284 285
        my $call = $calls{$_};

286
        # skip things which are REMOTE_MESSAGE
287
        next if $call->{msg};
288

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
	my $name = $structprefix . "Dispatch" . $call->{ProcName};
	my $argtype = $call->{args};
	my $rettype = $call->{ret};

	my $argann = $argtype ne "void" ? "" : " ATTRIBUTE_UNUSED";
	my $retann = $rettype ne "void" ? "" : " ATTRIBUTE_UNUSED";

	# First we print out a function declaration for the
	# real dispatcher body
	print "static int ${name}(\n";
	print "    virNetServerPtr server,\n";
	print "    virNetServerClientPtr client,\n";
	print "    virNetMessageHeaderPtr hdr,\n";
	print "    virNetMessageErrorPtr rerr";
	if ($argtype ne "void") {
	    print ",\n    $argtype *args";
	}
	if ($rettype ne "void") {
	    print ",\n    $rettype *ret";
	}
	print ");\n";


	# Next we print out a generic wrapper method which has
	# fixed function signature, for use in the dispatcher
	# table. This simply callers the real dispatcher method
	print "static int ${name}Helper(\n";
	print "    virNetServerPtr server,\n";
	print "    virNetServerClientPtr client,\n";
	print "    virNetMessageHeaderPtr hdr,\n";
	print "    virNetMessageErrorPtr rerr,\n";
	print "    void *args$argann,\n";
	print "    void *ret$retann)\n";
	print "{\n";
	print "  VIR_DEBUG(\"server=%p client=%p hdr=%p rerr=%p args=%p ret=%p\", server, client, hdr, rerr, args, ret);\n";
	print "  return $name(server, client, hdr, rerr";
	if ($argtype ne "void") {
	    print ", args";
	}
	if ($rettype ne "void") {
	    print ", ret";
	}
	print ");\n";
	print "}\n";

	# Finally we print out the dispatcher method body impl
	# (if possible)
        if (!exists($generate{$call->{ProcName}})) {
            print "/* ${structprefix}Dispatch$call->{ProcName} body has " .
                  "to be implemented manually */\n\n\n\n";
            next;
        }
341 342 343

        my $has_node_device = 0;
        my @vars_list = ();
344
        my @optionals_list = ();
345 346
        my @getters_list = ();
        my @args_list = ();
347
        my @prepare_ret_list = ();
348
        my @ret_list = ();
349
        my @free_list = ();
350
        my @free_list_on_error = ("virNetMessageSaveError(rerr);");
351

352
        # handle arguments to the function
353
        if ($argtype ne "void") {
354
            # node device is special, as it's identified by name
355 356 357
            if ($argtype =~ m/^remote_node_device_/ and
                !($argtype =~ m/^remote_node_device_lookup_by_name_/) and
                !($argtype =~ m/^remote_node_device_create_xml_/)) {
358 359 360
                $has_node_device = 1;
                push(@vars_list, "virNodeDevicePtr dev = NULL");
                push(@getters_list,
361
                     "    if (!(dev = virNodeDeviceLookupByName(priv->conn, args->name)))\n" .
362 363 364 365 366 367 368
                     "        goto cleanup;\n");
                push(@args_list, "dev");
                push(@free_list,
                     "    if (dev)\n" .
                     "        virNodeDeviceFree(dev);");
            }

369
            foreach my $args_member (@{$call->{args_members}}) {
370 371 372
                if ($args_member =~ m/^remote_nonnull_string name;/ and $has_node_device) {
                    # ignore the name arg for node devices
                    next
373 374 375 376
                } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter) (\S+);/) {
                    my $type_name = name_to_ProcName($1);

                    push(@vars_list, "vir${type_name}Ptr $2 = NULL");
377
                    push(@getters_list,
378
                         "    if (!($2 = get_nonnull_$1(priv->conn, args->$2)))\n" .
379
                         "        goto cleanup;\n");
380
                    push(@args_list, "$2");
381
                    push(@free_list,
382 383
                         "    if ($2)\n" .
                         "        vir${type_name}Free($2);");
384 385 386 387
                } elsif ($args_member =~ m/^remote_nonnull_domain_snapshot /) {
                    push(@vars_list, "virDomainPtr dom = NULL");
                    push(@vars_list, "virDomainSnapshotPtr snapshot = NULL");
                    push(@getters_list,
388
                         "    if (!(dom = get_nonnull_domain(priv->conn, args->snap.dom)))\n" .
389 390 391 392 393 394 395 396 397 398
                         "        goto cleanup;\n" .
                         "\n" .
                         "    if (!(snapshot = get_nonnull_domain_snapshot(dom, args->snap)))\n" .
                         "        goto cleanup;\n");
                    push(@args_list, "snapshot");
                    push(@free_list,
                         "    if (snapshot)\n" .
                         "        virDomainSnapshotFree(snapshot);\n" .
                         "    if (dom)\n" .
                         "        virDomainFree(dom);");
399
                } elsif ($args_member =~ m/^(?:remote_string|remote_uuid) (\S+)<\S+>;/) {
400
                    if (! @args_list) {
401
                        push(@args_list, "priv->conn");
402 403
                    }

404 405 406 407
                    push(@args_list, "args->$1.$1_val");
                    push(@args_list, "args->$1.$1_len");
                } elsif ($args_member =~ m/^(?:opaque|remote_nonnull_string) (\S+)<\S+>;(.*)$/) {
                    if (! @args_list) {
408
                        push(@args_list, "priv->conn");
409 410
                    }

411 412 413 414 415 416 417 418 419 420 421 422 423 424
                    my $cast = "";
                    my $arg_name = $1;
                    my $annotation = $2;

                    if ($annotation ne "") {
                        if ($annotation =~ m/\s*\/\*\s*(.*)\s*\*\//) {
                            $cast = $1;
                        } else {
                            die "malformed cast annotation for argument: $args_member";
                        }
                    }

                    push(@args_list, "${cast}args->$arg_name.${arg_name}_val");
                    push(@args_list, "args->$arg_name.${arg_name}_len");
425 426
                } elsif ($args_member =~ m/^(?:unsigned )?int (\S+)<\S+>;/) {
                    if (! @args_list) {
427
                        push(@args_list, "priv->conn");
428 429 430 431
                    }

                    push(@args_list, "args->$1.$1_val");
                    push(@args_list, "args->$1.$1_len");
432 433 434 435 436 437 438 439 440 441 442
                } elsif ($args_member =~ m/^remote_typed_param (\S+)<(\S+)>;/) {
                    push(@vars_list, "virTypedParameterPtr $1 = NULL");
                    push(@vars_list, "int n$1");
                    push(@args_list, "$1");
                    push(@args_list, "n$1");
                    push(@getters_list, "    if (($1 = remoteDeserializeTypedParameters(args->$1.$1_val,\n" .
                                        "                                                   args->$1.$1_len,\n" .
                                        "                                                   $2,\n" .
                                        "                                                   &n$1)) == NULL)\n" .
                                        "        goto cleanup;\n");
                    push(@free_list, "    VIR_FREE(params);");
443 444 445 446
                } elsif ($args_member =~ m/<\S+>;/ or $args_member =~ m/\[\S+\];/) {
                    # just make all other array types fail
                    die "unhandled type for argument value: $args_member";
                } elsif ($args_member =~ m/^remote_uuid (\S+);/) {
447
                    if (! @args_list) {
448
                        push(@args_list, "priv->conn");
449 450
                    }

451 452 453
                    push(@args_list, "(unsigned char *) args->$1");
                } elsif ($args_member =~ m/^remote_string (\S+);/) {
                    if (! @args_list) {
454
                        push(@args_list, "priv->conn");
455
                    }
456 457 458 459 460 461

                    push(@vars_list, "char *$1");
                    push(@optionals_list, "$1");
                    push(@args_list, "$1");
                } elsif ($args_member =~ m/^remote_nonnull_string (\S+);/) {
                    if (! @args_list) {
462
                        push(@args_list, "priv->conn");
463 464 465
                    }

                    push(@args_list, "args->$1");
466
                } elsif ($args_member =~ m/^(unsigned )?int (\S+);/) {
467
                    if (! @args_list) {
468
                        push(@args_list, "priv->conn");
469 470
                    }

471 472 473
                    push(@args_list, "args->$2");
                } elsif ($args_member =~ m/^(unsigned )?hyper (\S+);/) {
                    if (! @args_list) {
474
                        push(@args_list, "priv->conn");
475 476 477 478 479 480 481 482 483 484 485 486 487 488
                    }

                    my $arg_name = $2;

                    if (hyper_to_long($call->{ProcName}, "arg", $arg_name)) {
                        my $type_name = $1; $type_name .= "long";
                        my $sign = ""; $sign = "U" if ($1);

                        push(@vars_list, "$type_name $arg_name");
                        push(@getters_list, "    HYPER_TO_${sign}LONG($arg_name, args->$arg_name);\n");
                        push(@args_list, "$arg_name");
                    } else {
                        push(@args_list, "args->$arg_name");
                    }
489
                } elsif ($args_member =~ m/^(\/)?\*/) {
490 491 492
                    # ignore comments
                } else {
                    die "unhandled type for argument value: $args_member";
493 494 495 496
                }
            }
        }

497
        # handle return values of the function
498 499 500
        my $single_ret_var = "undefined";
        my $single_ret_by_ref = 0;
        my $single_ret_check = " == undefined";
501 502 503 504
        my $single_ret_as_list = 0;
        my $single_ret_list_name = "undefined";
        my $single_ret_list_max_var = "undefined";
        my $single_ret_list_max_define = "undefined";
505 506
        my $multi_ret = 0;

507
        if ($rettype ne "void" and
508
            scalar(@{$call->{ret_members}}) > 1) {
509 510
            $multi_ret = 1;
        }
511

512
        if ($rettype ne "void") {
513
            foreach my $ret_member (@{$call->{ret_members}}) {
514
                if ($multi_ret) {
515
                    if ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+)\[\S+\];/) {
516 517 518 519
                        if ($2 eq "hyper" and hyper_to_long($call->{ProcName}, "ret", $3)) {
                            die "legacy [u]long hyper arrays aren't supported";
                        }

520 521 522
                        push(@ret_list, "memcpy(ret->$3, tmp.$3, sizeof ret->$3);");
                    } elsif ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+);/) {
                        push(@ret_list, "ret->$3 = tmp.$3;");
523 524 525
                    } else {
                        die "unhandled type for multi-return-value: $ret_member";
                    }
526
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
527
                    push(@vars_list, "int len");
528
                    splice(@args_list, int($3), 0, ("ret->$1.$1_val"));
529
                    push(@ret_list, "ret->$1.$1_len = len;");
530
                    push(@free_list_on_error, "VIR_FREE(ret->$1.$1_val);");
531 532 533 534 535 536 537
                    $single_ret_var = "len";
                    $single_ret_by_ref = 0;
                    $single_ret_check = " < 0";
                    $single_ret_as_list = 1;
                    $single_ret_list_name = $1;
                    $single_ret_list_max_var = "max$1";
                    $single_ret_list_max_define = $2;
538 539 540
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+)<\S+>;/) {
                    # error out on unannotated arrays
                    die "remote_nonnull_string array without insert@<offset> annotation: $ret_member";
541
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+);/) {
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
                    if ($call->{ProcName} eq "GetType") {
                        # SPECIAL: virConnectGetType returns a constant string that must
                        #          not be freed. Therefore, duplicate the string here.
                        push(@vars_list, "const char *$1");
                        push(@ret_list, "/* We have to strdup because remoteDispatchClientRequest will");
                        push(@ret_list, " * free this string after it's been serialised. */");
                        push(@ret_list, "if (!(ret->type = strdup(type))) {");
                        push(@ret_list, "    virReportOOMError();");
                        push(@ret_list, "    goto cleanup;");
                        push(@ret_list, "}");
                    } else {
                        push(@vars_list, "char *$1");
                        push(@ret_list, "ret->$1 = $1;");
                    }

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
                    $single_ret_var = $1;
                    $single_ret_by_ref = 0;
                    $single_ret_check = " == NULL";
                } elsif ($ret_member =~ m/^remote_string (\S+);/) {
                    push(@vars_list, "char *$1 = NULL");
                    push(@vars_list, "char **$1_p = NULL");
                    push(@ret_list, "ret->$1 = $1_p;");
                    push(@free_list, "    VIR_FREE($1);");
                    push(@free_list_on_error, "VIR_FREE($1_p);");
                    push(@prepare_ret_list,
                         "if (VIR_ALLOC($1_p) < 0) {\n" .
                         "        virReportOOMError();\n" .
                         "        goto cleanup;\n" .
                         "    }\n" .
                         "    \n" .
                         "    *$1_p = strdup($1);\n" .
                         "    if (*$1_p == NULL) {\n" .
                         "        virReportOOMError();\n" .
                         "        goto cleanup;\n" .
                         "    }\n");

578 579 580
                    $single_ret_var = $1;
                    $single_ret_by_ref = 0;
                    $single_ret_check = " == NULL";
581
                } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|node_device|secret|nwfilter|domain_snapshot) (\S+);/) {
582 583
                    my $type_name = name_to_ProcName($1);

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
                    if ($call->{ProcName} eq "DomainCreateWithFlags") {
                        # SPECIAL: virDomainCreateWithFlags updates the given
                        #          domain object instead of returning a new one
                        push(@ret_list, "make_nonnull_$1(&ret->$2, $2);");
                        $single_ret_var = undef;
                        $single_ret_by_ref = 1;
                    } else {
                        push(@vars_list, "vir${type_name}Ptr $2 = NULL");
                        push(@ret_list, "make_nonnull_$1(&ret->$2, $2);");
                        push(@free_list,
                             "    if ($2)\n" .
                             "        vir${type_name}Free($2);");
                        $single_ret_var = $2;
                        $single_ret_by_ref = 0;
                        $single_ret_check = " == NULL";
                    }
600
                } elsif ($ret_member =~ m/^int (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
601
                    push(@vars_list, "int len");
602
                    splice(@args_list, int($3), 0, ("ret->$1.$1_val"));
603
                    push(@ret_list, "ret->$1.$1_len = len;");
604
                    push(@free_list_on_error, "VIR_FREE(ret->$1.$1_val);");
605 606 607 608 609 610 611
                    $single_ret_var = "len";
                    $single_ret_by_ref = 0;
                    $single_ret_check = " < 0";
                    $single_ret_as_list = 1;
                    $single_ret_list_name = $1;
                    $single_ret_list_max_var = "max$1";
                    $single_ret_list_max_define = $2;
612 613 614
                } elsif ($ret_member =~ m/^int (\S+)<\S+>;/) {
                    # error out on unannotated arrays
                    die "int array without insert@<offset> annotation: $ret_member";
615
                } elsif ($ret_member =~ m/^int (\S+);/) {
616 617 618 619
                    push(@vars_list, "int $1");
                    push(@ret_list, "ret->$1 = $1;");
                    $single_ret_var = $1;

620
                    if ($call->{ProcName} =~ m/GetAutostart$/) {
621 622 623
                        $single_ret_by_ref = 1;
                    } else {
                        $single_ret_by_ref = 0;
624

625
                        if ($call->{ProcName} eq "CPUCompare") {
626 627 628 629
                            $single_ret_check = " == VIR_CPU_COMPARE_ERROR";
                        } else {
                            $single_ret_check = " < 0";
                        }
630
                    }
631
                } elsif ($ret_member =~ m/^(?:unsigned )?hyper (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
632 633 634 635
                    if (hyper_to_long($call->{ProcName}, "ret", $1)) {
                        die "legacy [u]long hyper arrays aren't supported";
                    }

636 637
                    push(@vars_list, "int len");
                    push(@ret_list, "ret->$1.$1_len = len;");
638
                    push(@free_list_on_error, "VIR_FREE(ret->$1.$1_val);");
639 640 641 642
                    $single_ret_var = "len";
                    $single_ret_by_ref = 0;
                    $single_ret_as_list = 1;
                    $single_ret_list_name = $1;
643
                    $single_ret_list_max_var = "max$1";
644 645
                    $single_ret_list_max_define = $2;

646
                    if ($call->{ProcName} eq "NodeGetCellsFreeMemory") {
647
                        $single_ret_check = " <= 0";
648
                        splice(@args_list, int($3), 0, ("(unsigned long long *)ret->$1.$1_val"));
649 650
                    } else {
                        $single_ret_check = " < 0";
651
                        splice(@args_list, int($3), 0, ("ret->$1.$1_val"));
652
                    }
653
                } elsif ($ret_member =~ m/^(?:unsigned )?hyper (\S+)<\S+>;/) {
654 655
                    # error out on unannotated arrays
                    die "hyper array without insert@<offset> annotation: $ret_member";
656
                } elsif ($ret_member =~ m/^(unsigned )?hyper (\S+);(?:\s*\/\*\s*insert@(\d+)\s*\*\/)?/) {
657
                    my $type_name = $1;
658
                    my $ret_name = $2;
659
                    my $ret_assign;
660
                    my $insert = $3;
661

662 663 664 665 666 667 668 669 670
                    if (hyper_to_long($call->{ProcName}, "ret", $ret_name)) {
                        my $sign = ""; $sign = "U" if ($1);

                        $type_name .= "long";
                        $ret_assign = "HYPER_TO_${sign}LONG(ret->$ret_name, $ret_name);";
                    } else {
                        $type_name .= "long long";
                        $ret_assign = "ret->$ret_name = $ret_name;";
                    }
671 672

                    push(@vars_list, "$type_name $ret_name");
673
                    push(@ret_list, $ret_assign);
674 675 676 677 678 679 680

                    if ($insert) {
                        splice(@args_list, int($insert), 0, "&$ret_name");
                        $single_ret_var = undef;
                    } else {
                        $single_ret_var = $ret_name;
                    }
681

682 683
                    if ($call->{ProcName} eq "DomainGetMaxMemory" or
                        $call->{ProcName} eq "NodeGetFreeMemory") {
684 685 686
                        # SPECIAL: virDomainGetMaxMemory and virNodeGetFreeMemory
                        #          return the actual value directly and 0 indicates
                        #          an error
687 688 689 690 691
                        $single_ret_by_ref = 0;
                        $single_ret_check = " == 0";
                    } else {
                        $single_ret_by_ref = 1;
                    }
692 693 694 695 696 697 698 699 700 701 702 703
                } elsif ($ret_member =~ m/^opaque (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
                    push(@vars_list, "char *$1 = NULL");
                    push(@vars_list, "int $1_len = 0");
                    splice(@args_list, int($3), 0, ("&$1", "&$1_len"));
                    push(@ret_list, "ret->$1.$1_val = $1;");
                    push(@ret_list, "ret->$1.$1_len = $1_len;");
                    push(@free_list_on_error, "VIR_FREE($1);");
                    $single_ret_var = undef;
                    $single_ret_by_ref = 1;
                } elsif ($ret_member =~ m/^opaque (\S+)<\S+>;/) {
                    # error out on unannotated arrays
                    die "opaque array without insert@<offset> annotation: $ret_member";
704 705
                } elsif ($ret_member =~ m/^(\/)?\*/) {
                    # ignore comments
706 707
                } else {
                    die "unhandled type for return value: $ret_member";
708 709 710 711
                }
            }
        }

712
        # select struct type for multi-return-value functions
713
        if ($multi_ret) {
714 715 716 717
            if (!(defined $call->{ret_offset})) {
                die "multi-return-value without insert@<offset> annotation: $call->{ret}";
            }

718 719
            if (! @args_list) {
                push(@args_list, "priv->conn");
720 721
            }

722
            my $struct_name = $call->{ProcName};
723 724
            $struct_name =~ s/Get//;

725 726 727 728
            splice(@args_list, $call->{ret_offset}, 0, ("&tmp"));

            if ($call->{ProcName} eq "DomainBlockStats" ||
                $call->{ProcName} eq "DomainInterfaceStats") {
729 730 731
                # SPECIAL: virDomainBlockStats and virDomainInterfaceStats
                #          have a 'Struct' suffix on the actual struct name
                #          and take the struct size as additional argument
732
                $struct_name .= "Struct";
733
                splice(@args_list, $call->{ret_offset} + 1, 0, ("sizeof tmp"));
734 735 736 737 738
            }

            push(@vars_list, "vir$struct_name tmp");
        }

739
        if ($call->{streamflag} ne "none") {
740
            splice(@args_list, $call->{streamoffset}, 0, ("st"));
741
            push(@free_list_on_error, "if (stream) {");
742 743 744 745
            push(@free_list_on_error, "    virStreamAbort(st);");
            push(@free_list_on_error, "    daemonFreeClientStream(client, stream);");
            push(@free_list_on_error, "} else {");
            push(@free_list_on_error, "    virStreamFree(st);");
746 747 748
            push(@free_list_on_error, "}");
        }

749
        # print functions signature
750 751 752 753 754 755 756 757 758 759 760 761
	print "static int $name(\n";
	print "    virNetServerPtr server ATTRIBUTE_UNUSED,\n";
	print "    virNetServerClientPtr client,\n";
	print "    virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED,\n";
	print "    virNetMessageErrorPtr rerr";
        if ($argtype ne "void") {
	    print ",\n    $argtype *args";
	}
        if ($rettype ne "void") {
	    print ",\n    $rettype *ret";
	}
	print ")\n";
762 763 764 765 766

        # print function body
        print "{\n";
        print "    int rv = -1;\n";

767 768 769
        foreach my $var (@vars_list) {
            print "    $var;\n";
        }
770 771
	print "    struct daemonClientPrivate *priv =\n";
        print "        virNetServerClientGetPrivateData(client);\n";
772

773
        if ($call->{streamflag} ne "none") {
774 775
            print "    virStreamPtr st = NULL;\n";
            print "    daemonClientStreamPtr stream = NULL;\n";
776 777
        }

778
        print "\n";
779
        print "    if (!priv->conn) {\n";
780 781 782 783 784
        print "        virNetError(VIR_ERR_INTERNAL_ERROR, \"%s\", _(\"connection not open\"));\n";
        print "        goto cleanup;\n";
        print "    }\n";
        print "\n";

785 786 787 788 789 790 791 792 793
        if ($single_ret_as_list) {
            print "    if (args->$single_ret_list_max_var > $single_ret_list_max_define) {\n";
            print "        virNetError(VIR_ERR_INTERNAL_ERROR,\n";
            print "                    \"%s\", _(\"max$single_ret_list_name > $single_ret_list_max_define\"));\n";
            print "        goto cleanup;\n";
            print "    }\n";
            print "\n";
        }

794 795
        print join("\n", @getters_list);

796 797 798
        if (@getters_list) {
            print "\n";
        }
799

800 801 802 803 804 805 806 807
        foreach my $optional (@optionals_list) {
            print "    $optional = args->$optional ? *args->$optional : NULL;\n";
        }

        if (@optionals_list) {
            print "\n";
        }

808
        if ($call->{streamflag} ne "none") {
809 810 811 812
            print "    if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)))\n";
            print "        goto cleanup;\n";
            print "\n";
            print "    if (!(stream = daemonCreateClientStream(client, st, remoteProgram, hdr)))\n";
813 814 815 816
            print "        goto cleanup;\n";
            print "\n";
        }

817
        if ($rettype eq "void") {
818
            print "    if (vir$call->{ProcName}(";
819 820 821 822
            print join(', ', @args_list);
            print ") < 0)\n";
            print "        goto cleanup;\n";
            print "\n";
823
        } elsif (!$multi_ret) {
824
            my $prefix = "";
825
            my $proc_name = $call->{ProcName};
826 827

            if (! @args_list) {
828
                push(@args_list, "priv->conn");
829

830
                if ($call->{ProcName} ne "NodeGetFreeMemory") {
831 832
                    $prefix = "Connect"
                }
833 834
            }

835 836 837 838 839 840
            if ($call->{ProcName} eq "GetSysinfo" or
                $call->{ProcName} eq "GetMaxVcpus" or
                $call->{ProcName} eq "DomainXMLFromNative" or
                $call->{ProcName} eq "DomainXMLToNative" or
                $call->{ProcName} eq "FindStoragePoolSources" or
                $call->{ProcName} =~ m/^List/) {
841
                $prefix = "Connect"
842
            } elsif ($call->{ProcName} eq "SupportsFeature") {
843
                $prefix = "Drv"
844
            } elsif ($call->{ProcName} eq "CPUBaseline") {
845
                $proc_name = "ConnectBaselineCPU"
846
            } elsif ($call->{ProcName} eq "CPUCompare") {
847
                $proc_name = "ConnectCompareCPU"
848 849
            } elsif ($structprefix eq "qemu" && $call->{ProcName} =~ /^Domain/) {
                $proc_name =~ s/^(Domain)/${1}Qemu/;
850 851
            }

852 853 854 855 856 857 858 859 860 861
            if ($single_ret_as_list) {
                print "    /* Allocate return buffer. */\n";
                print "    if (VIR_ALLOC_N(ret->$single_ret_list_name.${single_ret_list_name}_val," .
                      " args->$single_ret_list_max_var) < 0) {\n";
                print "        virReportOOMError();\n";
                print "        goto cleanup;\n";
                print "    }\n";
                print "\n";
            }

862 863 864
            if ($single_ret_by_ref) {
                print "    if (vir$prefix$proc_name(";
                print join(', ', @args_list);
865 866 867 868 869 870

                if (defined $single_ret_var) {
                    print ", &$single_ret_var";
                }

                print ") < 0)\n";
871 872 873 874 875 876 877 878
            } else {
                print "    if (($single_ret_var = vir$prefix$proc_name(";
                print join(', ', @args_list);
                print "))$single_ret_check)\n";
            }

            print "        goto cleanup;\n";
            print "\n";
879
        } else {
880
            print "    if (vir$call->{ProcName}(";
881 882 883 884
            print join(', ', @args_list);
            print ") < 0)\n";
            print "        goto cleanup;\n";
            print "\n";
885
        }
886

887
        if ($call->{streamflag} ne "none") {
888
            print "    if (daemonAddClientStream(client, stream, ";
889 890

            if ($call->{streamflag} eq "write") {
891
                print "false";
892
            } else {
893
                print "true";
894 895
            }

896 897 898 899 900
            print ") < 0)\n";
            print "        goto cleanup;\n";
            print "\n";
        }

901 902 903 904 905 906
        if (@prepare_ret_list) {
            print "    ";
            print join("\n    ", @prepare_ret_list);
            print "\n";
        }

907 908
        if (@ret_list) {
            print "    ";
909
            print join("\n    ", @ret_list);
910
            print "\n";
911 912 913 914 915
        }

        print "    rv = 0;\n";
        print "\n";
        print "cleanup:\n";
916 917 918 919 920 921 922 923 924 925 926 927 928
        print "    if (rv < 0)";

        if (scalar(@free_list_on_error) > 1) {
            print " {";
        }

        print "\n        ";
        print join("\n        ", @free_list_on_error);
        print "\n";

        if (scalar(@free_list_on_error) > 1) {
            print "    }\n";
        }
929 930 931

        print join("\n", @free_list);

932 933 934 935
        if (@free_list) {
            print "\n";
        }

936
        print "    return rv;\n";
937
        print "}\n\n\n\n";
938
    }
939 940 941 942 943 944 945 946 947 948 949


    # Finally we write out the huge dispatch table which lists
    # the dispatch helper method. the XDR proc for processing
    # args and return values, and the size of the args and
    # return value structs. All methods are marked as requiring
    # authentication. Methods are selectively relaxed in the
    # daemon code which registers the program.

    print "virNetServerProgramProc ${structprefix}Procs[] = {\n";
    for ($id = 0 ; $id <= $#calls ; $id++) {
950
	my ($comment, $name, $argtype, $arglen, $argfilter, $retlen, $retfilter, $priority);
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972

	if (defined $calls[$id] && !$calls[$id]->{msg}) {
	    $comment = "/* Method $calls[$id]->{ProcName} => $id */";
	    $name = $structprefix . "Dispatch" . $calls[$id]->{ProcName} . "Helper";
	    my $argtype = $calls[$id]->{args};
	    my $rettype = $calls[$id]->{ret};
	    $arglen = $argtype ne "void" ? "sizeof($argtype)" : "0";
	    $retlen = $rettype ne "void" ? "sizeof($rettype)" : "0";
	    $argfilter = $argtype ne "void" ? "xdr_$argtype" : "xdr_void";
	    $retfilter = $rettype ne "void" ? "xdr_$rettype" : "xdr_void";
	} else {
	    if ($calls[$id]->{msg}) {
		$comment = "/* Async event $calls[$id]->{ProcName} => $id */";
	    } else {
		$comment = "/* Unused $id */";
	    }
	    $name = "NULL";
	    $arglen = $retlen = 0;
	    $argfilter = "xdr_void";
	    $retfilter = "xdr_void";
	}

973 974 975
    $priority = defined $calls[$id]->{priority} ? $calls[$id]->{priority} : 0;

	print "{ $comment\n   ${name},\n   $arglen,\n   (xdrproc_t)$argfilter,\n   $retlen,\n   (xdrproc_t)$retfilter,\n   true,\n   $priority\n},\n";
976 977 978
    }
    print "};\n";
    print "size_t ${structprefix}NProcs = ARRAY_CARDINALITY(${structprefix}Procs);\n";
979
}
980

981
# Bodies for client functions ("remote_client_bodies.h").
982
elsif ($opt_k) {
983
    my %generate = map { $_ => 1 } @autogen;
984 985 986 987 988 989 990 991
    my @keys = sort (keys %calls);

    foreach (@keys) {
        my $call = $calls{$_};

        # skip things which are REMOTE_MESSAGE
        next if $call->{msg};

992 993
        # skip procedures not on generate list
        next if ! exists($generate{$call->{ProcName}});
994

995 996 997
	my $argtype = $call->{args};
	my $rettype = $call->{ret};

998
        # handle arguments to the function
999 1000
        my @args_list = ();
        my @vars_list = ();
1001
        my @args_check_list = ();
1002
        my @setters_list = ();
1003
        my @setters_list2 = ();
1004
        my @free_list = ();
1005 1006
        my $priv_src = "conn";
        my $priv_name = "privateData";
1007
        my $call_args = "&args";
1008

1009
        if ($argtype eq "void") {
1010
            $call_args = "NULL";
1011
        } else {
1012
            push(@vars_list, "$argtype args");
1013 1014 1015 1016 1017

            my $is_first_arg = 1;
            my $has_node_device = 0;

            # node device is special
1018 1019 1020
            if ($argtype =~ m/^remote_node_/ and
                !($argtype =~ m/^remote_node_device_lookup_by_name_/) and
                !($argtype =~ m/^remote_node_device_create_xml_/)) {
1021
                $has_node_device = 1;
1022
                $priv_name = "devMonPrivateData";
1023 1024 1025 1026 1027 1028 1029 1030 1031
            }

            foreach my $args_member (@{$call->{args_members}}) {
                if ($args_member =~ m/^remote_nonnull_string name;/ and $has_node_device) {
                    $priv_src = "dev->conn";
                    push(@args_list, "virNodeDevicePtr dev");
                    push(@setters_list, "args.name = dev->name;");
                } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter|domain_snapshot) (\S+);/) {
                    my $name = $1;
1032
                    my $arg_name = $2;
1033 1034 1035 1036
                    my $type_name = name_to_ProcName($name);

                    if ($is_first_arg) {
                        if ($name eq "domain_snapshot") {
1037
                            $priv_src = "$arg_name->domain->conn";
1038
                        } else {
1039
                            $priv_src = "$arg_name->conn";
1040 1041 1042 1043 1044 1045 1046 1047 1048
                        }

                        if ($name =~ m/^storage_/) {
                            $priv_name = "storagePrivateData";
                        } elsif (!($name =~ m/^domain/)) {
                            $priv_name = "${name}PrivateData";
                        }
                    }

1049 1050 1051 1052 1053
                    push(@args_list, "vir${type_name}Ptr $arg_name");
                    push(@setters_list, "make_nonnull_$1(&args.$arg_name, $arg_name);");
                } elsif ($args_member =~ m/^remote_uuid (\S+);/) {
                    push(@args_list, "const unsigned char *$1");
                    push(@setters_list, "memcpy(args.$1, $1, VIR_UUID_BUFLEN);");
1054 1055 1056
                } elsif ($args_member =~ m/^remote_string (\S+);/) {
                    push(@args_list, "const char *$1");
                    push(@setters_list, "args.$1 = $1 ? (char **)&$1 : NULL;");
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
                } elsif ($args_member =~ m/^remote_nonnull_string (\S+)<(\S+)>;(.*)$/) {
                    my $type_name = "const char **";
                    my $arg_name = $1;
                    my $limit = $2;
                    my $annotation = $3;

                    if ($annotation ne "") {
                        if ($annotation =~ m/\s*\/\*\s*\((.*)\)\s*\*\//) {
                            $type_name = $1;
                        } else {
                            die "malformed cast annotation for argument: $args_member";
                        }
                    }

                    push(@args_list, "$type_name$arg_name");
                    push(@args_list, "unsigned int ${arg_name}len");
                    push(@setters_list, "args.$arg_name.${arg_name}_val = (char **)$arg_name;");
                    push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;");
                    push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $2 });
1076 1077 1078
                } elsif ($args_member =~ m/^remote_nonnull_string (\S+);/) {
                    push(@args_list, "const char *$1");
                    push(@setters_list, "args.$1 = (char *)$1;");
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
                } elsif ($args_member =~ m/^opaque (\S+)<(\S+)>;(.*)$/) {
                    my $type_name = "const char *";
                    my $arg_name = $1;
                    my $limit = $2;
                    my $annotation = $3;

                    if ($annotation ne "") {
                        if ($annotation =~ m/\s*\/\*\s*\((.*)\)\s*\*\//) {
                            $type_name = $1;
                        } else {
                            die "malformed cast annotation for argument: $args_member";
                        }
                    }

                    push(@args_list, "$type_name$arg_name");
1094

1095
                    if ($call->{ProcName} eq "SecretSetValue") {
1096
                        # SPECIAL: virSecretSetValue uses size_t instead of int
1097
                        push(@args_list, "size_t ${arg_name}len");
1098
                    } else {
1099
                        push(@args_list, "int ${arg_name}len");
1100 1101
                    }

1102 1103 1104 1105 1106 1107 1108 1109 1110
                    push(@setters_list, "args.$arg_name.${arg_name}_val = (char *)$arg_name;");
                    push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;");
                    push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit });
                } elsif ($args_member =~ m/^remote_string (\S+)<(\S+)>;/) {
                    my $arg_name = $1;
                    my $limit = $2;

                    push(@args_list, "const char *$arg_name");
                    push(@args_list, "int ${arg_name}len");
1111 1112 1113
                    push(@setters_list, "args.$arg_name.${arg_name}_val = (char *)$arg_name;");
                    push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;");
                    push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit });
1114 1115 1116 1117 1118 1119
                } elsif ($args_member =~ m/^((?:unsigned )?int) (\S+)<(\S+)>;/) {
                    my $type_name = $1;
                    my $arg_name = $2;
                    my $limit = $3;

                    push(@args_list, "${type_name} *$arg_name");
1120
                    push(@args_list, "int ${arg_name}len");
1121 1122 1123
                    push(@setters_list, "args.$arg_name.${arg_name}_val = $arg_name;");
                    push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;");
                    push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit });
1124 1125 1126 1127 1128 1129 1130
                } elsif ($args_member =~ m/^remote_typed_param (\S+)<(\S+)>;/) {
                    push(@args_list, "virTypedParameterPtr $1");
                    push(@args_list, "int n$1");
                    push(@setters_list2, "if (remoteSerializeTypedParameters($1, n$1, &args.$1.$1_val, &args.$1.$1_len) < 0) {\n" .
                                         "        xdr_free((xdrproc_t)xdr_$call->{args}, (char *)&args);\n" .
                                         "        goto done;\n" .
                                         "    }");
1131
                    push(@free_list, "    remoteFreeTypedParameters(args.params.params_val, args.params.params_len);\n");
1132 1133
                } elsif ($args_member =~ m/^((?:unsigned )?int) (\S+);\s*\/\*\s*call-by-reference\s*\*\//) {
                    my $type_name = "$1 *";
1134 1135 1136 1137
                    my $arg_name = $2;

                    push(@args_list, "$type_name $arg_name");
                    push(@setters_list, "args.$arg_name = *$arg_name;");
1138 1139
                } elsif ($args_member =~ m/^((?:unsigned )?int) (\S+);/) {
                    my $type_name = $1;
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
                    my $arg_name = $2;

                    push(@args_list, "$type_name $arg_name");
                    push(@setters_list, "args.$arg_name = $arg_name;");
                } elsif ($args_member =~ m/^(unsigned )?hyper (\S+);/) {
                    my $type_name = $1;
                    my $arg_name = $2;

                    if (hyper_to_long($call->{ProcName}, "arg", $arg_name)) {
                        $type_name .= "long";
                    } else {
                        $type_name .= "long long";
1152 1153 1154 1155
                    }

                    push(@args_list, "$type_name $arg_name");
                    push(@setters_list, "args.$arg_name = $arg_name;");
1156
                } elsif ($args_member =~ m/^(\/)?\*/) {
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
                    # ignore comments
                } else {
                    die "unhandled type for argument value: $args_member";
                }

                if ($is_first_arg and $priv_src eq "conn") {
                    unshift(@args_list, "virConnectPtr conn");
                }

                $is_first_arg = 0;
            }
        }

1170 1171 1172 1173
        if (! @args_list) {
            push(@args_list, "virConnectPtr conn");
        }

1174 1175
        # fix priv_name for the NumOf* functions
        if ($priv_name eq "privateData" and
1176 1177 1178
            !($call->{ProcName} =~ m/(Domains|DomainSnapshot)/) and
            ($call->{ProcName} =~ m/NumOf(Defined|Domain)*(\S+)s/ or
             $call->{ProcName} =~ m/List(Defined|Domain)*(\S+)s/)) {
1179 1180 1181 1182 1183
            my $prefix = lc $2;
            $prefix =~ s/(pool|vol)$//;
            $priv_name = "${prefix}PrivateData";
        }

1184 1185
        # handle return values of the function
        my @ret_list = ();
1186
        my @ret_list2 = ();
1187 1188 1189
        my $call_ret = "&ret";
        my $single_ret_var = "int rv = -1";
        my $single_ret_type = "int";
1190 1191 1192 1193 1194
        my $single_ret_as_list = 0;
        my $single_ret_list_error_msg_type = "undefined";
        my $single_ret_list_name = "undefined";
        my $single_ret_list_max_var = "undefined";
        my $single_ret_list_max_define = "undefined";
1195
        my $single_ret_cleanup = 0;
1196 1197
        my $multi_ret = 0;

1198
        if ($rettype ne "void" and
1199 1200 1201 1202
            scalar(@{$call->{ret_members}}) > 1) {
            $multi_ret = 1;
        }

1203
        if ($rettype eq "void") {
1204
            $call_ret = "NULL";
1205
        } else {
1206
            push(@vars_list, "$rettype ret");
1207 1208

            foreach my $ret_member (@{$call->{ret_members}}) {
1209
                if ($multi_ret) {
1210
                    if ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+)\[\S+\];/) {
1211 1212 1213 1214
                        if ($2 eq "hyper" and hyper_to_long($call->{ProcName}, "ret", $3)) {
                            die "legacy [u]long hyper arrays aren't supported";
                        }

1215 1216 1217
                        push(@ret_list, "memcpy(result->$3, ret.$3, sizeof result->$3);");
                    } elsif ($ret_member =~ m/<\S+>;/ or $ret_member =~ m/\[\S+\];/) {
                        # just make all other array types fail
1218 1219
                        die "unhandled type for multi-return-value for " .
                            "procedure $call->{name}: $ret_member";
1220
                    } elsif ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+);/) {
1221 1222 1223 1224 1225 1226 1227
                        if ($2 eq "hyper" and hyper_to_long($call->{ProcName}, "ret", $3)) {
                            my $sign = ""; $sign = "U" if ($1);

                            push(@ret_list, "HYPER_TO_${sign}LONG(result->$3, ret.$3);");
                        } else {
                            push(@ret_list, "result->$3 = ret.$3;");
                        }
1228
                    } else {
1229 1230
                        die "unhandled type for multi-return-value for " .
                            "procedure $call->{name}: $ret_member";
1231
                    }
1232 1233 1234 1235 1236
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
                    splice(@args_list, int($3), 0, ("char **const $1"));
                    push(@ret_list, "rv = ret.$1.$1_len;");
                    $single_ret_var = "int rv = -1";
                    $single_ret_type = "int";
1237 1238 1239 1240
                    $single_ret_as_list = 1;
                    $single_ret_list_name = $1;
                    $single_ret_list_max_var = "max$1";
                    $single_ret_list_max_define = $2;
1241 1242 1243
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+)<\S+>;/) {
                    # error out on unannotated arrays
                    die "remote_nonnull_string array without insert@<offset> annotation: $ret_member";
1244
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+);/) {
1245 1246 1247
                    push(@ret_list, "rv = ret.$1;");
                    $single_ret_var = "char *rv = NULL";
                    $single_ret_type = "char *";
1248 1249 1250 1251 1252
                } elsif ($ret_member =~ m/^remote_string (\S+);/) {
                    push(@ret_list, "rv = ret.$1 ? *ret.$1 : NULL;");
                    push(@ret_list, "VIR_FREE(ret.$1);");
                    $single_ret_var = "char *rv = NULL";
                    $single_ret_type = "char *";
1253
                } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|node_device|interface|secret|nwfilter|domain_snapshot) (\S+);/) {
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
                    my $name = $1;
                    my $arg_name = $2;
                    my $type_name = name_to_ProcName($name);

                    if ($name eq "node_device") {
                        $priv_name = "devMonPrivateData";
                    } elsif ($name =~ m/^storage_/) {
                        $priv_name = "storagePrivateData";
                    } elsif (!($name =~ m/^domain/)) {
                        $priv_name = "${name}PrivateData";
                    }

1266 1267 1268 1269 1270 1271 1272 1273
                    if ($call->{ProcName} eq "DomainCreateWithFlags") {
                        # SPECIAL: virDomainCreateWithFlags updates the given
                        #          domain object instead of returning a new one
                        push(@ret_list, "dom->id = ret.dom.id;");
                        push(@ret_list, "xdr_free((xdrproc_t)xdr_$call->{ret}, (char *)&ret);");
                        push(@ret_list, "rv = 0;");
                        $single_ret_var = "int rv = -1";
                        $single_ret_type = "int";
1274
                    } else {
1275 1276 1277 1278 1279
                        if ($name eq "domain_snapshot") {
                            push(@ret_list, "rv = get_nonnull_$name(dom, ret.$arg_name);");
                        } else {
                            push(@ret_list, "rv = get_nonnull_$name($priv_src, ret.$arg_name);");
                        }
1280

1281
                        push(@ret_list, "xdr_free((xdrproc_t)xdr_$rettype, (char *)&ret);");
1282 1283 1284
                        $single_ret_var = "vir${type_name}Ptr rv = NULL";
                        $single_ret_type = "vir${type_name}Ptr";
                    }
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
                } 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 (remoteDeserializeTypedParameters(ret.$1.$1_val,\n" .
                                     "                                         ret.$1.$1_len,\n" .
                                     "                                         $2,\n" .
                                     "                                         $1,\n" .
                                     "                                         n$1) < 0)\n" .
                                     "        goto cleanup;\n");
                    $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";
1297
                } elsif ($ret_member =~ m/^int (\S+);/) {
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
                    my $arg_name = $1;

                    if ($call->{ProcName} =~ m/GetAutostart$/) {
                        push(@args_list, "int *$arg_name");
                        push(@ret_list, "if ($arg_name) *$arg_name = ret.$arg_name;");
                        push(@ret_list, "rv = 0;");
                    } else {
                        push(@ret_list, "rv = ret.$arg_name;");
                    }

1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
                    $single_ret_var = "int rv = -1";
                    $single_ret_type = "int";
                } elsif ($ret_member =~ m/^(unsigned )?hyper (\S+);\s*\/\*\s*insert@(\d+)\s*\*\//) {
                    my $type_name = $1;
                    my $sign = ""; $sign = "U" if ($1);
                    my $ret_name = $2;
                    my $insert = $3;

                    if (hyper_to_long($call->{ProcName}, "ret", $ret_name)) {
                        $type_name .= "long";
                        push(@ret_list, "if ($ret_name) HYPER_TO_${sign}LONG(*$ret_name, ret.$ret_name);");
                    } else {
                        $type_name .= "long long";
                        push(@ret_list, "if ($ret_name) *$ret_name = ret.$ret_name;");
                    }

                    splice(@args_list, int($insert), 0, ("$type_name *$ret_name"));
                    push(@ret_list, "rv = 0;");
1326 1327
                    $single_ret_var = "int rv = -1";
                    $single_ret_type = "int";
1328
                } elsif ($ret_member =~ m/^unsigned hyper (\S+);/) {
1329
                    my $ret_name = $1;
1330

1331
                    if ($call->{ProcName} =~ m/Get(Lib)?Version/) {
1332 1333
                        push(@args_list, "unsigned long *$ret_name");
                        push(@ret_list, "if ($ret_name) HYPER_TO_ULONG(*$ret_name, ret.$ret_name);");
1334 1335 1336
                        push(@ret_list, "rv = 0;");
                        $single_ret_var = "int rv = -1";
                        $single_ret_type = "int";
1337 1338
                    } elsif (hyper_to_long($call->{ProcName}, "ret", $ret_name)) {
                        push(@ret_list, "HYPER_TO_ULONG(rv, ret.$ret_name);");
1339 1340
                        $single_ret_var = "unsigned long rv = 0";
                        $single_ret_type = "unsigned long";
1341 1342 1343 1344
                    } else {
                        push(@ret_list, "rv = ret.$ret_name;");
                        $single_ret_var = "unsigned long long rv = 0";
                        $single_ret_type = "unsigned long long";
1345
                    }
1346 1347
                } elsif ($ret_member =~ m/^(\/)?\*/) {
                    # ignore comments
1348
                } else {
1349 1350
                    die "unhandled type for return value for procedure " .
                        "$call->{name}: $ret_member";
1351 1352 1353 1354
                }
            }
        }

1355 1356
        # select struct type for multi-return-value functions
        if ($multi_ret) {
1357 1358
            if (!(defined $call->{ret_offset})) {
                die "multi-return-value without insert@<offset> annotation: $call->{ret}";
1359 1360
            }

1361 1362
            my $struct_name = $call->{ProcName};
            $struct_name =~ s/Get//;
1363

1364
            splice(@args_list, $call->{ret_offset}, 0, ("vir${struct_name}Ptr result"));
1365 1366
        }

1367 1368 1369 1370
        if ($call->{streamflag} ne "none") {
            splice(@args_list, $call->{streamoffset}, 0, ("virStreamPtr st"));
        }

1371 1372
        # print function
        print "\n";
1373
        print "static $single_ret_type\n";
1374
        print "$structprefix$call->{ProcName}(";
1375 1376 1377 1378 1379

        print join(", ", @args_list);

        print ")\n";
        print "{\n";
1380
        print "    $single_ret_var;\n";
1381 1382 1383 1384 1385 1386
        print "    struct private_data *priv = $priv_src->$priv_name;\n";

        foreach my $var (@vars_list) {
            print "    $var;\n";
        }

1387 1388 1389 1390
        if ($single_ret_as_list) {
            print "    int i;\n";
        }

1391
        if ($call->{streamflag} ne "none") {
1392
            print "    virNetClientStreamPtr netst = NULL;\n";
1393 1394
        }

1395 1396 1397
        print "\n";
        print "    remoteDriverLock(priv);\n";

1398 1399
        if ($call->{streamflag} ne "none") {
            print "\n";
1400
            print "    if (!(netst = virNetClientStreamNew(priv->remoteProgram, REMOTE_PROC_$call->{UC_NAME}, priv->counter)))\n";
1401
            print "        goto done;\n";
1402
            print "\n";
1403 1404 1405
            print "    if (virNetClientAddStream(priv->client, netst) < 0) {\n";
            print "        virNetClientStreamFree(netst);\n";
            print "        goto done;\n";
1406 1407
            print "    }";
            print "\n";
1408
            print "    st->driver = &remoteStreamDrv;\n";
1409
            print "    st->privateData = netst;\n";
1410 1411
        }

1412 1413 1414 1415 1416 1417 1418 1419 1420
        if ($call->{ProcName} eq "SupportsFeature") {
            # SPECIAL: VIR_DRV_FEATURE_REMOTE feature is handled directly
            print "\n";
            print "    if (feature == VIR_DRV_FEATURE_REMOTE) {\n";
            print "        rv = 1;\n";
            print "        goto done;\n";
            print "    }\n";
        }

1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
        foreach my $args_check (@args_check_list) {
            print "\n";
            print "    if ($args_check->{arg} > $args_check->{limit}) {\n";
            print "        remoteError(VIR_ERR_RPC,\n";
            print "                    _(\"%s length greater than maximum: %d > %d\"),\n";
            print "                    $args_check->{name}, (int)$args_check->{arg}, $args_check->{limit});\n";
            print "        goto done;\n";
            print "    }\n";
        }

1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
        if ($single_ret_as_list) {
            print "\n";
            print "    if ($single_ret_list_max_var > $single_ret_list_max_define) {\n";
            print "        remoteError(VIR_ERR_RPC,\n";
            print "                    _(\"too many remote ${single_ret_list_error_msg_type}s: %d > %d\"),\n";
            print "                    $single_ret_list_max_var, $single_ret_list_max_define);\n";
            print "        goto done;\n";
            print "    }\n";
        }

1441
        if (@setters_list) {
1442
            print "\n";
1443 1444 1445 1446 1447 1448 1449 1450 1451
            print "    ";
        }

        print join("\n    ", @setters_list);

        if (@setters_list) {
            print "\n";
        }

1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462
        if (@setters_list2) {
            print "\n";
            print "    ";
        }

        print join("\n    ", @setters_list2);

        if (@setters_list2) {
            print "\n";
        }

1463
        if ($rettype ne "void") {
1464 1465 1466 1467
            print "\n";
            print "    memset(&ret, 0, sizeof ret);\n";
        }

1468 1469 1470 1471 1472
        my $callflags = "0";
        if ($structprefix eq "qemu") {
            $callflags = "REMOTE_CALL_QEMU";
        }

1473
        print "\n";
1474
        print "    if (call($priv_src, priv, $callflags, ${procprefix}_PROC_$call->{UC_NAME},\n";
1475 1476
        print "             (xdrproc_t)xdr_$argtype, (char *)$call_args,\n";
        print "             (xdrproc_t)xdr_$rettype, (char *)$call_ret) == -1) {\n";
1477 1478

        if ($call->{streamflag} ne "none") {
1479 1480
            print "        virNetClientRemoveStream(priv->client, netst);\n";
            print "        virNetClientStreamFree(netst);\n";
1481 1482
        }

1483
        print "        goto done;\n";
1484
        print "    }\n";
1485
        print "\n";
1486

1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
        if ($single_ret_as_list) {
            print "    if (ret.$single_ret_list_name.${single_ret_list_name}_len > $single_ret_list_max_var) {\n";
            print "        remoteError(VIR_ERR_RPC,\n";
            print "                    _(\"too many remote ${single_ret_list_error_msg_type}s: %d > %d\"),\n";
            print "                    ret.$single_ret_list_name.${single_ret_list_name}_len, $single_ret_list_max_var);\n";
            print "        goto cleanup;\n";
            print "    }\n";
            print "\n";
            print "    /* This call is caller-frees (although that isn't clear from\n";
            print "     * the documentation).  However xdr_free will free up both the\n";
            print "     * names and the list of pointers, so we have to strdup the\n";
            print "     * names here. */\n";
            print "    for (i = 0; i < ret.$single_ret_list_name.${single_ret_list_name}_len; ++i) {\n";
            print "        ${single_ret_list_name}[i] = strdup(ret.$single_ret_list_name.${single_ret_list_name}_val[i]);\n";
            print "\n";
            print "        if (${single_ret_list_name}[i] == NULL) {\n";
            print "            for (--i; i >= 0; --i)\n";
            print "                VIR_FREE(${single_ret_list_name}[i]);\n";
            print "\n";
            print "            virReportOOMError();\n";
            print "            goto cleanup;\n";
            print "        }\n";
            print "    }\n";
            print "\n";
        }

1513 1514 1515 1516 1517 1518
        if (@ret_list2) {
            print "    ";
            print join("\n    ", @ret_list2);
            print "\n";
        }

1519 1520 1521 1522
        if (@ret_list) {
            print "    ";
            print join("\n    ", @ret_list);
            print "\n";
1523 1524
        }

1525 1526 1527 1528 1529
        if ($call->{ProcName} eq "DomainDestroy" ||
	    $call->{ProcName} eq "DomainSave" ||
	    $call->{ProcName} eq "DomainManagedSave") {
            # SPECIAL: virDomain{Destroy|Save|ManagedSave} need to reset
	    # the domain id explicitly on success
1530 1531 1532
            print "    dom->id = -1;\n";
        }

1533
        if ($multi_ret or !@ret_list) {
1534 1535 1536
            print "    rv = 0;\n";
        }

1537
        if ($single_ret_as_list or $single_ret_cleanup) {
1538 1539 1540 1541 1542
            print "\n";
            print "cleanup:\n";
            print "    xdr_free((xdrproc_t)xdr_remote_$call->{name}_ret, (char *)&ret);\n";
        }

1543 1544
        print "\n";
        print "done:\n";
1545 1546 1547

        print join("\n", @free_list);

1548 1549 1550 1551 1552
        print "    remoteDriverUnlock(priv);\n";
        print "    return rv;\n";
        print "}\n";
    }
}