gendispatch.pl 66.1 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

use strict;

20
use Getopt::Long;
21

22 23 24 25 26 27 28
my $mode = "debug";
my $res = GetOptions("mode=s" => \$mode);

die "cannot parse command line options" unless $res;

die "unknown mode '$mode', expecting 'client', 'server' or 'debug'"
    unless $mode =~ /^(client|server|debug)$/;
29

30 31
my $structprefix = shift or die "missing struct prefix argument";
my $procprefix = shift or die "missing procedure prefix argument";
32 33 34
my $protocol = shift or die "missing protocol argument";
my @autogen;

35 36 37 38 39 40 41 42 43 44 45 46 47 48

sub fixup_name {
    my $name = shift;

    $name =~ s/Nwfilter/NWFilter/;
    $name =~ s/Xml$/XML/;
    $name =~ s/Uri$/URI/;
    $name =~ s/Uuid$/UUID/;
    $name =~ s/Id$/ID/;
    $name =~ s/Mac$/MAC/;
    $name =~ s/Cpu$/CPU/;
    $name =~ s/Os$/OS/;
    $name =~ s/Nmi$/NMI/;
    $name =~ s/Pm/PM/;
49
    $name =~ s/Fstrim$/FSTrim/;
O
Osier Yang 已提交
50 51
    $name =~ s/Scsi/SCSI/;
    $name =~ s/Wwn$/WWN/;
52 53 54

    return $name;
}
55 56 57 58

# Convert name_of_call to NameOfCall.
sub name_to_ProcName {
    my $name = shift;
59 60 61

    my @elems;
    if ($name =~ /_/ || (lc $name) eq "open" || (lc $name) eq "close") {
62 63 64
        @elems = split /_/, $name;
        @elems = map lc, @elems;
        @elems = map ucfirst, @elems;
65
    } else {
66
        @elems = $name;
67 68 69 70 71 72 73 74 75 76
    }
    @elems = map { fixup_name($_) } @elems;
    my $procname = join "", @elems;

    return $procname;
}

sub name_to_TypeName {
    my $name = shift;

77
    my @elems = split /_/, $name;
78
    @elems = map lc, @elems;
79
    @elems = map ucfirst, @elems;
80 81 82
    @elems = map { fixup_name($_) } @elems;
    my $typename = join "", @elems;
    return $typename;
83 84 85 86
}

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

89
my $collect_args_members = 0;
90
my $collect_ret_members = 0;
91
my $collect_opts = 0;
92 93
my $last_name;

94 95 96
open PROTOCOL, "<$protocol" or die "cannot open $protocol: $!";

while (<PROTOCOL>) {
97 98 99 100 101 102
    if ($collect_args_members) {
        if (/^};/) {
            $collect_args_members = 0;
        } elsif ($_ =~ m/^\s*(.*\S)\s*$/) {
            push(@{$calls{$name}->{args_members}}, $1);
        }
103 104 105 106 107 108
    } elsif ($collect_ret_members) {
        if (/^};/) {
            $collect_ret_members = 0;
        } elsif ($_ =~ m/^\s*(.*\S)\s*$/) {
            push(@{$calls{$name}->{ret_members}}, $1);
        }
109 110 111 112 113 114 115 116 117 118 119 120 121
    } elsif ($collect_opts) {
        if (m,^\s*\*\s*\@(\w+)\s*:\s*(\w+)\s*$,) {
            $opts{$1} = $2;
        } elsif (m,^\s*\*/\s*$,) {
            $collect_opts = 0;
        } elsif (m,^\s*\*\s*$,) {
            # pass
        } else {
            die "cannot parse $_";
        }
    } elsif (m,/\*\*,) {
        %opts = ();
        $collect_opts = 1;
122 123 124 125
    } elsif (/^struct (${structprefix}_(.*)_args)/ ||
             /^struct (${structprefix}(.*)Args)/) {
        my $structname = $1;
        $name = $2;
126
        $ProcName = name_to_ProcName ($name);
127 128 129
        $name = lc $name;
        $name =~ s/_//g;
        die "duplicate definition of $_"
130 131 132 133 134
            if exists $calls{$name};

        $calls{$name} = {
            name => $name,
            ProcName => $ProcName,
135
            args => $structname,
136 137
            args_members => [],
            ret => "void"
138 139
        };

140
        $collect_args_members = 1;
141
        $collect_ret_members = 0;
142
        $last_name = $name;
143 144 145 146 147
    } elsif (/^struct (${structprefix}_(.*)_ret)\s+{(.*)$/ ||
             /^struct (${structprefix}(.*)Ret)\s+{(.*)$/) {
        my $structname = $1;
        $name = $2;
        $flags = $3;
148
        $ProcName = name_to_ProcName ($name);
149 150
        $name = lc $name;
        $name =~ s/_//g;
151 152

        if (exists $calls{$name}) {
153
            $calls{$name}->{ret} = $structname;
154 155 156 157 158
        } else {
            $calls{$name} = {
                name => $name,
                ProcName => $ProcName,
                args => "void",
159
                ret => $structname,
160
                ret_members => []
161 162
            }
        }
163

164
        if ($flags ne "") {
165 166 167 168 169 170 171
            if (!($flags =~ m/^\s*\/\*\s*insert@(\d+)\s*\*\/\s*$/)) {
                die "invalid generator flags for $calls{$name}->{ret}";
            }

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

172
        $collect_args_members = 0;
173 174
        $collect_ret_members = 1;
        $last_name = $name;
175 176 177 178
    } elsif (/^struct (${structprefix}_(.*)_msg)/ ||
             /^struct (${structprefix}(.*)Msg)/) {
        my $structname = $1;
        $name = $2;
179
        $ProcName = name_to_ProcName ($name);
180 181
        $name = lc $name;
        $name =~ s/_//g;
182 183 184
        $calls{$name} = {
            name => $name,
            ProcName => $ProcName,
185
            msg => $structname,
186 187 188
        };

        $collect_args_members = 0;
189
        $collect_ret_members = 0;
190
    } elsif (/^\s*(${procprefix}_PROC_(.*?))\s*=\s*(\d+)\s*,?\s*$/) {
191 192 193
        my $constname = $1;
        $name = $2;
        $id = $3;
194
        $ProcName = name_to_ProcName ($name);
195 196
        $name = lc $name;
        $name =~ s/_//g;
197

198 199 200 201 202 203 204 205 206 207 208 209
        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,
                args => "void",
                ret => "void"
            }
        }
210
        $calls{$name}->{constname} = $constname;
211

212 213 214
        if (!exists $opts{generate}) {
            die "'\@generate' annotation missing for $constname";
        }
215

216 217 218
        if ($opts{generate} !~ /^(both|server|client|none)$/) {
            die "'\@generate' annotation value '$opts{generate}' invalid";
        }
219

220 221 222 223 224 225 226
        if ($opts{generate} eq "both") {
            push(@autogen, $ProcName);
        } elsif ($mode eq "server" && ($opts{generate} eq "server")) {
            push(@autogen, $ProcName);
        } elsif ($mode eq "client" && ($opts{generate} eq "client")) {
            push(@autogen, $ProcName);
        }
227

228 229 230 231 232 233 234 235 236
        if (exists $opts{readstream}) {
            $calls{$name}->{streamflag} = "read";
            $calls{$name}->{streamoffset} = int($opts{readstream});
        } elsif (exists $opts{writestream}) {
            $calls{$name}->{streamflag} = "write";
            $calls{$name}->{streamoffset} = int($opts{writestream});
        } else {
            $calls{$name}->{streamflag} = "none";
        }
237

238

239 240 241 242 243 244
        # for now, we distinguish only two levels of priority:
        # low (0) and high (1)
        if (exists $opts{priority}) {
            if ($opts{priority} eq "high") {
                $calls{$name}->{priority} = 1;
            } elsif ($opts{priority} eq "low") {
245
                $calls{$name}->{priority} = 0;
246 247
            } else {
                die "\@priority annotation value '$opts{priority}' invalid for $constname"
248
            }
249 250
        } else {
            $calls{$name}->{priority} = 0;
251 252
        }

253
        $calls[$id] = $calls{$name};
254 255

        $collect_args_members = 0;
256
        $collect_ret_members = 0;
257 258
    } else {
        $collect_args_members = 0;
259
        $collect_ret_members = 0;
260 261 262
    }
}

263 264
close(PROTOCOL);

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
# 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 = {
    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 } },
296 297
    ConnectGetLibVersion        => { ret => { lib_ver => 1 } },
    ConnectGetVersion           => { ret => { hv_ver => 1 } },
298
    NodeGetInfo                 => { ret => { memory => 1 } },
299
    DomainBlockCommit           => { arg => { bandwidth => 1 } },
300
    DomainBlockPull             => { arg => { bandwidth => 1 } },
301
    DomainBlockRebase           => { arg => { bandwidth => 1 } },
302
    DomainBlockJobSetSpeed      => { arg => { bandwidth => 1 } },
303
    DomainMigrateGetMaxSpeed    => { ret => { bandwidth => 1 } },
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
};

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
    }
}

321 322 323 324
#----------------------------------------------------------------------
# Output

print <<__EOF__;
325
/* Automatically generated by gendispatch.pl.
326 327 328 329 330
 * Do not edit this file.  Any changes you make will be lost.
 */
__EOF__

# Debugging.
331
if ($mode eq "debug") {
332 333 334 335 336
    my @keys = sort (keys %calls);
    foreach (@keys) {
        print "$_:\n";
        print "        name $calls{$_}->{name} ($calls{$_}->{ProcName})\n";
        print "        $calls{$_}->{args} -> $calls{$_}->{ret}\n";
337
        print "        priority -> $calls{$_}->{priority}\n";
338 339 340
    }
}

341
# Bodies for dispatch functions ("remote_dispatch_bodies.h").
342
elsif ($mode eq "server") {
343
    my %generate = map { $_ => 1 } @autogen;
344 345 346
    my @keys = sort (keys %calls);

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

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

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
        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 "    virNetMessagePtr msg,\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 "    virNetMessagePtr msg,\n";
        print "    virNetMessageErrorPtr rerr,\n";
        print "    void *args$argann,\n";
        print "    void *ret$retann)\n";
        print "{\n";
        print "  VIR_DEBUG(\"server=%p client=%p msg=%p rerr=%p args=%p ret=%p\", server, client, msg, rerr, args, ret);\n";
        print "  return $name(server, client, msg, 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)
399 400 401 402 403
        if (!exists($generate{$call->{ProcName}})) {
            print "/* ${structprefix}Dispatch$call->{ProcName} body has " .
                  "to be implemented manually */\n\n\n\n";
            next;
        }
404 405 406

        my $has_node_device = 0;
        my @vars_list = ();
407
        my @optionals_list = ();
408 409
        my @getters_list = ();
        my @args_list = ();
410
        my @prepare_ret_list = ();
411
        my @ret_list = ();
412
        my @free_list = ();
413
        my @free_list_on_error = ("virNetMessageSaveError(rerr);");
414

415
        # handle arguments to the function
416
        if ($argtype ne "void") {
417
            # node device is special, as it's identified by name
418 419
            if ($argtype =~ m/^remote_node_device_/ and
                !($argtype =~ m/^remote_node_device_lookup_by_name_/) and
O
Osier Yang 已提交
420 421
                !($argtype =~ m/^remote_node_device_create_xml_/) and
                !($argtype =~ m/^remote_node_device_lookup_scsi_host_by_wwn_/)) {
422 423 424
                $has_node_device = 1;
                push(@vars_list, "virNodeDevicePtr dev = NULL");
                push(@getters_list,
425
                     "    if (!(dev = virNodeDeviceLookupByName(priv->conn, args->name)))\n" .
426 427 428 429 430 431 432
                     "        goto cleanup;\n");
                push(@args_list, "dev");
                push(@free_list,
                     "    if (dev)\n" .
                     "        virNodeDeviceFree(dev);");
            }

433
            foreach my $args_member (@{$call->{args_members}}) {
434 435 436
                if ($args_member =~ m/^remote_nonnull_string name;/ and $has_node_device) {
                    # ignore the name arg for node devices
                    next
437
                } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter) (\S+);/) {
438
                    my $type_name = name_to_TypeName($1);
439 440

                    push(@vars_list, "vir${type_name}Ptr $2 = NULL");
441
                    push(@getters_list,
442
                         "    if (!($2 = get_nonnull_$1(priv->conn, args->$2)))\n" .
443
                         "        goto cleanup;\n");
444
                    push(@args_list, "$2");
445
                    push(@free_list,
446 447
                         "    if ($2)\n" .
                         "        vir${type_name}Free($2);");
448 449 450 451
                } elsif ($args_member =~ m/^remote_nonnull_domain_snapshot /) {
                    push(@vars_list, "virDomainPtr dom = NULL");
                    push(@vars_list, "virDomainSnapshotPtr snapshot = NULL");
                    push(@getters_list,
452
                         "    if (!(dom = get_nonnull_domain(priv->conn, args->snap.dom)))\n" .
453 454 455 456 457 458 459 460 461 462
                         "        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);");
463
                } elsif ($args_member =~ m/^(?:remote_string|remote_uuid) (\S+)<\S+>;/) {
464
                    if (! @args_list) {
465
                        push(@args_list, "priv->conn");
466 467
                    }

468 469 470 471
                    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) {
472
                        push(@args_list, "priv->conn");
473 474
                    }

475 476 477 478 479 480 481 482 483 484 485 486 487 488
                    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");
489 490
                } elsif ($args_member =~ m/^(?:unsigned )?int (\S+)<\S+>;/) {
                    if (! @args_list) {
491
                        push(@args_list, "priv->conn");
492 493 494 495
                    }

                    push(@args_list, "args->$1.$1_val");
                    push(@args_list, "args->$1.$1_len");
496 497 498
                } elsif ($args_member =~ m/^remote_typed_param (\S+)<(\S+)>;/) {
                    push(@vars_list, "virTypedParameterPtr $1 = NULL");
                    push(@vars_list, "int n$1");
499 500 501
                    if ($call->{ProcName} eq "NodeSetMemoryParameters") {
                        push(@args_list, "priv->conn");
                    }
502 503 504 505 506 507 508
                    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");
509
                    push(@free_list, "    virTypedParamsFree($1, n$1);");
510 511 512 513
                } 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+);/) {
514
                    if (! @args_list) {
515
                        push(@args_list, "priv->conn");
516 517
                    }

518 519 520
                    push(@args_list, "(unsigned char *) args->$1");
                } elsif ($args_member =~ m/^remote_string (\S+);/) {
                    if (! @args_list) {
521
                        push(@args_list, "priv->conn");
522
                    }
523 524 525 526 527 528

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

                    push(@args_list, "args->$1");
533
                } elsif ($args_member =~ m/^(unsigned )?int (\S+);/) {
534
                    if (! @args_list) {
535
                        push(@args_list, "priv->conn");
536 537
                    }

538 539 540
                    push(@args_list, "args->$2");
                } elsif ($args_member =~ m/^(unsigned )?hyper (\S+);/) {
                    if (! @args_list) {
541
                        push(@args_list, "priv->conn");
542 543 544 545 546 547 548 549 550 551 552 553 554 555
                    }

                    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");
                    }
556
                } elsif ($args_member =~ m/^(\/)?\*/) {
557 558 559
                    # ignore comments
                } else {
                    die "unhandled type for argument value: $args_member";
560 561 562 563
                }
            }
        }

564
        # handle return values of the function
565 566 567
        my $single_ret_var = "undefined";
        my $single_ret_by_ref = 0;
        my $single_ret_check = " == undefined";
568 569 570 571
        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";
572 573
        my $multi_ret = 0;

574
        if ($rettype ne "void" and
575
            scalar(@{$call->{ret_members}}) > 1) {
576 577
            $multi_ret = 1;
        }
578

579
        if ($rettype ne "void") {
580
            foreach my $ret_member (@{$call->{ret_members}}) {
581
                if ($multi_ret) {
582
                    if ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+)\[\S+\];/) {
583 584 585 586
                        if ($2 eq "hyper" and hyper_to_long($call->{ProcName}, "ret", $3)) {
                            die "legacy [u]long hyper arrays aren't supported";
                        }

587
                        push(@ret_list, "memcpy(ret->$3, tmp.$3, sizeof(ret->$3));");
588 589
                    } elsif ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+);/) {
                        push(@ret_list, "ret->$3 = tmp.$3;");
590 591 592
                    } else {
                        die "unhandled type for multi-return-value: $ret_member";
                    }
593
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
594
                    push(@vars_list, "int len");
595
                    splice(@args_list, int($3), 0, ("ret->$1.$1_val"));
596
                    push(@ret_list, "ret->$1.$1_len = len;");
597
                    push(@free_list_on_error, "VIR_FREE(ret->$1.$1_val);");
598 599 600 601 602 603 604
                    $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;
605 606 607
                } 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";
608
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+);/) {
609
                    if ($call->{ProcName} eq "ConnectGetType") {
610 611 612 613 614 615 616 617 618 619 620 621 622 623
                        # 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;");
                    }

624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
                    $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");

645 646 647
                    $single_ret_var = $1;
                    $single_ret_by_ref = 0;
                    $single_ret_check = " == NULL";
648
                } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|node_device|secret|nwfilter|domain_snapshot) (\S+);/) {
649
                    my $type_name = name_to_TypeName($1);
650

651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
                    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";
                    }
667
                } elsif ($ret_member =~ m/^int (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
668
                    push(@vars_list, "int len");
669
                    splice(@args_list, int($3), 0, ("ret->$1.$1_val"));
670
                    push(@ret_list, "ret->$1.$1_len = len;");
671
                    push(@free_list_on_error, "VIR_FREE(ret->$1.$1_val);");
672 673 674 675 676 677 678
                    $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;
679 680 681
                } elsif ($ret_member =~ m/^int (\S+)<\S+>;/) {
                    # error out on unannotated arrays
                    die "int array without insert@<offset> annotation: $ret_member";
682
                } elsif ($ret_member =~ m/^int (\S+);/) {
683 684 685 686
                    push(@vars_list, "int $1");
                    push(@ret_list, "ret->$1 = $1;");
                    $single_ret_var = $1;

687
                    if ($call->{ProcName} =~ m/GetAutostart$/) {
688 689 690
                        $single_ret_by_ref = 1;
                    } else {
                        $single_ret_by_ref = 0;
691

692
                        if ($call->{ProcName} eq "CPUCompare") {
693 694 695 696
                            $single_ret_check = " == VIR_CPU_COMPARE_ERROR";
                        } else {
                            $single_ret_check = " < 0";
                        }
697
                    }
698
                } elsif ($ret_member =~ m/^(?:unsigned )?hyper (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) {
699 700 701 702
                    if (hyper_to_long($call->{ProcName}, "ret", $1)) {
                        die "legacy [u]long hyper arrays aren't supported";
                    }

703 704
                    push(@vars_list, "int len");
                    push(@ret_list, "ret->$1.$1_len = len;");
705
                    push(@free_list_on_error, "VIR_FREE(ret->$1.$1_val);");
706 707 708 709
                    $single_ret_var = "len";
                    $single_ret_by_ref = 0;
                    $single_ret_as_list = 1;
                    $single_ret_list_name = $1;
710
                    $single_ret_list_max_var = "max$1";
711 712
                    $single_ret_list_max_define = $2;

713
                    if ($call->{ProcName} eq "NodeGetCellsFreeMemory") {
714
                        $single_ret_check = " <= 0";
715
                        splice(@args_list, int($3), 0, ("(unsigned long long *)ret->$1.$1_val"));
716 717
                    } else {
                        $single_ret_check = " < 0";
718
                        splice(@args_list, int($3), 0, ("ret->$1.$1_val"));
719
                    }
720
                } elsif ($ret_member =~ m/^(?:unsigned )?hyper (\S+)<\S+>;/) {
721 722
                    # error out on unannotated arrays
                    die "hyper array without insert@<offset> annotation: $ret_member";
723
                } elsif ($ret_member =~ m/^(unsigned )?hyper (\S+);(?:\s*\/\*\s*insert@(\d+)\s*\*\/)?/) {
724
                    my $type_name = $1;
725
                    my $ret_name = $2;
726
                    my $ret_assign;
727
                    my $insert = $3;
728

729 730 731 732 733 734 735 736 737
                    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;";
                    }
738 739

                    push(@vars_list, "$type_name $ret_name");
740
                    push(@ret_list, $ret_assign);
741 742 743 744 745 746 747

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

749 750
                    if ($call->{ProcName} eq "DomainGetMaxMemory" or
                        $call->{ProcName} eq "NodeGetFreeMemory") {
751 752 753
                        # SPECIAL: virDomainGetMaxMemory and virNodeGetFreeMemory
                        #          return the actual value directly and 0 indicates
                        #          an error
754 755 756 757 758
                        $single_ret_by_ref = 0;
                        $single_ret_check = " == 0";
                    } else {
                        $single_ret_by_ref = 1;
                    }
759 760 761 762 763 764 765 766 767 768 769 770
                } 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";
771 772
                } elsif ($ret_member =~ m/^(\/)?\*/) {
                    # ignore comments
773 774
                } else {
                    die "unhandled type for return value: $ret_member";
775 776 777 778
                }
            }
        }

779
        # select struct type for multi-return-value functions
780
        if ($multi_ret) {
781 782 783 784
            if (!(defined $call->{ret_offset})) {
                die "multi-return-value without insert@<offset> annotation: $call->{ret}";
            }

785 786
            if (! @args_list) {
                push(@args_list, "priv->conn");
787 788
            }

789
            my $struct_name = $call->{ProcName};
790 791
            $struct_name =~ s/Get//;

792 793 794 795
            splice(@args_list, $call->{ret_offset}, 0, ("&tmp"));

            if ($call->{ProcName} eq "DomainBlockStats" ||
                $call->{ProcName} eq "DomainInterfaceStats") {
796 797 798
                # SPECIAL: virDomainBlockStats and virDomainInterfaceStats
                #          have a 'Struct' suffix on the actual struct name
                #          and take the struct size as additional argument
799
                $struct_name .= "Struct";
800
                splice(@args_list, $call->{ret_offset} + 1, 0, ("sizeof(tmp)"));
801 802 803 804 805
            }

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

806
        if ($call->{streamflag} ne "none") {
807
            splice(@args_list, $call->{streamoffset}, 0, ("st"));
808
            push(@free_list_on_error, "if (stream) {");
809 810 811 812
            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);");
813 814 815
            push(@free_list_on_error, "}");
        }

816
        # print functions signature
817 818 819 820 821
        print "static int $name(\n";
        print "    virNetServerPtr server ATTRIBUTE_UNUSED,\n";
        print "    virNetServerClientPtr client,\n";
        print "    virNetMessagePtr msg ATTRIBUTE_UNUSED,\n";
        print "    virNetMessageErrorPtr rerr";
822
        if ($argtype ne "void") {
823 824
            print ",\n    $argtype *args";
        }
825
        if ($rettype ne "void") {
826 827 828
            print ",\n    $rettype *ret";
        }
        print ")\n";
829 830 831 832 833

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

834 835 836
        foreach my $var (@vars_list) {
            print "    $var;\n";
        }
837
        print "    struct daemonClientPrivate *priv =\n";
838
        print "        virNetServerClientGetPrivateData(client);\n";
839

840
        if ($call->{streamflag} ne "none") {
841 842
            print "    virStreamPtr st = NULL;\n";
            print "    daemonClientStreamPtr stream = NULL;\n";
843 844
        }

845
        print "\n";
846
        print "    if (!priv->conn) {\n";
847
        print "        virReportError(VIR_ERR_INTERNAL_ERROR, \"%s\", _(\"connection not open\"));\n";
848 849 850 851
        print "        goto cleanup;\n";
        print "    }\n";
        print "\n";

852 853
        if ($single_ret_as_list) {
            print "    if (args->$single_ret_list_max_var > $single_ret_list_max_define) {\n";
854 855
            print "        virReportError(VIR_ERR_INTERNAL_ERROR,\n";
            print "                       \"%s\", _(\"max$single_ret_list_name > $single_ret_list_max_define\"));\n";
856 857 858 859 860
            print "        goto cleanup;\n";
            print "    }\n";
            print "\n";
        }

861 862
        print join("\n", @getters_list);

863 864 865
        if (@getters_list) {
            print "\n";
        }
866

867 868 869 870 871 872 873 874
        foreach my $optional (@optionals_list) {
            print "    $optional = args->$optional ? *args->$optional : NULL;\n";
        }

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

875
        if ($call->{streamflag} ne "none") {
876 877 878
            print "    if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)))\n";
            print "        goto cleanup;\n";
            print "\n";
879
            print "    if (!(stream = daemonCreateClientStream(client, st, remoteProgram, &msg->header)))\n";
880 881 882 883
            print "        goto cleanup;\n";
            print "\n";
        }

884
        if ($rettype eq "void") {
885
            print "    if (vir$call->{ProcName}(";
886 887 888 889
            print join(', ', @args_list);
            print ") < 0)\n";
            print "        goto cleanup;\n";
            print "\n";
890
        } elsif (!$multi_ret) {
891
            my $prefix = "";
892
            my $proc_name = $call->{ProcName};
893 894

            if (! @args_list) {
895
                push(@args_list, "priv->conn");
896 897
            }

898
            if ($structprefix eq "qemu" && $call->{ProcName} =~ /^Domain/) {
899
                $proc_name =~ s/^(Domain)/${1}Qemu/;
900
            }
901 902 903
            if ($structprefix eq "lxc" && $call->{ProcName} =~ /^Domain/) {
                $proc_name =~ s/^(Domain)/${1}Lxc/;
            }
904

905 906 907 908 909 910 911 912 913 914
            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";
            }

915 916 917
            if ($single_ret_by_ref) {
                print "    if (vir$prefix$proc_name(";
                print join(', ', @args_list);
918 919 920 921 922 923

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

                print ") < 0)\n";
924 925 926 927 928 929 930 931
            } 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";
932
        } else {
933
            print "    if (vir$call->{ProcName}(";
934 935 936 937
            print join(', ', @args_list);
            print ") < 0)\n";
            print "        goto cleanup;\n";
            print "\n";
938
        }
939

940
        if ($call->{streamflag} ne "none") {
941
            print "    if (daemonAddClientStream(client, stream, ";
942 943

            if ($call->{streamflag} eq "write") {
944
                print "false";
945
            } else {
946
                print "true";
947 948
            }

949 950 951 952 953
            print ") < 0)\n";
            print "        goto cleanup;\n";
            print "\n";
        }

954 955 956 957 958 959
        if (@prepare_ret_list) {
            print "    ";
            print join("\n    ", @prepare_ret_list);
            print "\n";
        }

960 961
        if (@ret_list) {
            print "    ";
962
            print join("\n    ", @ret_list);
963
            print "\n";
964 965 966 967 968
        }

        print "    rv = 0;\n";
        print "\n";
        print "cleanup:\n";
969 970 971 972 973 974 975 976 977 978 979 980 981
        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";
        }
982 983 984

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

985 986 987 988
        if (@free_list) {
            print "\n";
        }

989
        print "    return rv;\n";
990
        print "}\n\n\n\n";
991
    }
992 993 994 995 996 997 998 999 1000 1001 1002


    # 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++) {
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
        my ($comment, $name, $argtype, $arglen, $argfilter, $retlen, $retfilter, $priority);

        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";
        }
1025

1026 1027
    $priority = defined $calls[$id]->{priority} ? $calls[$id]->{priority} : 0;

1028
        print "{ $comment\n   ${name},\n   $arglen,\n   (xdrproc_t)$argfilter,\n   $retlen,\n   (xdrproc_t)$retfilter,\n   true,\n   $priority\n},\n";
1029 1030 1031
    }
    print "};\n";
    print "size_t ${structprefix}NProcs = ARRAY_CARDINALITY(${structprefix}Procs);\n";
1032
}
1033

1034
# Bodies for client functions ("remote_client_bodies.h").
1035
elsif ($mode eq "client") {
1036
    my %generate = map { $_ => 1 } @autogen;
1037 1038 1039 1040 1041 1042 1043 1044
    my @keys = sort (keys %calls);

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

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

1045 1046
        # skip procedures not on generate list
        next if ! exists($generate{$call->{ProcName}});
1047

1048 1049
        my $argtype = $call->{args};
        my $rettype = $call->{ret};
1050

1051
        # handle arguments to the function
1052 1053
        my @args_list = ();
        my @vars_list = ();
1054
        my @args_check_list = ();
1055
        my @setters_list = ();
1056
        my @setters_list2 = ();
1057
        my @free_list = ();
1058 1059
        my $priv_src = "conn";
        my $priv_name = "privateData";
1060
        my $call_args = "&args";
1061

1062
        if ($argtype eq "void") {
1063
            $call_args = "NULL";
1064
        } else {
1065
            push(@vars_list, "$argtype args");
1066 1067 1068 1069 1070

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

            # node device is special
1071 1072 1073
            if ($argtype =~ m/^remote_node_/ and
                !($argtype =~ m/^remote_node_device_lookup_by_name_/) and
                !($argtype =~ m/^remote_node_device_create_xml_/)) {
1074
                $has_node_device = 1;
1075
                $priv_name = "nodeDevicePrivateData";
1076 1077 1078 1079 1080 1081 1082 1083 1084
            }

            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;
1085
                    my $arg_name = $2;
1086
                    my $type_name = name_to_TypeName($name);
1087 1088 1089

                    if ($is_first_arg) {
                        if ($name eq "domain_snapshot") {
1090
                            $priv_src = "$arg_name->domain->conn";
1091
                        } else {
1092
                            $priv_src = "$arg_name->conn";
1093 1094 1095 1096 1097 1098 1099 1100 1101
                        }

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

1102 1103 1104 1105 1106
                    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);");
1107 1108 1109
                } elsif ($args_member =~ m/^remote_string (\S+);/) {
                    push(@args_list, "const char *$1");
                    push(@setters_list, "args.$1 = $1 ? (char **)&$1 : NULL;");
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
                } 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 });
1129 1130 1131
                } elsif ($args_member =~ m/^remote_nonnull_string (\S+);/) {
                    push(@args_list, "const char *$1");
                    push(@setters_list, "args.$1 = (char *)$1;");
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
                } 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");
1147

1148
                    if ($call->{ProcName} eq "SecretSetValue") {
1149
                        # SPECIAL: virSecretSetValue uses size_t instead of int
1150
                        push(@args_list, "size_t ${arg_name}len");
1151
                    } else {
1152
                        push(@args_list, "int ${arg_name}len");
1153 1154
                    }

1155 1156 1157 1158 1159 1160 1161 1162 1163
                    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");
1164 1165 1166
                    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 });
1167 1168 1169 1170 1171 1172
                } 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");
1173
                    push(@args_list, "int ${arg_name}len");
1174 1175 1176
                    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 });
1177 1178 1179 1180 1181 1182 1183
                } 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" .
                                         "    }");
1184
                    push(@free_list, "    remoteFreeTypedParameters(args.params.params_val, args.params.params_len);\n");
1185 1186
                } elsif ($args_member =~ m/^((?:unsigned )?int) (\S+);\s*\/\*\s*call-by-reference\s*\*\//) {
                    my $type_name = "$1 *";
1187 1188 1189 1190
                    my $arg_name = $2;

                    push(@args_list, "$type_name $arg_name");
                    push(@setters_list, "args.$arg_name = *$arg_name;");
1191 1192
                } elsif ($args_member =~ m/^((?:unsigned )?int) (\S+);/) {
                    my $type_name = $1;
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
                    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";
1205 1206 1207 1208
                    }

                    push(@args_list, "$type_name $arg_name");
                    push(@setters_list, "args.$arg_name = $arg_name;");
1209
                } elsif ($args_member =~ m/^(\/)?\*/) {
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
                    # 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;
            }
        }

1223 1224 1225 1226
        if (! @args_list) {
            push(@args_list, "virConnectPtr conn");
        }

1227 1228
        # fix priv_name for the NumOf* functions
        if ($priv_name eq "privateData" and
1229 1230 1231
            !($call->{ProcName} =~ m/(Domains|DomainSnapshot)/) and
            ($call->{ProcName} =~ m/NumOf(Defined|Domain)*(\S+)s/ or
             $call->{ProcName} =~ m/List(Defined|Domain)*(\S+)s/)) {
1232 1233 1234 1235 1236
            my $prefix = lc $2;
            $prefix =~ s/(pool|vol)$//;
            $priv_name = "${prefix}PrivateData";
        }

1237 1238
        # handle return values of the function
        my @ret_list = ();
1239
        my @ret_list2 = ();
1240 1241 1242
        my $call_ret = "&ret";
        my $single_ret_var = "int rv = -1";
        my $single_ret_type = "int";
1243 1244 1245 1246 1247
        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";
1248
        my $single_ret_cleanup = 0;
1249 1250
        my $multi_ret = 0;

1251
        if ($rettype ne "void" and
1252 1253 1254 1255
            scalar(@{$call->{ret_members}}) > 1) {
            $multi_ret = 1;
        }

1256
        if ($rettype eq "void") {
1257
            $call_ret = "NULL";
1258
        } else {
1259
            push(@vars_list, "$rettype ret");
1260 1261

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

1268
                        push(@ret_list, "memcpy(result->$3, ret.$3, sizeof(result->$3));");
1269 1270
                    } elsif ($ret_member =~ m/<\S+>;/ or $ret_member =~ m/\[\S+\];/) {
                        # just make all other array types fail
1271 1272
                        die "unhandled type for multi-return-value for " .
                            "procedure $call->{name}: $ret_member";
1273
                    } elsif ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+);/) {
1274 1275 1276 1277 1278 1279 1280
                        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;");
                        }
1281
                    } else {
1282 1283
                        die "unhandled type for multi-return-value for " .
                            "procedure $call->{name}: $ret_member";
1284
                    }
1285 1286 1287 1288 1289
                } 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";
1290 1291 1292 1293
                    $single_ret_as_list = 1;
                    $single_ret_list_name = $1;
                    $single_ret_list_max_var = "max$1";
                    $single_ret_list_max_define = $2;
1294 1295 1296
                } 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";
1297
                } elsif ($ret_member =~ m/^remote_nonnull_string (\S+);/) {
1298 1299 1300
                    push(@ret_list, "rv = ret.$1;");
                    $single_ret_var = "char *rv = NULL";
                    $single_ret_type = "char *";
1301 1302 1303 1304 1305
                } 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 *";
1306
                } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|node_device|interface|secret|nwfilter|domain_snapshot) (\S+);/) {
1307 1308
                    my $name = $1;
                    my $arg_name = $2;
1309
                    my $type_name = name_to_TypeName($name);
1310 1311

                    if ($name eq "node_device") {
1312
                        $priv_name = "nodeDevicePrivateData";
1313 1314 1315 1316 1317 1318
                    } elsif ($name =~ m/^storage_/) {
                        $priv_name = "storagePrivateData";
                    } elsif (!($name =~ m/^domain/)) {
                        $priv_name = "${name}PrivateData";
                    }

1319 1320 1321 1322 1323 1324 1325 1326
                    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";
1327
                    } else {
1328
                        if ($name eq "domain_snapshot") {
1329 1330 1331
                            my $dom = "$priv_src";
                            $dom =~ s/->conn//;
                            push(@ret_list, "rv = get_nonnull_$name($dom, ret.$arg_name);");
1332 1333 1334
                        } else {
                            push(@ret_list, "rv = get_nonnull_$name($priv_src, ret.$arg_name);");
                        }
1335

1336
                        push(@ret_list, "xdr_free((xdrproc_t)xdr_$rettype, (char *)&ret);");
1337 1338 1339
                        $single_ret_var = "vir${type_name}Ptr rv = NULL";
                        $single_ret_type = "vir${type_name}Ptr";
                    }
1340 1341 1342 1343 1344
                } 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" .
1345
                                     "                                         &$1,\n" .
1346 1347 1348 1349 1350 1351
                                     "                                         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";
1352
                } elsif ($ret_member =~ m/^int (\S+);/) {
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
                    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;");
                    }

1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
                    $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;");
1381 1382
                    $single_ret_var = "int rv = -1";
                    $single_ret_type = "int";
1383
                } elsif ($ret_member =~ m/^unsigned hyper (\S+);/) {
1384
                    my $ret_name = $1;
1385

1386
                    if ($call->{ProcName} =~ m/Get(Lib)?Version/) {
1387 1388
                        push(@args_list, "unsigned long *$ret_name");
                        push(@ret_list, "if ($ret_name) HYPER_TO_ULONG(*$ret_name, ret.$ret_name);");
1389 1390 1391
                        push(@ret_list, "rv = 0;");
                        $single_ret_var = "int rv = -1";
                        $single_ret_type = "int";
1392 1393
                    } elsif (hyper_to_long($call->{ProcName}, "ret", $ret_name)) {
                        push(@ret_list, "HYPER_TO_ULONG(rv, ret.$ret_name);");
1394 1395
                        $single_ret_var = "unsigned long rv = 0";
                        $single_ret_type = "unsigned long";
1396 1397 1398 1399
                    } else {
                        push(@ret_list, "rv = ret.$ret_name;");
                        $single_ret_var = "unsigned long long rv = 0";
                        $single_ret_type = "unsigned long long";
1400
                    }
1401 1402
                } elsif ($ret_member =~ m/^(\/)?\*/) {
                    # ignore comments
1403
                } else {
1404 1405
                    die "unhandled type for return value for procedure " .
                        "$call->{name}: $ret_member";
1406 1407 1408 1409
                }
            }
        }

1410 1411
        # select struct type for multi-return-value functions
        if ($multi_ret) {
1412 1413
            if (!(defined $call->{ret_offset})) {
                die "multi-return-value without insert@<offset> annotation: $call->{ret}";
1414 1415
            }

1416 1417
            my $struct_name = $call->{ProcName};
            $struct_name =~ s/Get//;
1418

1419
            splice(@args_list, $call->{ret_offset}, 0, ("vir${struct_name}Ptr result"));
1420 1421
        }

1422 1423 1424 1425
        if ($call->{streamflag} ne "none") {
            splice(@args_list, $call->{streamoffset}, 0, ("virStreamPtr st"));
        }

1426 1427
        # print function
        print "\n";
1428
        print "static $single_ret_type\n";
1429
        print "$structprefix$call->{ProcName}(";
1430 1431 1432 1433 1434

        print join(", ", @args_list);

        print ")\n";
        print "{\n";
1435
        print "    $single_ret_var;\n";
1436 1437 1438 1439 1440 1441
        print "    struct private_data *priv = $priv_src->$priv_name;\n";

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

1442 1443 1444 1445
        if ($single_ret_as_list) {
            print "    int i;\n";
        }

1446
        if ($call->{streamflag} ne "none") {
1447
            print "    virNetClientStreamPtr netst = NULL;\n";
1448 1449
        }

1450 1451 1452
        print "\n";
        print "    remoteDriverLock(priv);\n";

1453 1454
        if ($call->{streamflag} ne "none") {
            print "\n";
1455
            print "    if (!(netst = virNetClientStreamNew(priv->remoteProgram, $call->{constname}, priv->counter)))\n";
1456
            print "        goto done;\n";
1457
            print "\n";
1458
            print "    if (virNetClientAddStream(priv->client, netst) < 0) {\n";
1459
            print "        virObjectUnref(netst);\n";
1460
            print "        goto done;\n";
1461 1462
            print "    }";
            print "\n";
1463
            print "    st->driver = &remoteStreamDrv;\n";
1464
            print "    st->privateData = netst;\n";
1465 1466
        }

1467 1468 1469 1470 1471 1472 1473 1474 1475
        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";
        }

1476 1477 1478
        foreach my $args_check (@args_check_list) {
            print "\n";
            print "    if ($args_check->{arg} > $args_check->{limit}) {\n";
1479 1480 1481
            print "        virReportError(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";
1482 1483 1484 1485
            print "        goto done;\n";
            print "    }\n";
        }

1486 1487 1488
        if ($single_ret_as_list) {
            print "\n";
            print "    if ($single_ret_list_max_var > $single_ret_list_max_define) {\n";
1489 1490 1491
            print "        virReportError(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";
1492 1493 1494 1495
            print "        goto done;\n";
            print "    }\n";
        }

1496
        if (@setters_list) {
1497
            print "\n";
1498 1499 1500 1501 1502 1503 1504 1505 1506
            print "    ";
        }

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

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

1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517
        if (@setters_list2) {
            print "\n";
            print "    ";
        }

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

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

1518
        if ($rettype ne "void") {
1519
            print "\n";
1520
            print "    memset(&ret, 0, sizeof(ret));\n";
1521 1522
        }

1523 1524 1525 1526
        my $callflags = "0";
        if ($structprefix eq "qemu") {
            $callflags = "REMOTE_CALL_QEMU";
        }
1527 1528 1529
        if ($structprefix eq "lxc") {
            $callflags = "REMOTE_CALL_LXC";
        }
1530

1531
        print "\n";
1532
        print "    if (call($priv_src, priv, $callflags, $call->{constname},\n";
1533 1534
        print "             (xdrproc_t)xdr_$argtype, (char *)$call_args,\n";
        print "             (xdrproc_t)xdr_$rettype, (char *)$call_ret) == -1) {\n";
1535 1536

        if ($call->{streamflag} ne "none") {
1537
            print "        virNetClientRemoveStream(priv->client, netst);\n";
1538
            print "        virObjectUnref(netst);\n";
1539 1540
            print "        st->driver = NULL;\n";
            print "        st->privateData = NULL;\n";
1541 1542
        }

1543
        print "        goto done;\n";
1544
        print "    }\n";
1545
        print "\n";
1546

1547 1548
        if ($single_ret_as_list) {
            print "    if (ret.$single_ret_list_name.${single_ret_list_name}_len > $single_ret_list_max_var) {\n";
1549 1550 1551
            print "        virReportError(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";
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572
            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";
        }

1573 1574 1575 1576 1577 1578
        if (@ret_list2) {
            print "    ";
            print join("\n    ", @ret_list2);
            print "\n";
        }

1579 1580 1581 1582
        if (@ret_list) {
            print "    ";
            print join("\n    ", @ret_list);
            print "\n";
1583 1584
        }

1585
        if ($call->{ProcName} eq "DomainDestroy" ||
1586 1587
            $call->{ProcName} eq "DomainSave" ||
            $call->{ProcName} eq "DomainManagedSave") {
1588
            # SPECIAL: virDomain{Destroy|Save|ManagedSave} need to reset
1589
            # the domain id explicitly on success
1590 1591 1592
            print "    dom->id = -1;\n";
        }

1593
        if ($multi_ret or !@ret_list) {
1594 1595 1596
            print "    rv = 0;\n";
        }

1597
        if ($single_ret_as_list or $single_ret_cleanup) {
1598 1599
            print "\n";
            print "cleanup:\n";
1600
            print "    xdr_free((xdrproc_t)xdr_$call->{ret}, (char *)&ret);\n";
1601 1602
        }

1603 1604
        print "\n";
        print "done:\n";
1605 1606 1607

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

1608 1609 1610 1611 1612
        print "    remoteDriverUnlock(priv);\n";
        print "    return rv;\n";
        print "}\n";
    }
}