File Coverage

blib/lib/XDR/CallReply.pm
Criterion Covered Total %
statement 175 191 91.6
branch 67 96 69.7
condition 9 9 100.0
subroutine 22 22 100.0
pod 0 4 0.0
total 273 322 84.7


line stmt bran cond sub pod time code
1             # CallRep.pm - XDR RPC protocol helper functions
2             # Copyright (C) 2000 Mountain View Data, Inc.
3             # Written by Gordon Matzigkeit , 2000-12-15
4             #
5             # This file is part of Perl XDR.
6             #
7             # Perl XDR is free software; you can redistribute it and/or modify it
8             # under the terms of the GNU General Public License as published by
9             # the Free Software Foundation; either version 2 of the License, or
10             # (at your option) any later version.
11             #
12             # Perl XDR is distributed in the hope that it will be useful, but
13             # WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15             # General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License
18             # along with this program; if not, write to the Free Software
19             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20             # USA
21              
22             package XDR::CallReply;
23             # [guilt]
24             # [maint
25             # File: CallRep.pm
26             # Summary: XDR protocol helper functions
27             # Package: Perl XDR
28             # Owner: Mountain View Data, Inc.
29             # Years: 2000
30             # Author: Gordon Matzigkeit
31             # Contact:
32             # Date: 2000-12-15
33             # License: GPL]
34             # [clemency]
35              
36 1     1   573 use strict;
  1         1  
  1         34  
37 1     1   5 use Carp;
  1         1  
  1         1392  
38              
39              
40             sub new
41             {
42 1     1 0 42 my ($type, $prog, $vers) = @_;
43 1 50       5 $vers = 0 if (! $vers);
44 1 50       6 $prog = 0 if (! $prog);
45 1         8 bless { PROGRAM => $prog, VERSION => $vers, types => {} }, $type;
46             }
47              
48              
49             # Define a new type name.
50             sub typedef
51             {
52 2     2 0 15 my ($self, $type, $name, @args) = @_;
53 2 100       9 if ($type eq 'struct')
54             {
55 1         6 $self->{TYPES}->{$name} = [@args];
56             }
57             else
58             {
59 1         11 $self->{TYPES}->{$name} = $type;
60             }
61             }
62              
63              
64             # Encode a structure.
65             sub struct
66             {
67 18     18 0 28 my ($self, $type, $arg) = @_;
68 18         24 my $types = $self->{TYPES};
69 18         19 my $ret = '';
70              
71 18         17 my $tname = $type;
72 18   100     94 while (! ref ($type) && defined $self->{TYPES}->{$type})
73             {
74 12         15 $tname = $type;
75 12         47 $type = $self->{TYPES}->{$type};
76             }
77 18 100       64 if (UNIVERSAL::isa ($arg, 'XDR::Decode'))
    100          
78             {
79             # We're decoding.
80 9 100       16 if (ref $type)
81             {
82 3         3 my $i;
83 3         5 $ret = [];
84             # FIXME: Why is $type getting an undef pushed on it?
85 3         8 for ($i = 0; $i < @$type; $i ++)
86             {
87 6         9 my ($subtype) = $type->[$i];
88 6 50       17 ($subtype) = split (/\s+/, $subtype) if (! ref $subtype);
89 6         16 push @$ret, $self->struct ($subtype, $arg);
90             }
91             }
92             else
93             {
94 6         274 $ret = eval "\$arg->$type ()";
95 6 50       22 confess $@ if ($@);
96             }
97             }
98             elsif (ref $type)
99             {
100             # We're encoding a reference.
101 3 50       9 confess "\`$arg' is not an array reference" if (ref $arg ne 'ARRAY');
102 3 50       9 if (scalar (@$type) != scalar (@$arg))
103             {
104 0         0 warn "Received ", scalar (@$arg) + 1, " arguments for struct ",
105             $tname, ", not ", scalar (@$type) + 1, "\n";
106             }
107 3         4 my $i;
108             # FIXME: Why is $type getting an undef pushed on it?
109 3         11 for ($i = 0; $i < @$type; $i ++)
110             {
111 6         8 my ($subtype) = $type->[$i];
112 6 50       22 ($subtype) = split (/\s+/, $subtype) if (! ref $subtype);
113 6         30 $ret .= $self->struct ($subtype, $arg->[$i]);
114             }
115             }
116             else
117             {
118             # Encoding a scalar.
119 6         313 my ($sub) = eval "XDR::Encode::$type (\$arg)";
120 6 50       23 confess $@ if ($@);
121 6         10 $ret .= $sub;
122             }
123 18         79 return $ret;
124             }
125              
126              
127             # Define an RPC.
128             sub define
129             {
130 4     4 0 27 my ($self, $proc, $rets, $name, @args) = @_;
131 4         17 $self->{$proc} = [ $rets, $name, @args ];
132              
133             # Automatically determine the package name.
134 4         14 my ($pkg) = caller;
135 4         7 my $need_struct = 0;
136              
137             # Determine the types and build up a prototype.
138 4         5 my ($proto, $arg, $nargs, $i);
139 4         5 $nargs = 0;
140 4         29 for ($i = 0; $i <= $#args; $i ++)
141             {
142 6         27 my ($type, $name) = split (/ /, $args[$i]);
143 6         11 $proto .= "\$";
144 6 100       13 $arg .= ' . ' if ($i != 0);
145 6         10 my $tname = $type;
146 6   100     37 while (! ref $type && defined $self->{TYPES}->{$type})
147             {
148 3         5 $tname = $type;
149 3         15 $type = $self->{TYPES}->{$type};
150             }
151 6 100       17 if ($type ne 'void')
152             {
153 5 100       10 if (ref $type)
154             {
155 2         652 $arg .= "\$_xdr_callreply->struct ('$tname', \$_[$nargs])";
156             }
157             else
158             {
159 3         8 $arg .= "XDR::Encode::$type (\$_[$nargs])";
160             }
161 5         17 $nargs ++;
162             }
163             }
164              
165 4 100       12 $arg = "''" if ($nargs == 0);
166              
167 4         10 my ($type) = split (/ /, $rets);
168 4         5 my $tname = $type;
169 4   100     26 while (! ref $type && defined $self->{TYPES}->{$type})
170             {
171 1         2 $tname = $type;
172 1         5 $type = $self->{TYPES}->{$type};
173             }
174              
175 4         6 my ($res, $nres);
176 4         7 $nres = 0;
177 4 100       10 if ($type ne 'void')
178             {
179 2 100       7 if (ref $type)
180             {
181 1         4 $res = "\$_xdr_callreply->struct ('$tname', \$_[0])";
182             }
183             else
184             {
185 1         3 $res = "XDR::Encode::$type (\$_[0])";
186             }
187 2         3 $nres ++;
188             }
189 4 100       12 $res = "''" if ($nres == 0);
190              
191 4         10 my ($stub) = "package $pkg;\n";
192 4 100       52 if (! $pkg->can ('call'))
193             {
194 1         12 $stub .= "
195             # FIXME: It would be nice to close \$self within this eval, but
196             # perl documentation implies that it is impossible.
197             use vars qw(\$_xdr_callreply);
198             \$_xdr_callreply = \$self;
199              
200             use Carp;
201              
202             # Return a call packet generator.
203             sub call
204             {
205             my (\$type) = \@_;
206             return bless [ 0, 0 ], \$type;
207             }
208              
209              
210             # Return a reply packet generator.
211             sub reply
212             {
213             my (\$type) = \@_;
214             return bless [ 1 ], \$type;
215             }
216              
217              
218             # Return a new hook database.
219             sub hookdb
220             {
221             my (\$type) = \@_;
222             return bless [ \$_xdr_callreply, {}, {} ], \$type;
223             }
224              
225              
226             # Set up a hook for the given callrep.
227             use XDR ':vers';
228             use XDR::RPC;
229             sub hook
230             {
231             my (\$slf, \$proto, \$hook, \$xid) = \@_;
232             if (defined \$xid)
233             {
234             # We're binding a reply packet.
235             \$xid = XDR::RPC->decode (\$xid)->xid
236             if (\$xid !~ /^\d+\$/);
237             \$slf->[1]->{\$xid} = [\$hook, \@\$proto];
238             }
239             else
240             {
241             # We have a call packet.
242             \$slf->[2]->{\&RPCVERS}->{\$proto->[0]}->{\$proto->[1]}->{\$proto->[2]} =
243             [ \$hook, \@\$proto ];
244             }
245             }";
246             }
247              
248 4 100       28 if (! $pkg->can ('dispatch'))
249             {
250 1         5 $stub .= "
251              
252             use XDR ':all';
253             use XDR::RPC;
254             use XDR::Encode ':all';
255              
256             # Invoke the hook for a given RPC.
257             sub dispatch
258             {
259             my (\$slf, \$rpc, \@args) = \@_;
260              
261             # Implicitly convert buffers to RPC objects.
262             \$rpc = XDR::RPC->decode (\$rpc)
263             if (! UNIVERSAL::isa (\$rpc, 'XDR::RPC'));
264              
265             my (\$bad, \@proto, \$func);
266             if (\$rpc->can ('rpcvers'))
267             {
268             # Call packet.
269             my (\$binding) = \$slf->[2];
270             my \$t = \$binding->{\$rpc->rpcvers};
271             if (! defined \$t)
272             {
273             # Bad version.
274             my (\@vsns, \$low, \$high) = sort keys %\$binding;
275             \$low = \$vsns[0];
276             \$high = \$vsns[\$\#vsns - 1];
277             return reply_packet (\$rpc->xid, MSG_DENIED, RPC_MISMATCH,
278             unsigned (\$low) . unsigned (\$high));
279             }
280              
281             \$t = \$t->{\$rpc->prog};
282             if (! defined \$t)
283             {
284             # Bad program.
285             return reply_packet (\$rpc->xid, MSG_ACCEPTED, PROG_UNAVAIL);
286             }
287              
288             my (\$prog) = \$t;
289             \$t = \$t->{\$rpc->vers};
290             if (! defined \$t)
291             {
292             # Bad version.
293             my (\@vsns, \$low, \$high) = sort keys \%{\$prog};
294             \$low = \$vsns[0];
295             \$high = \$vsns[\$\#vsns - 1];
296             return reply_packet (\$rpc->xid, MSG_ACCEPTED, PROG_MISMATCH,
297             unsigned (\$low) . unsigned (\$high));
298             }
299              
300             \$t = \$t->{\$rpc->proc};
301             if (! defined \$t)
302             {
303             # Bad procedure.
304             return reply_packet (\$rpc->xid, MSG_ACCEPTED, PROC_UNAVAIL);
305             }
306              
307             my (\$hook, \$progt, \$vers, \$proc,
308             \$ret, \$name, \@pto)
309             = @\$t;
310              
311             # Invoke the reply hook with the correct arguments.
312             \@proto = \@pto;
313             \$bad = reply_packet (\$rpc->xid, MSG_ACCEPTED, GARBAGE_ARGS);
314             \$func = \$hook;
315             }
316             else
317             {
318             # Reply packet.
319             my (\$hook, \$prog, \$vers, \$proc, \$ret) =
320             \@{\$slf->[1]->{\$rpc->xid}};
321              
322             # Not waiting for that reply xid.
323             return \$bad if (! defined \$hook);
324              
325             # Reply hooks are one-shot.
326             delete \$slf->[1]->{\$rpc->xid};
327              
328             \$bad = 1;
329             \@proto = (\$ret);
330             \$func = \$hook;
331             }
332              
333             # Call the hook.
334             push \@args, eval '\$rpc->args (\$_xdr_callreply, \@proto)';
335             return \$bad if \$@;
336              
337             return &\$func (\$rpc, \@args);
338             }
339             ";
340             }
341              
342             # Actually define the stub.
343 4         33 $stub .= "
344              
345              
346             sub $name # ($proto)
347             {
348             my (\$slf) = shift;
349             my \$callrep = \$slf->[0];
350             if (\$callrep == 0)
351             {
352             # Return the call packet.
353             carp '$pkg->call->$name received ', \$#_ + 1,
354             \" arguments instead of $nargs\\n\"
355             if (\$#_ != $nargs - 1);
356             call_packet (\$slf->[1] ++, $proc, $arg,
357             \$_xdr_callreply->{VERSION},
358             \$_xdr_callreply->{PROGRAM});
359             }
360             elsif (\$callrep == 1)
361             {
362             # Return the reply arguments.
363             carp '$pkg->reply->$name received ', \$#_ + 1,
364             \" results instead of $nres\\n\"
365             if (\$#_ != $nres - 1);
366             $res;
367             }
368             else
369             {
370             # Return the callrep specification.
371             [\$callrep->{PROGRAM}, \$callrep->{VERSION}, $proc,
372             \@{\$callrep->{$proc}}];
373             }
374             }";
375              
376             # warn "FIXME!\n", $stub;
377 1 50   1   9 eval $stub;
  1 50   1   2  
  1 100   1   66  
  1 100   1   14  
  1 50   1   2  
  1 0   1   268  
  1 100   1   6  
  1 50   4   2  
  1 50   2   169  
  1 0   2   7  
  1 100   4   1  
  1 50   1   201  
  1 50   6   7  
  1 50   6   2  
  1 100   2   329  
  1 100   1   6  
  1 50       2  
  1 50       32  
  1 50       6  
  1 50       2  
  1 50       895  
  4 50       820  
  4 100       101  
  4 50       6  
  4 50       14  
  1 100       6  
  1         8  
  1         3  
  1         4  
  2         3  
  2         50  
  2         40  
  2         4  
  2         9  
  1         7  
  1         6  
  0         0  
  0         0  
  1         3  
  1         25  
  2         46  
  2         7  
  2         8  
  1         4  
  1         6  
  0         0  
  0         0  
  1         2  
  1         7  
  4         98  
  4         4  
  4         11  
  1         6  
  1         4  
  1         5  
  1         4  
  2         5  
  2         57  
  1         2  
  1         4  
  6         130  
  6         55  
  6         10  
  6         37  
  4         7  
  4         12  
  4         12  
  0         0  
  0         0  
  0         0  
  0         0  
  4         12  
  4         12  
  0         0  
  4         6  
  4         10  
  4         23  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         13  
  4         14  
  0         0  
  4         16  
  4         9  
  4         15  
  4         15  
  2         3  
  2         9  
  2         6  
  2         7  
  2         4  
  2         4  
  2         3  
  6         416  
  6         24  
  6         24  
  6         13  
  6         11  
  2         14  
  2         13  
  4         32  
  2         56  
  2         8  
  1         46  
  1         4  
378 4 50       29 croak $@ if $@;
379             }
380              
381             1;