1 | .\" |
---|
2 | .\" Must use -- tbl and pic -- with this one |
---|
3 | .\" |
---|
4 | .\" @(#)rpc.prog.ms 2.3 88/08/11 4.0 RPCSRC |
---|
5 | .de BT |
---|
6 | .if \\n%=1 .tl ''- % -'' |
---|
7 | .. |
---|
8 | .IX "Network Programming" "" "" "" PAGE MAJOR |
---|
9 | .nr OF 0 |
---|
10 | .ND |
---|
11 | .\" prevent excess underlining in nroff |
---|
12 | .if n .fp 2 R |
---|
13 | .OH 'Remote Procedure Call Programming Guide''Page %' |
---|
14 | .EH 'Page %''Remote Procedure Call Programming Guide' |
---|
15 | .SH |
---|
16 | \&Remote Procedure Call Programming Guide |
---|
17 | .nr OF 1 |
---|
18 | .IX "RPC Programming Guide" |
---|
19 | .LP |
---|
20 | This document assumes a working knowledge of network theory. It is |
---|
21 | intended for programmers who wish to write network applications using |
---|
22 | remote procedure calls (explained below), and who want to understand |
---|
23 | the RPC mechanisms usually hidden by the |
---|
24 | .I rpcgen(1) |
---|
25 | protocol compiler. |
---|
26 | .I rpcgen |
---|
27 | is described in detail in the previous chapter, the |
---|
28 | .I "\fBrpcgen\fP \fIProgramming Guide\fP". |
---|
29 | .SH |
---|
30 | Note: |
---|
31 | .I |
---|
32 | .IX rpcgen "" \fIrpcgen\fP |
---|
33 | Before attempting to write a network application, or to convert an |
---|
34 | existing non-network application to run over the network, you may want to |
---|
35 | understand the material in this chapter. However, for most applications, |
---|
36 | you can circumvent the need to cope with the details presented here by using |
---|
37 | .I rpcgen . |
---|
38 | The |
---|
39 | .I "Generating XDR Routines" |
---|
40 | section of that chapter contains the complete source for a working RPC |
---|
41 | service\(ema remote directory listing service which uses |
---|
42 | .I rpcgen |
---|
43 | to generate XDR routines as well as client and server stubs. |
---|
44 | .LP |
---|
45 | .LP |
---|
46 | What are remote procedure calls? Simply put, they are the high-level |
---|
47 | communications paradigm used in the operating system. |
---|
48 | RPC presumes the existence of |
---|
49 | low-level networking mechanisms (such as TCP/IP and UDP/IP), and upon them |
---|
50 | it implements a logical client to server communications system designed |
---|
51 | specifically for the support of network applications. With RPC, the client |
---|
52 | makes a procedure call to send a data packet to the server. When the |
---|
53 | packet arrives, the server calls a dispatch routine, performs whatever |
---|
54 | service is requested, sends back the reply, and the procedure call returns |
---|
55 | to the client. |
---|
56 | .NH 0 |
---|
57 | \&Layers of RPC |
---|
58 | .IX "layers of RPC" |
---|
59 | .IX "RPC" "layers" |
---|
60 | .LP |
---|
61 | The RPC interface can be seen as being divided into three layers.\** |
---|
62 | .FS |
---|
63 | For a complete specification of the routines in the remote procedure |
---|
64 | call Library, see the |
---|
65 | .I rpc(3N) |
---|
66 | manual page. |
---|
67 | .FE |
---|
68 | .LP |
---|
69 | .I "The Highest Layer:" |
---|
70 | .IX RPC "The Highest Layer" |
---|
71 | The highest layer is totally transparent to the operating system, |
---|
72 | machine and network upon which is is run. It's probably best to |
---|
73 | think of this level as a way of |
---|
74 | .I using |
---|
75 | RPC, rather than as |
---|
76 | a \fIpart of\fP RPC proper. Programmers who write RPC routines |
---|
77 | should (almost) always make this layer available to others by way |
---|
78 | of a simple C front end that entirely hides the networking. |
---|
79 | .LP |
---|
80 | To illustrate, at this level a program can simply make a call to |
---|
81 | .I rnusers (), |
---|
82 | a C routine which returns the number of users on a remote machine. |
---|
83 | The user is not explicitly aware of using RPC \(em they simply |
---|
84 | call a procedure, just as they would call |
---|
85 | .I malloc() . |
---|
86 | .LP |
---|
87 | .I "The Middle Layer:" |
---|
88 | .IX RPC "The Middle Layer" |
---|
89 | The middle layer is really \*QRPC proper.\*U Here, the user doesn't |
---|
90 | need to consider details about sockets, the UNIX system, or other low-level |
---|
91 | implementation mechanisms. They simply make remote procedure calls |
---|
92 | to routines on other machines. The selling point here is simplicity. |
---|
93 | It's this layer that allows RPC to pass the \*Qhello world\*U test \(em |
---|
94 | simple things should be simple. The middle-layer routines are used |
---|
95 | for most applications. |
---|
96 | .LP |
---|
97 | RPC calls are made with the system routines |
---|
98 | .I registerrpc() |
---|
99 | .I callrpc() |
---|
100 | and |
---|
101 | .I svc_run (). |
---|
102 | The first two of these are the most fundamental: |
---|
103 | .I registerrpc() |
---|
104 | obtains a unique system-wide procedure-identification number, and |
---|
105 | .I callrpc() |
---|
106 | actually executes a remote procedure call. At the middle level, a |
---|
107 | call to |
---|
108 | .I rnusers() |
---|
109 | is implemented by way of these two routines. |
---|
110 | .LP |
---|
111 | The middle layer is unfortunately rarely used in serious programming |
---|
112 | due to its inflexibility (simplicity). It does not allow timeout |
---|
113 | specifications or the choice of transport. It allows no UNIX |
---|
114 | process control or flexibility in case of errors. It doesn't support |
---|
115 | multiple kinds of call authentication. The programmer rarely needs |
---|
116 | all these kinds of control, but one or two of them is often necessary. |
---|
117 | .LP |
---|
118 | .I "The Lowest Layer:" |
---|
119 | .IX RPC "The Lowest Layer" |
---|
120 | The lowest layer does allow these details to be controlled by the |
---|
121 | programmer, and for that reason it is often necessary. Programs |
---|
122 | written at this level are also most efficient, but this is rarely a |
---|
123 | real issue \(em since RPC clients and servers rarely generate |
---|
124 | heavy network loads. |
---|
125 | .LP |
---|
126 | Although this document only discusses the interface to C, |
---|
127 | remote procedure calls can be made from any language. |
---|
128 | Even though this document discusses RPC |
---|
129 | when it is used to communicate |
---|
130 | between processes on different machines, |
---|
131 | it works just as well for communication |
---|
132 | between different processes on the same machine. |
---|
133 | .br |
---|
134 | .KS |
---|
135 | .NH 2 |
---|
136 | \&The RPC Paradigm |
---|
137 | .IX RPC paradigm |
---|
138 | .LP |
---|
139 | Here is a diagram of the RPC paradigm: |
---|
140 | .LP |
---|
141 | \fBFigure 1-1\fI Network Communication with the Remote Reocedure Call\fR |
---|
142 | .LP |
---|
143 | .PS |
---|
144 | L1: arrow down 1i "client " rjust "program " rjust |
---|
145 | L2: line right 1.5i "\fIcallrpc\fP" "function" |
---|
146 | move up 1.5i; line dotted down 6i; move up 4.5i |
---|
147 | arrow right 1i |
---|
148 | L3: arrow down 1i "invoke " rjust "service " rjust |
---|
149 | L4: arrow right 1.5i "call" "service" |
---|
150 | L5: arrow down 1i " service" ljust " executes" ljust |
---|
151 | L6: arrow left 1.5i "\fIreturn\fP" "answer" |
---|
152 | L7: arrow down 1i "request " rjust "completed " rjust |
---|
153 | L8: line left 1i |
---|
154 | arrow left 1.5i "\fIreturn\fP" "reply" |
---|
155 | L9: arrow down 1i "program " rjust "continues " rjust |
---|
156 | line dashed down from L2 to L9 |
---|
157 | line dashed down from L4 to L7 |
---|
158 | line dashed up 1i from L3 "service " rjust "daemon " rjust |
---|
159 | arrow dashed down 1i from L8 |
---|
160 | move right 1i from L3 |
---|
161 | box invis "Machine B" |
---|
162 | move left 1.2i from L2; move down |
---|
163 | box invis "Machine A" |
---|
164 | .PE |
---|
165 | .KE |
---|
166 | .KS |
---|
167 | .NH 1 |
---|
168 | \&Higher Layers of RPC |
---|
169 | .NH 2 |
---|
170 | \&Highest Layer |
---|
171 | .IX "highest layer of RPC" |
---|
172 | .IX RPC "highest layer" |
---|
173 | .LP |
---|
174 | Imagine you're writing a program that needs to know |
---|
175 | how many users are logged into a remote machine. |
---|
176 | You can do this by calling the RPC library routine |
---|
177 | .I rnusers() |
---|
178 | as illustrated below: |
---|
179 | .ie t .DS |
---|
180 | .el .DS L |
---|
181 | .ft CW |
---|
182 | #include <stdio.h> |
---|
183 | |
---|
184 | main(argc, argv) |
---|
185 | int argc; |
---|
186 | char **argv; |
---|
187 | { |
---|
188 | int num; |
---|
189 | |
---|
190 | if (argc != 2) { |
---|
191 | fprintf(stderr, "usage: rnusers hostname\en"); |
---|
192 | exit(1); |
---|
193 | } |
---|
194 | if ((num = rnusers(argv[1])) < 0) { |
---|
195 | fprintf(stderr, "error: rnusers\en"); |
---|
196 | exit(-1); |
---|
197 | } |
---|
198 | printf("%d users on %s\en", num, argv[1]); |
---|
199 | exit(0); |
---|
200 | } |
---|
201 | .DE |
---|
202 | .KE |
---|
203 | RPC library routines such as |
---|
204 | .I rnusers() |
---|
205 | are in the RPC services library |
---|
206 | .I librpcsvc.a |
---|
207 | Thus, the program above should be compiled with |
---|
208 | .DS |
---|
209 | .ft CW |
---|
210 | % cc \fIprogram.c -lrpcsvc\fP |
---|
211 | .DE |
---|
212 | .I rnusers (), |
---|
213 | like the other RPC library routines, is documented in section 3R |
---|
214 | of the |
---|
215 | .I "System Interface Manual for the Sun Workstation" , |
---|
216 | the same section which documents the standard Sun RPC services. |
---|
217 | .IX "RPC Services" |
---|
218 | See the |
---|
219 | .I intro(3R) |
---|
220 | manual page for an explanation of the documentation strategy |
---|
221 | for these services and their RPC protocols. |
---|
222 | .LP |
---|
223 | Here are some of the RPC service library routines available to the |
---|
224 | C programmer: |
---|
225 | .LP |
---|
226 | \fBTable 3-3\fI RPC Service Library Routines\RP |
---|
227 | .TS |
---|
228 | box tab (&) ; |
---|
229 | cfI cfI |
---|
230 | lfL l . |
---|
231 | Routine&Description |
---|
232 | _ |
---|
233 | .sp.5 |
---|
234 | rnusers&Return number of users on remote machine |
---|
235 | rusers&Return information about users on remote machine |
---|
236 | havedisk&Determine if remote machine has disk |
---|
237 | rstats&Get performance data from remote kernel |
---|
238 | rwall&Write to specified remote machines |
---|
239 | yppasswd&Update user password in Yellow Pages |
---|
240 | .TE |
---|
241 | .LP |
---|
242 | Other RPC services \(em for example |
---|
243 | .I ether() |
---|
244 | .I mount |
---|
245 | .I rquota() |
---|
246 | and |
---|
247 | .I spray |
---|
248 | \(em are not available to the C programmer as library routines. |
---|
249 | They do, however, |
---|
250 | have RPC program numbers so they can be invoked with |
---|
251 | .I callrpc() |
---|
252 | which will be discussed in the next section. Most of them also |
---|
253 | have compilable |
---|
254 | .I rpcgen(1) |
---|
255 | protocol description files. (The |
---|
256 | .I rpcgen |
---|
257 | protocol compiler radically simplifies the process of developing |
---|
258 | network applications. |
---|
259 | See the \fBrpcgen\fI Programming Guide\fR |
---|
260 | for detailed information about |
---|
261 | .I rpcgen |
---|
262 | and |
---|
263 | .I rpcgen |
---|
264 | protocol description files). |
---|
265 | .KS |
---|
266 | .NH 2 |
---|
267 | \&Intermediate Layer |
---|
268 | .IX "intermediate layer of RPC" |
---|
269 | .IX "RPC" "intermediate layer" |
---|
270 | .LP |
---|
271 | The simplest interface, which explicitly makes RPC calls, uses the |
---|
272 | functions |
---|
273 | .I callrpc() |
---|
274 | and |
---|
275 | .I registerrpc() |
---|
276 | Using this method, the number of remote users can be gotten as follows: |
---|
277 | .ie t .DS |
---|
278 | .el .DS L |
---|
279 | #include <stdio.h> |
---|
280 | #include <rpc/rpc.h> |
---|
281 | #include <utmp.h> |
---|
282 | #include <rpcsvc/rusers.h> |
---|
283 | |
---|
284 | main(argc, argv) |
---|
285 | int argc; |
---|
286 | char **argv; |
---|
287 | { |
---|
288 | unsigned long nusers; |
---|
289 | int stat; |
---|
290 | |
---|
291 | if (argc != 2) { |
---|
292 | fprintf(stderr, "usage: nusers hostname\en"); |
---|
293 | exit(-1); |
---|
294 | } |
---|
295 | if (stat = callrpc(argv[1], |
---|
296 | RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, |
---|
297 | xdr_void, 0, xdr_u_long, &nusers) != 0) { |
---|
298 | clnt_perrno(stat); |
---|
299 | exit(1); |
---|
300 | } |
---|
301 | printf("%d users on %s\en", nusers, argv[1]); |
---|
302 | exit(0); |
---|
303 | } |
---|
304 | .DE |
---|
305 | .KE |
---|
306 | Each RPC procedure is uniquely defined by a program number, |
---|
307 | version number, and procedure number. The program number |
---|
308 | specifies a group of related remote procedures, each of |
---|
309 | which has a different procedure number. Each program also |
---|
310 | has a version number, so when a minor change is made to a |
---|
311 | remote service (adding a new procedure, for example), a new |
---|
312 | program number doesn't have to be assigned. When you want |
---|
313 | to call a procedure to find the number of remote users, you |
---|
314 | look up the appropriate program, version and procedure numbers |
---|
315 | in a manual, just as you look up the name of a memory allocator |
---|
316 | when you want to allocate memory. |
---|
317 | .LP |
---|
318 | The simplest way of making remote procedure calls is with the the RPC |
---|
319 | library routine |
---|
320 | .I callrpc() |
---|
321 | It has eight parameters. The first is the name of the remote server |
---|
322 | machine. The next three parameters are the program, version, and procedure |
---|
323 | numbers\(emtogether they identify the procedure to be called. |
---|
324 | The fifth and sixth parameters are an XDR filter and an argument to |
---|
325 | be encoded and passed to the remote procedure. |
---|
326 | The final two parameters are a filter for decoding the results |
---|
327 | returned by the remote procedure and a pointer to the place where |
---|
328 | the procedure's results are to be stored. Multiple arguments and |
---|
329 | results are handled by embedding them in structures. If |
---|
330 | .I callrpc() |
---|
331 | completes successfully, it returns zero; else it returns a nonzero |
---|
332 | value. The return codes (of type |
---|
333 | .IX "enum clnt_stat (in RPC programming)" "" "\fIenum clnt_stat\fP (in RPC programming)" |
---|
334 | cast into an integer) are found in |
---|
335 | .I <rpc/clnt.h> . |
---|
336 | .LP |
---|
337 | Since data types may be represented differently on different machines, |
---|
338 | .I callrpc() |
---|
339 | needs both the type of the RPC argument, as well as |
---|
340 | a pointer to the argument itself (and similarly for the result). For |
---|
341 | .I RUSERSPROC_NUM , |
---|
342 | the return value is an |
---|
343 | .I "unsigned long" |
---|
344 | so |
---|
345 | .I callrpc() |
---|
346 | has |
---|
347 | .I xdr_u_long() |
---|
348 | as its first return parameter, which says |
---|
349 | that the result is of type |
---|
350 | .I "unsigned long" |
---|
351 | and |
---|
352 | .I &nusers |
---|
353 | as its second return parameter, |
---|
354 | which is a pointer to where the long result will be placed. Since |
---|
355 | .I RUSERSPROC_NUM |
---|
356 | takes no argument, the argument parameter of |
---|
357 | .I callrpc() |
---|
358 | is |
---|
359 | .I xdr_void (). |
---|
360 | .LP |
---|
361 | After trying several times to deliver a message, if |
---|
362 | .I callrpc() |
---|
363 | gets no answer, it returns with an error code. |
---|
364 | The delivery mechanism is UDP, |
---|
365 | which stands for User Datagram Protocol. |
---|
366 | Methods for adjusting the number of retries |
---|
367 | or for using a different protocol require you to use the lower |
---|
368 | layer of the RPC library, discussed later in this document. |
---|
369 | The remote server procedure |
---|
370 | corresponding to the above might look like this: |
---|
371 | .ie t .DS |
---|
372 | .el .DS L |
---|
373 | .ft CW |
---|
374 | .ft CW |
---|
375 | char * |
---|
376 | nuser(indata) |
---|
377 | char *indata; |
---|
378 | { |
---|
379 | unsigned long nusers; |
---|
380 | |
---|
381 | .ft I |
---|
382 | /* |
---|
383 | * Code here to compute the number of users |
---|
384 | * and place result in variable \fInusers\fP. |
---|
385 | */ |
---|
386 | .ft CW |
---|
387 | return((char *)&nusers); |
---|
388 | } |
---|
389 | .DE |
---|
390 | .LP |
---|
391 | It takes one argument, which is a pointer to the input |
---|
392 | of the remote procedure call (ignored in our example), |
---|
393 | and it returns a pointer to the result. |
---|
394 | In the current version of C, |
---|
395 | character pointers are the generic pointers, |
---|
396 | so both the input argument and the return value are cast to |
---|
397 | .I "char *" . |
---|
398 | .LP |
---|
399 | Normally, a server registers all of the RPC calls it plans |
---|
400 | to handle, and then goes into an infinite loop waiting to service requests. |
---|
401 | In this example, there is only a single procedure |
---|
402 | to register, so the main body of the server would look like this: |
---|
403 | .ie t .DS |
---|
404 | .el .DS L |
---|
405 | .ft CW |
---|
406 | #include <stdio.h> |
---|
407 | #include <rpc/rpc.h> |
---|
408 | #include <utmp.h> |
---|
409 | #include <rpcsvc/rusers.h> |
---|
410 | |
---|
411 | char *nuser(); |
---|
412 | |
---|
413 | main() |
---|
414 | { |
---|
415 | registerrpc(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, |
---|
416 | nuser, xdr_void, xdr_u_long); |
---|
417 | svc_run(); /* \fINever returns\fP */ |
---|
418 | fprintf(stderr, "Error: svc_run returned!\en"); |
---|
419 | exit(1); |
---|
420 | } |
---|
421 | .DE |
---|
422 | .LP |
---|
423 | The |
---|
424 | .I registerrpc() |
---|
425 | routine registers a C procedure as corresponding to a |
---|
426 | given RPC procedure number. The first three parameters, |
---|
427 | .I RUSERPROG , |
---|
428 | .I RUSERSVERS , |
---|
429 | and |
---|
430 | .I RUSERSPROC_NUM |
---|
431 | are the program, version, and procedure numbers |
---|
432 | of the remote procedure to be registered; |
---|
433 | .I nuser() |
---|
434 | is the name of the local procedure that implements the remote |
---|
435 | procedure; and |
---|
436 | .I xdr_void() |
---|
437 | and |
---|
438 | .I xdr_u_long() |
---|
439 | are the XDR filters for the remote procedure's arguments and |
---|
440 | results, respectively. (Multiple arguments or multiple results |
---|
441 | are passed as structures). |
---|
442 | .LP |
---|
443 | Only the UDP transport mechanism can use |
---|
444 | .I registerrpc() |
---|
445 | thus, it is always safe in conjunction with calls generated by |
---|
446 | .I callrpc() . |
---|
447 | .SH |
---|
448 | .IX "UDP 8K warning" |
---|
449 | Warning: the UDP transport mechanism can only deal with |
---|
450 | arguments and results less than 8K bytes in length. |
---|
451 | .LP |
---|
452 | .LP |
---|
453 | After registering the local procedure, the server program's |
---|
454 | main procedure calls |
---|
455 | .I svc_run (), |
---|
456 | the RPC library's remote procedure dispatcher. It is this |
---|
457 | function that calls the remote procedures in response to RPC |
---|
458 | call messages. Note that the dispatcher takes care of decoding |
---|
459 | remote procedure arguments and encoding results, using the XDR |
---|
460 | filters specified when the remote procedure was registered. |
---|
461 | .NH 2 |
---|
462 | \&Assigning Program Numbers |
---|
463 | .IX "program number assignment" |
---|
464 | .IX "assigning program numbers" |
---|
465 | .LP |
---|
466 | Program numbers are assigned in groups of |
---|
467 | .I 0x20000000 |
---|
468 | according to the following chart: |
---|
469 | .DS |
---|
470 | .ft CW |
---|
471 | 0x0 - 0x1fffffff \fRDefined by Sun\fP |
---|
472 | 0x20000000 - 0x3fffffff \fRDefined by user\fP |
---|
473 | 0x40000000 - 0x5fffffff \fRTransient\fP |
---|
474 | 0x60000000 - 0x7fffffff \fRReserved\fP |
---|
475 | 0x80000000 - 0x9fffffff \fRReserved\fP |
---|
476 | 0xa0000000 - 0xbfffffff \fRReserved\fP |
---|
477 | 0xc0000000 - 0xdfffffff \fRReserved\fP |
---|
478 | 0xe0000000 - 0xffffffff \fRReserved\fP |
---|
479 | .ft R |
---|
480 | .DE |
---|
481 | Sun Microsystems administers the first group of numbers, which |
---|
482 | should be identical for all Sun customers. If a customer |
---|
483 | develops an application that might be of general interest, that |
---|
484 | application should be given an assigned number in the first |
---|
485 | range. The second group of numbers is reserved for specific |
---|
486 | customer applications. This range is intended primarily for |
---|
487 | debugging new programs. The third group is reserved for |
---|
488 | applications that generate program numbers dynamically. The |
---|
489 | final groups are reserved for future use, and should not be |
---|
490 | used. |
---|
491 | .LP |
---|
492 | To register a protocol specification, send a request by network |
---|
493 | mail to |
---|
494 | .I rpc@sun |
---|
495 | or write to: |
---|
496 | .DS |
---|
497 | RPC Administrator |
---|
498 | Sun Microsystems |
---|
499 | 2550 Garcia Ave. |
---|
500 | Mountain View, CA 94043 |
---|
501 | .DE |
---|
502 | Please include a compilable |
---|
503 | .I rpcgen |
---|
504 | \*Q.x\*U file describing your protocol. |
---|
505 | You will be given a unique program number in return. |
---|
506 | .IX RPC administration |
---|
507 | .IX administration "of RPC" |
---|
508 | .LP |
---|
509 | The RPC program numbers and protocol specifications |
---|
510 | of standard Sun RPC services can be |
---|
511 | found in the include files in |
---|
512 | .I "/usr/include/rpcsvc" . |
---|
513 | These services, however, constitute only a small subset |
---|
514 | of those which have been registered. The complete list of |
---|
515 | registered programs, as of the time when this manual was |
---|
516 | printed, is: |
---|
517 | .LP |
---|
518 | \fBTable 3-2\fI RPC Registered Programs\fR |
---|
519 | .TS H |
---|
520 | box tab (&) ; |
---|
521 | lfBI lfBI lfBI |
---|
522 | lfL lfL lfI . |
---|
523 | RPC Number&Program&Description |
---|
524 | _ |
---|
525 | .TH |
---|
526 | .sp.5 |
---|
527 | 100000&PMAPPROG&portmapper |
---|
528 | 100001&RSTATPROG&remote stats |
---|
529 | 100002&RUSERSPROG&remote users |
---|
530 | 100003&NFSPROG&nfs |
---|
531 | 100004&YPPROG&Yellow Pages |
---|
532 | 100005&MOUNTPROG&mount demon |
---|
533 | 100006&DBXPROG&remote dbx |
---|
534 | 100007&YPBINDPROG&yp binder |
---|
535 | 100008&WALLPROG&shutdown msg |
---|
536 | 100009&YPPASSWDPROG&yppasswd server |
---|
537 | 100010ÐERSTATPROGðer stats |
---|
538 | 100011&RQUOTAPROG&disk quotas |
---|
539 | 100012&SPRAYPROG&spray packets |
---|
540 | 100013&IBM3270PROG&3270 mapper |
---|
541 | 100014&IBMRJEPROG&RJE mapper |
---|
542 | 100015&SELNSVCPROG&selection service |
---|
543 | 100016&RDATABASEPROG&remote database access |
---|
544 | 100017&REXECPROG&remote execution |
---|
545 | 100018&ALICEPROG&Alice Office Automation |
---|
546 | 100019&SCHEDPROG&scheduling service |
---|
547 | 100020&LOCKPROG&local lock manager |
---|
548 | 100021&NETLOCKPROG&network lock manager |
---|
549 | 100022&X25PROG&x.25 inr protocol |
---|
550 | 100023&STATMON1PROG&status monitor 1 |
---|
551 | 100024&STATMON2PROG&status monitor 2 |
---|
552 | 100025&SELNLIBPROG&selection library |
---|
553 | 100026&BOOTPARAMPROG&boot parameters service |
---|
554 | 100027&MAZEPROG&mazewars game |
---|
555 | 100028&YPUPDATEPROG&yp update |
---|
556 | 100029&KEYSERVEPROG&key server |
---|
557 | 100030&SECURECMDPROG&secure login |
---|
558 | 100031&NETFWDIPROG&nfs net forwarder init |
---|
559 | 100032&NETFWDTPROG&nfs net forwarder trans |
---|
560 | 100033&SUNLINKMAP_PROG&sunlink MAP |
---|
561 | 100034&NETMONPROG&network monitor |
---|
562 | 100035&DBASEPROG&lightweight database |
---|
563 | 100036&PWDAUTHPROG&password authorization |
---|
564 | 100037&TFSPROG&translucent file svc |
---|
565 | 100038&NSEPROG&nse server |
---|
566 | 100039&NSE_ACTIVATE_PROG&nse activate daemon |
---|
567 | .sp .2i |
---|
568 | 150001&PCNFSDPROG&pc passwd authorization |
---|
569 | .sp .2i |
---|
570 | 200000&PYRAMIDLOCKINGPROG&Pyramid-locking |
---|
571 | 200001&PYRAMIDSYS5&Pyramid-sys5 |
---|
572 | 200002&CADDS_IMAGE&CV cadds_image |
---|
573 | .sp .2i |
---|
574 | 300001&ADT_RFLOCKPROG&ADT file locking |
---|
575 | .TE |
---|
576 | .NH 2 |
---|
577 | \&Passing Arbitrary Data Types |
---|
578 | .IX "arbitrary data types" |
---|
579 | .LP |
---|
580 | In the previous example, the RPC call passes a single |
---|
581 | .I "unsigned long" |
---|
582 | RPC can handle arbitrary data structures, regardless of |
---|
583 | different machines' byte orders or structure layout conventions, |
---|
584 | by always converting them to a network standard called |
---|
585 | .I "External Data Representation" |
---|
586 | (XDR) before |
---|
587 | sending them over the wire. |
---|
588 | The process of converting from a particular machine representation |
---|
589 | to XDR format is called |
---|
590 | .I serializing , |
---|
591 | and the reverse process is called |
---|
592 | .I deserializing . |
---|
593 | The type field parameters of |
---|
594 | .I callrpc() |
---|
595 | and |
---|
596 | .I registerrpc() |
---|
597 | can be a built-in procedure like |
---|
598 | .I xdr_u_long() |
---|
599 | in the previous example, or a user supplied one. |
---|
600 | XDR has these built-in type routines: |
---|
601 | .IX RPC "built-in routines" |
---|
602 | .DS |
---|
603 | .ft CW |
---|
604 | xdr_int() xdr_u_int() xdr_enum() |
---|
605 | xdr_long() xdr_u_long() xdr_bool() |
---|
606 | xdr_short() xdr_u_short() xdr_wrapstring() |
---|
607 | xdr_char() xdr_u_char() |
---|
608 | .DE |
---|
609 | Note that the routine |
---|
610 | .I xdr_string() |
---|
611 | exists, but cannot be used with |
---|
612 | .I callrpc() |
---|
613 | and |
---|
614 | .I registerrpc (), |
---|
615 | which only pass two parameters to their XDR routines. |
---|
616 | .I xdr_wrapstring() |
---|
617 | has only two parameters, and is thus OK. It calls |
---|
618 | .I xdr_string (). |
---|
619 | .LP |
---|
620 | As an example of a user-defined type routine, |
---|
621 | if you wanted to send the structure |
---|
622 | .DS |
---|
623 | .ft CW |
---|
624 | struct simple { |
---|
625 | int a; |
---|
626 | short b; |
---|
627 | } simple; |
---|
628 | .DE |
---|
629 | then you would call |
---|
630 | .I callrpc() |
---|
631 | as |
---|
632 | .DS |
---|
633 | .ft CW |
---|
634 | callrpc(hostname, PROGNUM, VERSNUM, PROCNUM, |
---|
635 | xdr_simple, &simple ...); |
---|
636 | .DE |
---|
637 | where |
---|
638 | .I xdr_simple() |
---|
639 | is written as: |
---|
640 | .ie t .DS |
---|
641 | .el .DS L |
---|
642 | .ft CW |
---|
643 | #include <rpc/rpc.h> |
---|
644 | |
---|
645 | xdr_simple(xdrsp, simplep) |
---|
646 | XDR *xdrsp; |
---|
647 | struct simple *simplep; |
---|
648 | { |
---|
649 | if (!xdr_int(xdrsp, &simplep->a)) |
---|
650 | return (0); |
---|
651 | if (!xdr_short(xdrsp, &simplep->b)) |
---|
652 | return (0); |
---|
653 | return (1); |
---|
654 | } |
---|
655 | .DE |
---|
656 | .LP |
---|
657 | An XDR routine returns nonzero (true in the sense of C) if it |
---|
658 | completes successfully, and zero otherwise. |
---|
659 | A complete description of XDR is in the |
---|
660 | .I "XDR Protocol Specification" |
---|
661 | section of this manual, only few implementation examples are |
---|
662 | given here. |
---|
663 | .LP |
---|
664 | In addition to the built-in primitives, |
---|
665 | there are also the prefabricated building blocks: |
---|
666 | .DS |
---|
667 | .ft CW |
---|
668 | xdr_array() xdr_bytes() xdr_reference() |
---|
669 | xdr_vector() xdr_union() xdr_pointer() |
---|
670 | xdr_string() xdr_opaque() |
---|
671 | .DE |
---|
672 | To send a variable array of integers, |
---|
673 | you might package them up as a structure like this |
---|
674 | .DS |
---|
675 | .ft CW |
---|
676 | struct varintarr { |
---|
677 | int *data; |
---|
678 | int arrlnth; |
---|
679 | } arr; |
---|
680 | .DE |
---|
681 | and make an RPC call such as |
---|
682 | .DS |
---|
683 | .ft CW |
---|
684 | callrpc(hostname, PROGNUM, VERSNUM, PROCNUM, |
---|
685 | xdr_varintarr, &arr...); |
---|
686 | .DE |
---|
687 | with |
---|
688 | .I xdr_varintarr() |
---|
689 | defined as: |
---|
690 | .ie t .DS |
---|
691 | .el .DS L |
---|
692 | .ft CW |
---|
693 | xdr_varintarr(xdrsp, arrp) |
---|
694 | XDR *xdrsp; |
---|
695 | struct varintarr *arrp; |
---|
696 | { |
---|
697 | return (xdr_array(xdrsp, &arrp->data, &arrp->arrlnth, |
---|
698 | MAXLEN, sizeof(int), xdr_int)); |
---|
699 | } |
---|
700 | .DE |
---|
701 | This routine takes as parameters the XDR handle, |
---|
702 | a pointer to the array, a pointer to the size of the array, |
---|
703 | the maximum allowable array size, |
---|
704 | the size of each array element, |
---|
705 | and an XDR routine for handling each array element. |
---|
706 | .KS |
---|
707 | .LP |
---|
708 | If the size of the array is known in advance, one can use |
---|
709 | .I xdr_vector (), |
---|
710 | which serializes fixed-length arrays. |
---|
711 | .ie t .DS |
---|
712 | .el .DS L |
---|
713 | .ft CW |
---|
714 | int intarr[SIZE]; |
---|
715 | |
---|
716 | xdr_intarr(xdrsp, intarr) |
---|
717 | XDR *xdrsp; |
---|
718 | int intarr[]; |
---|
719 | { |
---|
720 | int i; |
---|
721 | |
---|
722 | return (xdr_vector(xdrsp, intarr, SIZE, sizeof(int), |
---|
723 | xdr_int)); |
---|
724 | } |
---|
725 | .DE |
---|
726 | .KE |
---|
727 | .LP |
---|
728 | XDR always converts quantities to 4-byte multiples when serializing. |
---|
729 | Thus, if either of the examples above involved characters |
---|
730 | instead of integers, each character would occupy 32 bits. |
---|
731 | That is the reason for the XDR routine |
---|
732 | .I xdr_bytes() |
---|
733 | which is like |
---|
734 | .I xdr_array() |
---|
735 | except that it packs characters; |
---|
736 | .I xdr_bytes() |
---|
737 | has four parameters, similar to the first four parameters of |
---|
738 | .I xdr_array (). |
---|
739 | For null-terminated strings, there is also the |
---|
740 | .I xdr_string() |
---|
741 | routine, which is the same as |
---|
742 | .I xdr_bytes() |
---|
743 | without the length parameter. |
---|
744 | On serializing it gets the string length from |
---|
745 | .I strlen (), |
---|
746 | and on deserializing it creates a null-terminated string. |
---|
747 | .LP |
---|
748 | Here is a final example that calls the previously written |
---|
749 | .I xdr_simple() |
---|
750 | as well as the built-in functions |
---|
751 | .I xdr_string() |
---|
752 | and |
---|
753 | .I xdr_reference (), |
---|
754 | which chases pointers: |
---|
755 | .ie t .DS |
---|
756 | .el .DS L |
---|
757 | .ft CW |
---|
758 | struct finalexample { |
---|
759 | char *string; |
---|
760 | struct simple *simplep; |
---|
761 | } finalexample; |
---|
762 | |
---|
763 | xdr_finalexample(xdrsp, finalp) |
---|
764 | XDR *xdrsp; |
---|
765 | struct finalexample *finalp; |
---|
766 | { |
---|
767 | |
---|
768 | if (!xdr_string(xdrsp, &finalp->string, MAXSTRLEN)) |
---|
769 | return (0); |
---|
770 | if (!xdr_reference(xdrsp, &finalp->simplep, |
---|
771 | sizeof(struct simple), xdr_simple); |
---|
772 | return (0); |
---|
773 | return (1); |
---|
774 | } |
---|
775 | .DE |
---|
776 | Note that we could as easily call |
---|
777 | .I xdr_simple() |
---|
778 | here instead of |
---|
779 | .I xdr_reference (). |
---|
780 | .NH 1 |
---|
781 | \&Lowest Layer of RPC |
---|
782 | .IX "lowest layer of RPC" |
---|
783 | .IX "RPC" "lowest layer" |
---|
784 | .LP |
---|
785 | In the examples given so far, |
---|
786 | RPC takes care of many details automatically for you. |
---|
787 | In this section, we'll show you how you can change the defaults |
---|
788 | by using lower layers of the RPC library. |
---|
789 | It is assumed that you are familiar with sockets |
---|
790 | and the system calls for dealing with them. |
---|
791 | .LP |
---|
792 | There are several occasions when you may need to use lower layers of |
---|
793 | RPC. First, you may need to use TCP, since the higher layer uses UDP, |
---|
794 | which restricts RPC calls to 8K bytes of data. Using TCP permits calls |
---|
795 | to send long streams of data. |
---|
796 | For an example, see the |
---|
797 | .I TCP |
---|
798 | section below. Second, you may want to allocate and free memory |
---|
799 | while serializing or deserializing with XDR routines. |
---|
800 | There is no call at the higher level to let |
---|
801 | you free memory explicitly. |
---|
802 | For more explanation, see the |
---|
803 | .I "Memory Allocation with XDR" |
---|
804 | section below. |
---|
805 | Third, you may need to perform authentication |
---|
806 | on either the client or server side, by supplying |
---|
807 | credentials or verifying them. |
---|
808 | See the explanation in the |
---|
809 | .I Authentication |
---|
810 | section below. |
---|
811 | .NH 2 |
---|
812 | \&More on the Server Side |
---|
813 | .IX RPC "server side" |
---|
814 | .LP |
---|
815 | The server for the |
---|
816 | .I nusers() |
---|
817 | program shown below does the same thing as the one using |
---|
818 | .I registerrpc() |
---|
819 | above, but is written using a lower layer of the RPC package: |
---|
820 | .ie t .DS |
---|
821 | .el .DS L |
---|
822 | .ft CW |
---|
823 | #include <stdio.h> |
---|
824 | #include <rpc/rpc.h> |
---|
825 | #include <utmp.h> |
---|
826 | #include <rpcsvc/rusers.h> |
---|
827 | |
---|
828 | main() |
---|
829 | { |
---|
830 | SVCXPRT *transp; |
---|
831 | int nuser(); |
---|
832 | |
---|
833 | transp = svcudp_create(RPC_ANYSOCK); |
---|
834 | if (transp == NULL){ |
---|
835 | fprintf(stderr, "can't create an RPC server\en"); |
---|
836 | exit(1); |
---|
837 | } |
---|
838 | pmap_unset(RUSERSPROG, RUSERSVERS); |
---|
839 | if (!svc_register(transp, RUSERSPROG, RUSERSVERS, |
---|
840 | nuser, IPPROTO_UDP)) { |
---|
841 | fprintf(stderr, "can't register RUSER service\en"); |
---|
842 | exit(1); |
---|
843 | } |
---|
844 | svc_run(); /* \fINever returns\fP */ |
---|
845 | fprintf(stderr, "should never reach this point\en"); |
---|
846 | } |
---|
847 | |
---|
848 | nuser(rqstp, transp) |
---|
849 | struct svc_req *rqstp; |
---|
850 | SVCXPRT *transp; |
---|
851 | { |
---|
852 | unsigned long nusers; |
---|
853 | |
---|
854 | switch (rqstp->rq_proc) { |
---|
855 | case NULLPROC: |
---|
856 | if (!svc_sendreply(transp, xdr_void, 0)) |
---|
857 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
858 | return; |
---|
859 | case RUSERSPROC_NUM: |
---|
860 | .ft I |
---|
861 | /* |
---|
862 | * Code here to compute the number of users |
---|
863 | * and assign it to the variable \fInusers\fP |
---|
864 | */ |
---|
865 | .ft CW |
---|
866 | if (!svc_sendreply(transp, xdr_u_long, &nusers)) |
---|
867 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
868 | return; |
---|
869 | default: |
---|
870 | svcerr_noproc(transp); |
---|
871 | return; |
---|
872 | } |
---|
873 | } |
---|
874 | .DE |
---|
875 | .LP |
---|
876 | First, the server gets a transport handle, which is used |
---|
877 | for receiving and replying to RPC messages. |
---|
878 | .I registerrpc() |
---|
879 | uses |
---|
880 | .I svcudp_create() |
---|
881 | to get a UDP handle. |
---|
882 | If you require a more reliable protocol, call |
---|
883 | .I svctcp_create() |
---|
884 | instead. |
---|
885 | If the argument to |
---|
886 | .I svcudp_create() |
---|
887 | is |
---|
888 | .I RPC_ANYSOCK |
---|
889 | the RPC library creates a socket |
---|
890 | on which to receive and reply to RPC calls. Otherwise, |
---|
891 | .I svcudp_create() |
---|
892 | expects its argument to be a valid socket number. |
---|
893 | If you specify your own socket, it can be bound or unbound. |
---|
894 | If it is bound to a port by the user, the port numbers of |
---|
895 | .I svcudp_create() |
---|
896 | and |
---|
897 | .I clnttcp_create() |
---|
898 | (the low-level client routine) must match. |
---|
899 | .LP |
---|
900 | If the user specifies the |
---|
901 | .I RPC_ANYSOCK |
---|
902 | argument, the RPC library routines will open sockets. |
---|
903 | Otherwise they will expect the user to do so. The routines |
---|
904 | .I svcudp_create() |
---|
905 | and |
---|
906 | .I clntudp_create() |
---|
907 | will cause the RPC library routines to |
---|
908 | .I bind() |
---|
909 | their socket if it is not bound already. |
---|
910 | .LP |
---|
911 | A service may choose to register its port number with the |
---|
912 | local portmapper service. This is done is done by specifying |
---|
913 | a non-zero protocol number in |
---|
914 | .I svc_register (). |
---|
915 | Incidently, a client can discover the server's port number by |
---|
916 | consulting the portmapper on their server's machine. This can |
---|
917 | be done automatically by specifying a zero port number in |
---|
918 | .I clntudp_create() |
---|
919 | or |
---|
920 | .I clnttcp_create (). |
---|
921 | .LP |
---|
922 | After creating an |
---|
923 | .I SVCXPRT , |
---|
924 | the next step is to call |
---|
925 | .I pmap_unset() |
---|
926 | so that if the |
---|
927 | .I nusers() |
---|
928 | server crashed earlier, |
---|
929 | any previous trace of it is erased before restarting. |
---|
930 | More precisely, |
---|
931 | .I pmap_unset() |
---|
932 | erases the entry for |
---|
933 | .I RUSERSPROG |
---|
934 | from the port mapper's tables. |
---|
935 | .LP |
---|
936 | Finally, we associate the program number for |
---|
937 | .I nusers() |
---|
938 | with the procedure |
---|
939 | .I nuser (). |
---|
940 | The final argument to |
---|
941 | .I svc_register() |
---|
942 | is normally the protocol being used, |
---|
943 | which, in this case, is |
---|
944 | .I IPPROTO_UDP |
---|
945 | Notice that unlike |
---|
946 | .I registerrpc (), |
---|
947 | there are no XDR routines involved |
---|
948 | in the registration process. |
---|
949 | Also, registration is done on the program, |
---|
950 | rather than procedure, level. |
---|
951 | .LP |
---|
952 | The user routine |
---|
953 | .I nuser() |
---|
954 | must call and dispatch the appropriate XDR routines |
---|
955 | based on the procedure number. |
---|
956 | Note that |
---|
957 | two things are handled by |
---|
958 | .I nuser() |
---|
959 | that |
---|
960 | .I registerrpc() |
---|
961 | handles automatically. |
---|
962 | The first is that procedure |
---|
963 | .I NULLPROC |
---|
964 | (currently zero) returns with no results. |
---|
965 | This can be used as a simple test |
---|
966 | for detecting if a remote program is running. |
---|
967 | Second, there is a check for invalid procedure numbers. |
---|
968 | If one is detected, |
---|
969 | .I svcerr_noproc() |
---|
970 | is called to handle the error. |
---|
971 | .KS |
---|
972 | .LP |
---|
973 | The user service routine serializes the results and returns |
---|
974 | them to the RPC caller via |
---|
975 | .I svc_sendreply() |
---|
976 | Its first parameter is the |
---|
977 | .I SVCXPRT |
---|
978 | handle, the second is the XDR routine, |
---|
979 | and the third is a pointer to the data to be returned. |
---|
980 | Not illustrated above is how a server |
---|
981 | handles an RPC program that receives data. |
---|
982 | As an example, we can add a procedure |
---|
983 | .I RUSERSPROC_BOOL |
---|
984 | which has an argument |
---|
985 | .I nusers (), |
---|
986 | and returns |
---|
987 | .I TRUE |
---|
988 | or |
---|
989 | .I FALSE |
---|
990 | depending on whether there are nusers logged on. |
---|
991 | It would look like this: |
---|
992 | .ie t .DS |
---|
993 | .el .DS L |
---|
994 | .ft CW |
---|
995 | case RUSERSPROC_BOOL: { |
---|
996 | int bool; |
---|
997 | unsigned nuserquery; |
---|
998 | |
---|
999 | if (!svc_getargs(transp, xdr_u_int, &nuserquery) { |
---|
1000 | svcerr_decode(transp); |
---|
1001 | return; |
---|
1002 | } |
---|
1003 | .ft I |
---|
1004 | /* |
---|
1005 | * Code to set \fInusers\fP = number of users |
---|
1006 | */ |
---|
1007 | .ft CW |
---|
1008 | if (nuserquery == nusers) |
---|
1009 | bool = TRUE; |
---|
1010 | else |
---|
1011 | bool = FALSE; |
---|
1012 | if (!svc_sendreply(transp, xdr_bool, &bool)) { |
---|
1013 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
1014 | return (1); |
---|
1015 | } |
---|
1016 | return; |
---|
1017 | } |
---|
1018 | .DE |
---|
1019 | .KE |
---|
1020 | .LP |
---|
1021 | The relevant routine is |
---|
1022 | .I svc_getargs() |
---|
1023 | which takes an |
---|
1024 | .I SVCXPRT |
---|
1025 | handle, the XDR routine, |
---|
1026 | and a pointer to where the input is to be placed as arguments. |
---|
1027 | .NH 2 |
---|
1028 | \&Memory Allocation with XDR |
---|
1029 | .IX "memory allocation with XDR" |
---|
1030 | .IX XDR "memory allocation" |
---|
1031 | .LP |
---|
1032 | XDR routines not only do input and output, |
---|
1033 | they also do memory allocation. |
---|
1034 | This is why the second parameter of |
---|
1035 | .I xdr_array() |
---|
1036 | is a pointer to an array, rather than the array itself. |
---|
1037 | If it is |
---|
1038 | .I NULL , |
---|
1039 | then |
---|
1040 | .I xdr_array() |
---|
1041 | allocates space for the array and returns a pointer to it, |
---|
1042 | putting the size of the array in the third argument. |
---|
1043 | As an example, consider the following XDR routine |
---|
1044 | .I xdr_chararr1() |
---|
1045 | which deals with a fixed array of bytes with length |
---|
1046 | .I SIZE . |
---|
1047 | .ie t .DS |
---|
1048 | .el .DS L |
---|
1049 | .ft CW |
---|
1050 | xdr_chararr1(xdrsp, chararr) |
---|
1051 | XDR *xdrsp; |
---|
1052 | char chararr[]; |
---|
1053 | { |
---|
1054 | char *p; |
---|
1055 | int len; |
---|
1056 | |
---|
1057 | p = chararr; |
---|
1058 | len = SIZE; |
---|
1059 | return (xdr_bytes(xdrsp, &p, &len, SIZE)); |
---|
1060 | } |
---|
1061 | .DE |
---|
1062 | If space has already been allocated in |
---|
1063 | .I chararr , |
---|
1064 | it can be called from a server like this: |
---|
1065 | .ie t .DS |
---|
1066 | .el .DS L |
---|
1067 | .ft CW |
---|
1068 | char chararr[SIZE]; |
---|
1069 | |
---|
1070 | svc_getargs(transp, xdr_chararr1, chararr); |
---|
1071 | .DE |
---|
1072 | If you want XDR to do the allocation, |
---|
1073 | you would have to rewrite this routine in the following way: |
---|
1074 | .ie t .DS |
---|
1075 | .el .DS L |
---|
1076 | .ft CW |
---|
1077 | xdr_chararr2(xdrsp, chararrp) |
---|
1078 | XDR *xdrsp; |
---|
1079 | char **chararrp; |
---|
1080 | { |
---|
1081 | int len; |
---|
1082 | |
---|
1083 | len = SIZE; |
---|
1084 | return (xdr_bytes(xdrsp, charrarrp, &len, SIZE)); |
---|
1085 | } |
---|
1086 | .DE |
---|
1087 | Then the RPC call might look like this: |
---|
1088 | .ie t .DS |
---|
1089 | .el .DS L |
---|
1090 | .ft CW |
---|
1091 | char *arrptr; |
---|
1092 | |
---|
1093 | arrptr = NULL; |
---|
1094 | svc_getargs(transp, xdr_chararr2, &arrptr); |
---|
1095 | .ft I |
---|
1096 | /* |
---|
1097 | * Use the result here |
---|
1098 | */ |
---|
1099 | .ft CW |
---|
1100 | svc_freeargs(transp, xdr_chararr2, &arrptr); |
---|
1101 | .DE |
---|
1102 | Note that, after being used, the character array can be freed with |
---|
1103 | .I svc_freeargs() |
---|
1104 | .I svc_freeargs() |
---|
1105 | will not attempt to free any memory if the variable indicating it |
---|
1106 | is NULL. For example, in the the routine |
---|
1107 | .I xdr_finalexample (), |
---|
1108 | given earlier, if |
---|
1109 | .I finalp->string |
---|
1110 | was NULL, then it would not be freed. The same is true for |
---|
1111 | .I finalp->simplep . |
---|
1112 | .LP |
---|
1113 | To summarize, each XDR routine is responsible |
---|
1114 | for serializing, deserializing, and freeing memory. |
---|
1115 | When an XDR routine is called from |
---|
1116 | .I callrpc() |
---|
1117 | the serializing part is used. |
---|
1118 | When called from |
---|
1119 | .I svc_getargs() |
---|
1120 | the deserializer is used. |
---|
1121 | And when called from |
---|
1122 | .I svc_freeargs() |
---|
1123 | the memory deallocator is used. When building simple examples like those |
---|
1124 | in this section, a user doesn't have to worry |
---|
1125 | about the three modes. |
---|
1126 | See the |
---|
1127 | .I "External Data Representation: Sun Technical Notes" |
---|
1128 | for examples of more sophisticated XDR routines that determine |
---|
1129 | which of the three modes they are in and adjust their behavior accordingly. |
---|
1130 | .KS |
---|
1131 | .NH 2 |
---|
1132 | \&The Calling Side |
---|
1133 | .IX RPC "calling side" |
---|
1134 | .LP |
---|
1135 | When you use |
---|
1136 | .I callrpc() |
---|
1137 | you have no control over the RPC delivery |
---|
1138 | mechanism or the socket used to transport the data. |
---|
1139 | To illustrate the layer of RPC that lets you adjust these |
---|
1140 | parameters, consider the following code to call the |
---|
1141 | .I nusers |
---|
1142 | service: |
---|
1143 | .ie t .DS |
---|
1144 | .el .DS L |
---|
1145 | .ft CW |
---|
1146 | .vs 11 |
---|
1147 | #include <stdio.h> |
---|
1148 | #include <rpc/rpc.h> |
---|
1149 | #include <utmp.h> |
---|
1150 | #include <rpcsvc/rusers.h> |
---|
1151 | #include <sys/socket.h> |
---|
1152 | #include <sys/time.h> |
---|
1153 | #include <netdb.h> |
---|
1154 | |
---|
1155 | main(argc, argv) |
---|
1156 | int argc; |
---|
1157 | char **argv; |
---|
1158 | { |
---|
1159 | struct hostent *hp; |
---|
1160 | struct timeval pertry_timeout, total_timeout; |
---|
1161 | struct sockaddr_in server_addr; |
---|
1162 | int sock = RPC_ANYSOCK; |
---|
1163 | register CLIENT *client; |
---|
1164 | enum clnt_stat clnt_stat; |
---|
1165 | unsigned long nusers; |
---|
1166 | |
---|
1167 | if (argc != 2) { |
---|
1168 | fprintf(stderr, "usage: nusers hostname\en"); |
---|
1169 | exit(-1); |
---|
1170 | } |
---|
1171 | if ((hp = gethostbyname(argv[1])) == NULL) { |
---|
1172 | fprintf(stderr, "can't get addr for %s\en",argv[1]); |
---|
1173 | exit(-1); |
---|
1174 | } |
---|
1175 | pertry_timeout.tv_sec = 3; |
---|
1176 | pertry_timeout.tv_usec = 0; |
---|
1177 | bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, |
---|
1178 | hp->h_length); |
---|
1179 | server_addr.sin_family = AF_INET; |
---|
1180 | server_addr.sin_port = 0; |
---|
1181 | if ((client = clntudp_create(&server_addr, RUSERSPROG, |
---|
1182 | RUSERSVERS, pertry_timeout, &sock)) == NULL) { |
---|
1183 | clnt_pcreateerror("clntudp_create"); |
---|
1184 | exit(-1); |
---|
1185 | } |
---|
1186 | total_timeout.tv_sec = 20; |
---|
1187 | total_timeout.tv_usec = 0; |
---|
1188 | clnt_stat = clnt_call(client, RUSERSPROC_NUM, xdr_void, |
---|
1189 | 0, xdr_u_long, &nusers, total_timeout); |
---|
1190 | if (clnt_stat != RPC_SUCCESS) { |
---|
1191 | clnt_perror(client, "rpc"); |
---|
1192 | exit(-1); |
---|
1193 | } |
---|
1194 | clnt_destroy(client); |
---|
1195 | close(sock); |
---|
1196 | exit(0); |
---|
1197 | } |
---|
1198 | .vs |
---|
1199 | .DE |
---|
1200 | .KE |
---|
1201 | The low-level version of |
---|
1202 | .I callrpc() |
---|
1203 | is |
---|
1204 | .I clnt_call() |
---|
1205 | which takes a |
---|
1206 | .I CLIENT |
---|
1207 | pointer rather than a host name. The parameters to |
---|
1208 | .I clnt_call() |
---|
1209 | are a |
---|
1210 | .I CLIENT |
---|
1211 | pointer, the procedure number, |
---|
1212 | the XDR routine for serializing the argument, |
---|
1213 | a pointer to the argument, |
---|
1214 | the XDR routine for deserializing the return value, |
---|
1215 | a pointer to where the return value will be placed, |
---|
1216 | and the time in seconds to wait for a reply. |
---|
1217 | .LP |
---|
1218 | The |
---|
1219 | .I CLIENT |
---|
1220 | pointer is encoded with the transport mechanism. |
---|
1221 | .I callrpc() |
---|
1222 | uses UDP, thus it calls |
---|
1223 | .I clntudp_create() |
---|
1224 | to get a |
---|
1225 | .I CLIENT |
---|
1226 | pointer. To get TCP (Transmission Control Protocol), you would use |
---|
1227 | .I clnttcp_create() . |
---|
1228 | .LP |
---|
1229 | The parameters to |
---|
1230 | .I clntudp_create() |
---|
1231 | are the server address, the program number, the version number, |
---|
1232 | a timeout value (between tries), and a pointer to a socket. |
---|
1233 | The final argument to |
---|
1234 | .I clnt_call() |
---|
1235 | is the total time to wait for a response. |
---|
1236 | Thus, the number of tries is the |
---|
1237 | .I clnt_call() |
---|
1238 | timeout divided by the |
---|
1239 | .I clntudp_create() |
---|
1240 | timeout. |
---|
1241 | .LP |
---|
1242 | Note that the |
---|
1243 | .I clnt_destroy() |
---|
1244 | call |
---|
1245 | always deallocates the space associated with the |
---|
1246 | .I CLIENT |
---|
1247 | handle. It closes the socket associated with the |
---|
1248 | .I CLIENT |
---|
1249 | handle, however, only if the RPC library opened it. It the |
---|
1250 | socket was opened by the user, it stays open. This makes it |
---|
1251 | possible, in cases where there are multiple client handles |
---|
1252 | using the same socket, to destroy one handle without closing |
---|
1253 | the socket that other handles are using. |
---|
1254 | .LP |
---|
1255 | To make a stream connection, the call to |
---|
1256 | .I clntudp_create() |
---|
1257 | is replaced with a call to |
---|
1258 | .I clnttcp_create() . |
---|
1259 | .DS |
---|
1260 | .ft CW |
---|
1261 | clnttcp_create(&server_addr, prognum, versnum, &sock, |
---|
1262 | inputsize, outputsize); |
---|
1263 | .DE |
---|
1264 | There is no timeout argument; instead, the receive and send buffer |
---|
1265 | sizes must be specified. When the |
---|
1266 | .I clnttcp_create() |
---|
1267 | call is made, a TCP connection is established. |
---|
1268 | All RPC calls using that |
---|
1269 | .I CLIENT |
---|
1270 | handle would use this connection. |
---|
1271 | The server side of an RPC call using TCP has |
---|
1272 | .I svcudp_create() |
---|
1273 | replaced by |
---|
1274 | .I svctcp_create() . |
---|
1275 | .DS |
---|
1276 | .ft CW |
---|
1277 | transp = svctcp_create(RPC_ANYSOCK, 0, 0); |
---|
1278 | .DE |
---|
1279 | The last two arguments to |
---|
1280 | .I svctcp_create() |
---|
1281 | are send and receive sizes respectively. If `0' is specified for |
---|
1282 | either of these, the system chooses a reasonable default. |
---|
1283 | .KS |
---|
1284 | .NH 1 |
---|
1285 | \&Other RPC Features |
---|
1286 | .IX "RPC" "miscellaneous features" |
---|
1287 | .IX "miscellaneous RPC features" |
---|
1288 | .LP |
---|
1289 | This section discusses some other aspects of RPC |
---|
1290 | that are occasionally useful. |
---|
1291 | .NH 2 |
---|
1292 | \&Select on the Server Side |
---|
1293 | .IX RPC select() RPC \fIselect()\fP |
---|
1294 | .IX select() "" \fIselect()\fP "on the server side" |
---|
1295 | .LP |
---|
1296 | Suppose a process is processing RPC requests |
---|
1297 | while performing some other activity. |
---|
1298 | If the other activity involves periodically updating a data structure, |
---|
1299 | the process can set an alarm signal before calling |
---|
1300 | .I svc_run() |
---|
1301 | But if the other activity |
---|
1302 | involves waiting on a a file descriptor, the |
---|
1303 | .I svc_run() |
---|
1304 | call won't work. |
---|
1305 | The code for |
---|
1306 | .I svc_run() |
---|
1307 | is as follows: |
---|
1308 | .ie t .DS |
---|
1309 | .el .DS L |
---|
1310 | .ft CW |
---|
1311 | .vs 11 |
---|
1312 | void |
---|
1313 | svc_run() |
---|
1314 | { |
---|
1315 | fd_set readfds; |
---|
1316 | int dtbsz = getdtablesize(); |
---|
1317 | |
---|
1318 | for (;;) { |
---|
1319 | readfds = svc_fds; |
---|
1320 | switch (select(dtbsz, &readfds, NULL,NULL,NULL)) { |
---|
1321 | |
---|
1322 | case -1: |
---|
1323 | if (errno == EINTR) |
---|
1324 | continue; |
---|
1325 | perror("select"); |
---|
1326 | return; |
---|
1327 | case 0: |
---|
1328 | break; |
---|
1329 | default: |
---|
1330 | svc_getreqset(&readfds); |
---|
1331 | } |
---|
1332 | } |
---|
1333 | } |
---|
1334 | .vs |
---|
1335 | .DE |
---|
1336 | .KE |
---|
1337 | .LP |
---|
1338 | You can bypass |
---|
1339 | .I svc_run() |
---|
1340 | and call |
---|
1341 | .I svc_getreqset() |
---|
1342 | yourself. |
---|
1343 | All you need to know are the file descriptors |
---|
1344 | of the socket(s) associated with the programs you are waiting on. |
---|
1345 | Thus you can have your own |
---|
1346 | .I select() |
---|
1347 | .IX select() "" \fIselect()\fP |
---|
1348 | that waits on both the RPC socket, |
---|
1349 | and your own descriptors. Note that |
---|
1350 | .I svc_fds() |
---|
1351 | is a bit mask of all the file descriptors that RPC is using for |
---|
1352 | services. It can change everytime that |
---|
1353 | .I any |
---|
1354 | RPC library routine is called, because descriptors are constantly |
---|
1355 | being opened and closed, for example for TCP connections. |
---|
1356 | .NH 2 |
---|
1357 | \&Broadcast RPC |
---|
1358 | .IX "broadcast RPC" |
---|
1359 | .IX RPC "broadcast" |
---|
1360 | .LP |
---|
1361 | The |
---|
1362 | .I portmapper |
---|
1363 | is a daemon that converts RPC program numbers |
---|
1364 | into DARPA protocol port numbers; see the |
---|
1365 | .I portmap |
---|
1366 | man page. You can't do broadcast RPC without the portmapper. |
---|
1367 | Here are the main differences between |
---|
1368 | broadcast RPC and normal RPC calls: |
---|
1369 | .IP 1. |
---|
1370 | Normal RPC expects one answer, whereas |
---|
1371 | broadcast RPC expects many answers |
---|
1372 | (one or more answer from each responding machine). |
---|
1373 | .IP 2. |
---|
1374 | Broadcast RPC can only be supported by packet-oriented (connectionless) |
---|
1375 | transport protocols like UPD/IP. |
---|
1376 | .IP 3. |
---|
1377 | The implementation of broadcast RPC |
---|
1378 | treats all unsuccessful responses as garbage by filtering them out. |
---|
1379 | Thus, if there is a version mismatch between the |
---|
1380 | broadcaster and a remote service, |
---|
1381 | the user of broadcast RPC never knows. |
---|
1382 | .IP 4. |
---|
1383 | All broadcast messages are sent to the portmap port. |
---|
1384 | Thus, only services that register themselves with their portmapper |
---|
1385 | are accessible via the broadcast RPC mechanism. |
---|
1386 | .IP 5. |
---|
1387 | Broadcast requests are limited in size to the MTU (Maximum Transfer |
---|
1388 | Unit) of the local network. For Ethernet, the MTU is 1500 bytes. |
---|
1389 | .KS |
---|
1390 | .NH 3 |
---|
1391 | \&Broadcast RPC Synopsis |
---|
1392 | .IX "broadcast RPC" synopsis |
---|
1393 | .IX "RPC" "broadcast synopsis" |
---|
1394 | .ie t .DS |
---|
1395 | .el .DS L |
---|
1396 | .ft CW |
---|
1397 | #include <rpc/pmap_clnt.h> |
---|
1398 | . . . |
---|
1399 | enum clnt_stat clnt_stat; |
---|
1400 | . . . |
---|
1401 | clnt_stat = clnt_broadcast(prognum, versnum, procnum, |
---|
1402 | inproc, in, outproc, out, eachresult) |
---|
1403 | u_long prognum; /* \fIprogram number\fP */ |
---|
1404 | u_long versnum; /* \fIversion number\fP */ |
---|
1405 | u_long procnum; /* \fIprocedure number\fP */ |
---|
1406 | xdrproc_t inproc; /* \fIxdr routine for args\fP */ |
---|
1407 | caddr_t in; /* \fIpointer to args\fP */ |
---|
1408 | xdrproc_t outproc; /* \fIxdr routine for results\fP */ |
---|
1409 | caddr_t out; /* \fIpointer to results\fP */ |
---|
1410 | bool_t (*eachresult)();/* \fIcall with each result gotten\fP */ |
---|
1411 | .DE |
---|
1412 | .KE |
---|
1413 | The procedure |
---|
1414 | .I eachresult() |
---|
1415 | is called each time a valid result is obtained. |
---|
1416 | It returns a boolean that indicates |
---|
1417 | whether or not the user wants more responses. |
---|
1418 | .ie t .DS |
---|
1419 | .el .DS L |
---|
1420 | .ft CW |
---|
1421 | bool_t done; |
---|
1422 | . . . |
---|
1423 | done = eachresult(resultsp, raddr) |
---|
1424 | caddr_t resultsp; |
---|
1425 | struct sockaddr_in *raddr; /* \fIAddr of responding machine\fP */ |
---|
1426 | .DE |
---|
1427 | If |
---|
1428 | .I done |
---|
1429 | is |
---|
1430 | .I TRUE , |
---|
1431 | then broadcasting stops and |
---|
1432 | .I clnt_broadcast() |
---|
1433 | returns successfully. |
---|
1434 | Otherwise, the routine waits for another response. |
---|
1435 | The request is rebroadcast |
---|
1436 | after a few seconds of waiting. |
---|
1437 | If no responses come back, |
---|
1438 | the routine returns with |
---|
1439 | .I RPC_TIMEDOUT . |
---|
1440 | .NH 2 |
---|
1441 | \&Batching |
---|
1442 | .IX "batching" |
---|
1443 | .IX RPC "batching" |
---|
1444 | .LP |
---|
1445 | The RPC architecture is designed so that clients send a call message, |
---|
1446 | and wait for servers to reply that the call succeeded. |
---|
1447 | This implies that clients do not compute |
---|
1448 | while servers are processing a call. |
---|
1449 | This is inefficient if the client does not want or need |
---|
1450 | an acknowledgement for every message sent. |
---|
1451 | It is possible for clients to continue computing |
---|
1452 | while waiting for a response, |
---|
1453 | using RPC batch facilities. |
---|
1454 | .LP |
---|
1455 | RPC messages can be placed in a \*Qpipeline\*U of calls |
---|
1456 | to a desired server; this is called batching. |
---|
1457 | Batching assumes that: |
---|
1458 | 1) each RPC call in the pipeline requires no response from the server, |
---|
1459 | and the server does not send a response message; and |
---|
1460 | 2) the pipeline of calls is transported on a reliable |
---|
1461 | byte stream transport such as TCP/IP. |
---|
1462 | Since the server does not respond to every call, |
---|
1463 | the client can generate new calls in parallel |
---|
1464 | with the server executing previous calls. |
---|
1465 | Furthermore, the TCP/IP implementation can buffer up |
---|
1466 | many call messages, and send them to the server in one |
---|
1467 | .I write() |
---|
1468 | system call. This overlapped execution |
---|
1469 | greatly decreases the interprocess communication overhead of |
---|
1470 | the client and server processes, |
---|
1471 | and the total elapsed time of a series of calls. |
---|
1472 | .LP |
---|
1473 | Since the batched calls are buffered, |
---|
1474 | the client should eventually do a nonbatched call |
---|
1475 | in order to flush the pipeline. |
---|
1476 | .LP |
---|
1477 | A contrived example of batching follows. |
---|
1478 | Assume a string rendering service (like a window system) |
---|
1479 | has two similar calls: one renders a string and returns void results, |
---|
1480 | while the other renders a string and remains silent. |
---|
1481 | The service (using the TCP/IP transport) may look like: |
---|
1482 | .ie t .DS |
---|
1483 | .el .DS L |
---|
1484 | .ft CW |
---|
1485 | #include <stdio.h> |
---|
1486 | #include <rpc/rpc.h> |
---|
1487 | #include <suntool/windows.h> |
---|
1488 | |
---|
1489 | void windowdispatch(); |
---|
1490 | |
---|
1491 | main() |
---|
1492 | { |
---|
1493 | SVCXPRT *transp; |
---|
1494 | |
---|
1495 | transp = svctcp_create(RPC_ANYSOCK, 0, 0); |
---|
1496 | if (transp == NULL){ |
---|
1497 | fprintf(stderr, "can't create an RPC server\en"); |
---|
1498 | exit(1); |
---|
1499 | } |
---|
1500 | pmap_unset(WINDOWPROG, WINDOWVERS); |
---|
1501 | if (!svc_register(transp, WINDOWPROG, WINDOWVERS, |
---|
1502 | windowdispatch, IPPROTO_TCP)) { |
---|
1503 | fprintf(stderr, "can't register WINDOW service\en"); |
---|
1504 | exit(1); |
---|
1505 | } |
---|
1506 | svc_run(); /* \fINever returns\fP */ |
---|
1507 | fprintf(stderr, "should never reach this point\en"); |
---|
1508 | } |
---|
1509 | |
---|
1510 | void |
---|
1511 | windowdispatch(rqstp, transp) |
---|
1512 | struct svc_req *rqstp; |
---|
1513 | SVCXPRT *transp; |
---|
1514 | { |
---|
1515 | char *s = NULL; |
---|
1516 | |
---|
1517 | switch (rqstp->rq_proc) { |
---|
1518 | case NULLPROC: |
---|
1519 | if (!svc_sendreply(transp, xdr_void, 0)) |
---|
1520 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
1521 | return; |
---|
1522 | case RENDERSTRING: |
---|
1523 | if (!svc_getargs(transp, xdr_wrapstring, &s)) { |
---|
1524 | fprintf(stderr, "can't decode arguments\en"); |
---|
1525 | .ft I |
---|
1526 | /* |
---|
1527 | * Tell caller he screwed up |
---|
1528 | */ |
---|
1529 | .ft CW |
---|
1530 | svcerr_decode(transp); |
---|
1531 | break; |
---|
1532 | } |
---|
1533 | .ft I |
---|
1534 | /* |
---|
1535 | * Code here to render the string \fIs\fP |
---|
1536 | */ |
---|
1537 | .ft CW |
---|
1538 | if (!svc_sendreply(transp, xdr_void, NULL)) |
---|
1539 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
1540 | break; |
---|
1541 | case RENDERSTRING_BATCHED: |
---|
1542 | if (!svc_getargs(transp, xdr_wrapstring, &s)) { |
---|
1543 | fprintf(stderr, "can't decode arguments\en"); |
---|
1544 | .ft I |
---|
1545 | /* |
---|
1546 | * We are silent in the face of protocol errors |
---|
1547 | */ |
---|
1548 | .ft CW |
---|
1549 | break; |
---|
1550 | } |
---|
1551 | .ft I |
---|
1552 | /* |
---|
1553 | * Code here to render string s, but send no reply! |
---|
1554 | */ |
---|
1555 | .ft CW |
---|
1556 | break; |
---|
1557 | default: |
---|
1558 | svcerr_noproc(transp); |
---|
1559 | return; |
---|
1560 | } |
---|
1561 | .ft I |
---|
1562 | /* |
---|
1563 | * Now free string allocated while decoding arguments |
---|
1564 | */ |
---|
1565 | .ft CW |
---|
1566 | svc_freeargs(transp, xdr_wrapstring, &s); |
---|
1567 | } |
---|
1568 | .DE |
---|
1569 | Of course the service could have one procedure |
---|
1570 | that takes the string and a boolean |
---|
1571 | to indicate whether or not the procedure should respond. |
---|
1572 | .LP |
---|
1573 | In order for a client to take advantage of batching, |
---|
1574 | the client must perform RPC calls on a TCP-based transport |
---|
1575 | and the actual calls must have the following attributes: |
---|
1576 | 1) the result's XDR routine must be zero |
---|
1577 | .I NULL ), |
---|
1578 | and 2) the RPC call's timeout must be zero. |
---|
1579 | .KS |
---|
1580 | .LP |
---|
1581 | Here is an example of a client that uses batching to render a |
---|
1582 | bunch of strings; the batching is flushed when the client gets |
---|
1583 | a null string (EOF): |
---|
1584 | .ie t .DS |
---|
1585 | .el .DS L |
---|
1586 | .ft CW |
---|
1587 | .vs 11 |
---|
1588 | #include <stdio.h> |
---|
1589 | #include <rpc/rpc.h> |
---|
1590 | #include <sys/socket.h> |
---|
1591 | #include <sys/time.h> |
---|
1592 | #include <netdb.h> |
---|
1593 | #include <suntool/windows.h> |
---|
1594 | |
---|
1595 | main(argc, argv) |
---|
1596 | int argc; |
---|
1597 | char **argv; |
---|
1598 | { |
---|
1599 | struct hostent *hp; |
---|
1600 | struct timeval pertry_timeout, total_timeout; |
---|
1601 | struct sockaddr_in server_addr; |
---|
1602 | int sock = RPC_ANYSOCK; |
---|
1603 | register CLIENT *client; |
---|
1604 | enum clnt_stat clnt_stat; |
---|
1605 | char buf[1000], *s = buf; |
---|
1606 | |
---|
1607 | if ((client = clnttcp_create(&server_addr, |
---|
1608 | WINDOWPROG, WINDOWVERS, &sock, 0, 0)) == NULL) { |
---|
1609 | perror("clnttcp_create"); |
---|
1610 | exit(-1); |
---|
1611 | } |
---|
1612 | total_timeout.tv_sec = 0; |
---|
1613 | total_timeout.tv_usec = 0; |
---|
1614 | while (scanf("%s", s) != EOF) { |
---|
1615 | clnt_stat = clnt_call(client, RENDERSTRING_BATCHED, |
---|
1616 | xdr_wrapstring, &s, NULL, NULL, total_timeout); |
---|
1617 | if (clnt_stat != RPC_SUCCESS) { |
---|
1618 | clnt_perror(client, "batched rpc"); |
---|
1619 | exit(-1); |
---|
1620 | } |
---|
1621 | } |
---|
1622 | |
---|
1623 | /* \fINow flush the pipeline\fP */ |
---|
1624 | |
---|
1625 | total_timeout.tv_sec = 20; |
---|
1626 | clnt_stat = clnt_call(client, NULLPROC, xdr_void, NULL, |
---|
1627 | xdr_void, NULL, total_timeout); |
---|
1628 | if (clnt_stat != RPC_SUCCESS) { |
---|
1629 | clnt_perror(client, "rpc"); |
---|
1630 | exit(-1); |
---|
1631 | } |
---|
1632 | clnt_destroy(client); |
---|
1633 | exit(0); |
---|
1634 | } |
---|
1635 | .vs |
---|
1636 | .DE |
---|
1637 | .KE |
---|
1638 | Since the server sends no message, |
---|
1639 | the clients cannot be notified of any of the failures that may occur. |
---|
1640 | Therefore, clients are on their own when it comes to handling errors. |
---|
1641 | .LP |
---|
1642 | The above example was completed to render |
---|
1643 | all of the (2000) lines in the file |
---|
1644 | .I /etc/termcap . |
---|
1645 | The rendering service did nothing but throw the lines away. |
---|
1646 | The example was run in the following four configurations: |
---|
1647 | 1) machine to itself, regular RPC; |
---|
1648 | 2) machine to itself, batched RPC; |
---|
1649 | 3) machine to another, regular RPC; and |
---|
1650 | 4) machine to another, batched RPC. |
---|
1651 | The results are as follows: |
---|
1652 | 1) 50 seconds; |
---|
1653 | 2) 16 seconds; |
---|
1654 | 3) 52 seconds; |
---|
1655 | 4) 10 seconds. |
---|
1656 | Running |
---|
1657 | .I fscanf() |
---|
1658 | on |
---|
1659 | .I /etc/termcap |
---|
1660 | only requires six seconds. |
---|
1661 | These timings show the advantage of protocols |
---|
1662 | that allow for overlapped execution, |
---|
1663 | though these protocols are often hard to design. |
---|
1664 | .NH 2 |
---|
1665 | \&Authentication |
---|
1666 | .IX "authentication" |
---|
1667 | .IX "RPC" "authentication" |
---|
1668 | .LP |
---|
1669 | In the examples presented so far, |
---|
1670 | the caller never identified itself to the server, |
---|
1671 | and the server never required an ID from the caller. |
---|
1672 | Clearly, some network services, such as a network filesystem, |
---|
1673 | require stronger security than what has been presented so far. |
---|
1674 | .LP |
---|
1675 | In reality, every RPC call is authenticated by |
---|
1676 | the RPC package on the server, and similarly, |
---|
1677 | the RPC client package generates and sends authentication parameters. |
---|
1678 | Just as different transports (TCP/IP or UDP/IP) |
---|
1679 | can be used when creating RPC clients and servers, |
---|
1680 | different forms of authentication can be associated with RPC clients; |
---|
1681 | the default authentication type used as a default is type |
---|
1682 | .I none . |
---|
1683 | .LP |
---|
1684 | The authentication subsystem of the RPC package is open ended. |
---|
1685 | That is, numerous types of authentication are easy to support. |
---|
1686 | .NH 3 |
---|
1687 | \&UNIX Authentication |
---|
1688 | .IX "UNIX Authentication" |
---|
1689 | .IP "\fIThe Client Side\fP" |
---|
1690 | .LP |
---|
1691 | When a caller creates a new RPC client handle as in: |
---|
1692 | .DS |
---|
1693 | .ft CW |
---|
1694 | clnt = clntudp_create(address, prognum, versnum, |
---|
1695 | wait, sockp) |
---|
1696 | .DE |
---|
1697 | the appropriate transport instance defaults |
---|
1698 | the associate authentication handle to be |
---|
1699 | .DS |
---|
1700 | .ft CW |
---|
1701 | clnt->cl_auth = authnone_create(); |
---|
1702 | .DE |
---|
1703 | The RPC client can choose to use |
---|
1704 | .I UNIX |
---|
1705 | style authentication by setting |
---|
1706 | .I clnt\->cl_auth |
---|
1707 | after creating the RPC client handle: |
---|
1708 | .DS |
---|
1709 | .ft CW |
---|
1710 | clnt->cl_auth = authunix_create_default(); |
---|
1711 | .DE |
---|
1712 | This causes each RPC call associated with |
---|
1713 | .I clnt |
---|
1714 | to carry with it the following authentication credentials structure: |
---|
1715 | .ie t .DS |
---|
1716 | .el .DS L |
---|
1717 | .ft I |
---|
1718 | /* |
---|
1719 | * UNIX style credentials. |
---|
1720 | */ |
---|
1721 | .ft CW |
---|
1722 | struct authunix_parms { |
---|
1723 | u_long aup_time; /* \fIcredentials creation time\fP */ |
---|
1724 | char *aup_machname; /* \fIhost name where client is\fP */ |
---|
1725 | int aup_uid; /* \fIclient's UNIX effective uid\fP */ |
---|
1726 | int aup_gid; /* \fIclient's current group id\fP */ |
---|
1727 | u_int aup_len; /* \fIelement length of aup_gids\fP */ |
---|
1728 | int *aup_gids; /* \fIarray of groups user is in\fP */ |
---|
1729 | }; |
---|
1730 | .DE |
---|
1731 | These fields are set by |
---|
1732 | .I authunix_create_default() |
---|
1733 | by invoking the appropriate system calls. |
---|
1734 | Since the RPC user created this new style of authentication, |
---|
1735 | the user is responsible for destroying it with: |
---|
1736 | .DS |
---|
1737 | .ft CW |
---|
1738 | auth_destroy(clnt->cl_auth); |
---|
1739 | .DE |
---|
1740 | This should be done in all cases, to conserve memory. |
---|
1741 | .sp |
---|
1742 | .IP "\fIThe Server Side\fP" |
---|
1743 | .LP |
---|
1744 | Service implementors have a harder time dealing with authentication issues |
---|
1745 | since the RPC package passes the service dispatch routine a request |
---|
1746 | that has an arbitrary authentication style associated with it. |
---|
1747 | Consider the fields of a request handle passed to a service dispatch routine: |
---|
1748 | .ie t .DS |
---|
1749 | .el .DS L |
---|
1750 | .ft I |
---|
1751 | /* |
---|
1752 | * An RPC Service request |
---|
1753 | */ |
---|
1754 | .ft CW |
---|
1755 | struct svc_req { |
---|
1756 | u_long rq_prog; /* \fIservice program number\fP */ |
---|
1757 | u_long rq_vers; /* \fIservice protocol vers num\fP */ |
---|
1758 | u_long rq_proc; /* \fIdesired procedure number\fP */ |
---|
1759 | struct opaque_auth rq_cred; /* \fIraw credentials from wire\fP */ |
---|
1760 | caddr_t rq_clntcred; /* \fIcredentials (read only)\fP */ |
---|
1761 | }; |
---|
1762 | .DE |
---|
1763 | The |
---|
1764 | .I rq_cred |
---|
1765 | is mostly opaque, except for one field of interest: |
---|
1766 | the style or flavor of authentication credentials: |
---|
1767 | .ie t .DS |
---|
1768 | .el .DS L |
---|
1769 | .ft I |
---|
1770 | /* |
---|
1771 | * Authentication info. Mostly opaque to the programmer. |
---|
1772 | */ |
---|
1773 | .ft CW |
---|
1774 | struct opaque_auth { |
---|
1775 | enum_t oa_flavor; /* \fIstyle of credentials\fP */ |
---|
1776 | caddr_t oa_base; /* \fIaddress of more auth stuff\fP */ |
---|
1777 | u_int oa_length; /* \fInot to exceed \fIMAX_AUTH_BYTES */ |
---|
1778 | }; |
---|
1779 | .DE |
---|
1780 | .IX RPC guarantees |
---|
1781 | The RPC package guarantees the following |
---|
1782 | to the service dispatch routine: |
---|
1783 | .IP 1. |
---|
1784 | That the request's |
---|
1785 | .I rq_cred |
---|
1786 | is well formed. Thus the service implementor may inspect the request's |
---|
1787 | .I rq_cred.oa_flavor |
---|
1788 | to determine which style of authentication the caller used. |
---|
1789 | The service implementor may also wish to inspect the other fields of |
---|
1790 | .I rq_cred |
---|
1791 | if the style is not one of the styles supported by the RPC package. |
---|
1792 | .IP 2. |
---|
1793 | That the request's |
---|
1794 | .I rq_clntcred |
---|
1795 | field is either |
---|
1796 | .I NULL |
---|
1797 | or points to a well formed structure |
---|
1798 | that corresponds to a supported style of authentication credentials. |
---|
1799 | Remember that only |
---|
1800 | .I unix |
---|
1801 | style is currently supported, so (currently) |
---|
1802 | .I rq_clntcred |
---|
1803 | could be cast to a pointer to an |
---|
1804 | .I authunix_parms |
---|
1805 | structure. If |
---|
1806 | .I rq_clntcred |
---|
1807 | is |
---|
1808 | .I NULL , |
---|
1809 | the service implementor may wish to inspect the other (opaque) fields of |
---|
1810 | .I rq_cred |
---|
1811 | in case the service knows about a new type of authentication |
---|
1812 | that the RPC package does not know about. |
---|
1813 | .LP |
---|
1814 | Our remote users service example can be extended so that |
---|
1815 | it computes results for all users except UID 16: |
---|
1816 | .ie t .DS |
---|
1817 | .el .DS L |
---|
1818 | .ft CW |
---|
1819 | .vs 11 |
---|
1820 | nuser(rqstp, transp) |
---|
1821 | struct svc_req *rqstp; |
---|
1822 | SVCXPRT *transp; |
---|
1823 | { |
---|
1824 | struct authunix_parms *unix_cred; |
---|
1825 | int uid; |
---|
1826 | unsigned long nusers; |
---|
1827 | |
---|
1828 | .ft I |
---|
1829 | /* |
---|
1830 | * we don't care about authentication for null proc |
---|
1831 | */ |
---|
1832 | .ft CW |
---|
1833 | if (rqstp->rq_proc == NULLPROC) { |
---|
1834 | if (!svc_sendreply(transp, xdr_void, 0)) { |
---|
1835 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
1836 | return (1); |
---|
1837 | } |
---|
1838 | return; |
---|
1839 | } |
---|
1840 | .ft I |
---|
1841 | /* |
---|
1842 | * now get the uid |
---|
1843 | */ |
---|
1844 | .ft CW |
---|
1845 | switch (rqstp->rq_cred.oa_flavor) { |
---|
1846 | case AUTH_UNIX: |
---|
1847 | unix_cred = |
---|
1848 | (struct authunix_parms *)rqstp->rq_clntcred; |
---|
1849 | uid = unix_cred->aup_uid; |
---|
1850 | break; |
---|
1851 | case AUTH_NULL: |
---|
1852 | default: |
---|
1853 | svcerr_weakauth(transp); |
---|
1854 | return; |
---|
1855 | } |
---|
1856 | switch (rqstp->rq_proc) { |
---|
1857 | case RUSERSPROC_NUM: |
---|
1858 | .ft I |
---|
1859 | /* |
---|
1860 | * make sure caller is allowed to call this proc |
---|
1861 | */ |
---|
1862 | .ft CW |
---|
1863 | if (uid == 16) { |
---|
1864 | svcerr_systemerr(transp); |
---|
1865 | return; |
---|
1866 | } |
---|
1867 | .ft I |
---|
1868 | /* |
---|
1869 | * Code here to compute the number of users |
---|
1870 | * and assign it to the variable \fInusers\fP |
---|
1871 | */ |
---|
1872 | .ft CW |
---|
1873 | if (!svc_sendreply(transp, xdr_u_long, &nusers)) { |
---|
1874 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
1875 | return (1); |
---|
1876 | } |
---|
1877 | return; |
---|
1878 | default: |
---|
1879 | svcerr_noproc(transp); |
---|
1880 | return; |
---|
1881 | } |
---|
1882 | } |
---|
1883 | .vs |
---|
1884 | .DE |
---|
1885 | A few things should be noted here. |
---|
1886 | First, it is customary not to check |
---|
1887 | the authentication parameters associated with the |
---|
1888 | .I NULLPROC |
---|
1889 | (procedure number zero). |
---|
1890 | Second, if the authentication parameter's type is not suitable |
---|
1891 | for your service, you should call |
---|
1892 | .I svcerr_weakauth() . |
---|
1893 | And finally, the service protocol itself should return status |
---|
1894 | for access denied; in the case of our example, the protocol |
---|
1895 | does not have such a status, so we call the service primitive |
---|
1896 | .I svcerr_systemerr() |
---|
1897 | instead. |
---|
1898 | .LP |
---|
1899 | The last point underscores the relation between |
---|
1900 | the RPC authentication package and the services; |
---|
1901 | RPC deals only with |
---|
1902 | .I authentication |
---|
1903 | and not with individual services' |
---|
1904 | .I "access control" . |
---|
1905 | The services themselves must implement their own access control policies |
---|
1906 | and reflect these policies as return statuses in their protocols. |
---|
1907 | .NH 2 |
---|
1908 | \&DES Authentication |
---|
1909 | .IX RPC DES |
---|
1910 | .IX RPC authentication |
---|
1911 | .LP |
---|
1912 | UNIX authentication is quite easy to defeat. Instead of using |
---|
1913 | .I authunix_create_default (), |
---|
1914 | one can call |
---|
1915 | .I authunix_create() |
---|
1916 | and then modify the RPC authentication handle it returns by filling in |
---|
1917 | whatever user ID and hostname they wish the server to think they have. |
---|
1918 | DES authentication is thus recommended for people who want more security |
---|
1919 | than UNIX authentication offers. |
---|
1920 | .LP |
---|
1921 | The details of the DES authentication protocol are complicated and |
---|
1922 | are not explained here. |
---|
1923 | See |
---|
1924 | .I "Remote Procedure Calls: Protocol Specification" |
---|
1925 | for the details. |
---|
1926 | .LP |
---|
1927 | In order for DES authentication to work, the |
---|
1928 | .I keyserv(8c) |
---|
1929 | daemon must be running on both the server and client machines. The |
---|
1930 | users on these machines need public keys assigned by the network |
---|
1931 | administrator in the |
---|
1932 | .I publickey(5) |
---|
1933 | database. And, they need to have decrypted their secret keys |
---|
1934 | using their login password. This automatically happens when one |
---|
1935 | logs in using |
---|
1936 | .I login(1) , |
---|
1937 | or can be done manually using |
---|
1938 | .I keylogin(1) . |
---|
1939 | The |
---|
1940 | .I "Network Services" |
---|
1941 | chapter |
---|
1942 | ./" XXX |
---|
1943 | explains more how to setup secure networking. |
---|
1944 | .sp |
---|
1945 | .IP "\fIClient Side\fP" |
---|
1946 | .LP |
---|
1947 | If a client wishes to use DES authentication, it must set its |
---|
1948 | authentication handle appropriately. Here is an example: |
---|
1949 | .DS |
---|
1950 | cl->cl_auth = |
---|
1951 | authdes_create(servername, 60, &server_addr, NULL); |
---|
1952 | .DE |
---|
1953 | The first argument is the network name or \*Qnetname\*U of the owner of |
---|
1954 | the server process. Typically, server processes are root processes |
---|
1955 | and their netname can be derived using the following call: |
---|
1956 | .DS |
---|
1957 | char servername[MAXNETNAMELEN]; |
---|
1958 | |
---|
1959 | host2netname(servername, rhostname, NULL); |
---|
1960 | .DE |
---|
1961 | Here, |
---|
1962 | .I rhostname |
---|
1963 | is the hostname of the machine the server process is running on. |
---|
1964 | .I host2netname() |
---|
1965 | fills in |
---|
1966 | .I servername |
---|
1967 | to contain this root process's netname. If the |
---|
1968 | server process was run by a regular user, one could use the call |
---|
1969 | .I user2netname() |
---|
1970 | instead. Here is an example for a server process with the same user |
---|
1971 | ID as the client: |
---|
1972 | .DS |
---|
1973 | char servername[MAXNETNAMELEN]; |
---|
1974 | |
---|
1975 | user2netname(servername, getuid(), NULL); |
---|
1976 | .DE |
---|
1977 | The last argument to both of these calls, |
---|
1978 | .I user2netname() |
---|
1979 | and |
---|
1980 | .I host2netname (), |
---|
1981 | is the name of the naming domain where the server is located. The |
---|
1982 | .I NULL |
---|
1983 | used here means \*Quse the local domain name.\*U |
---|
1984 | .LP |
---|
1985 | The second argument to |
---|
1986 | .I authdes_create() |
---|
1987 | is a lifetime for the credential. Here it is set to sixty |
---|
1988 | seconds. What that means is that the credential will expire 60 |
---|
1989 | seconds from now. If some mischievous user tries to reuse the |
---|
1990 | credential, the server RPC subsystem will recognize that it has |
---|
1991 | expired and not grant any requests. If the same mischievous user |
---|
1992 | tries to reuse the credential within the sixty second lifetime, |
---|
1993 | he will still be rejected because the server RPC subsystem |
---|
1994 | remembers which credentials it has already seen in the near past, |
---|
1995 | and will not grant requests to duplicates. |
---|
1996 | .LP |
---|
1997 | The third argument to |
---|
1998 | .I authdes_create() |
---|
1999 | is the address of the host to synchronize with. In order for DES |
---|
2000 | authentication to work, the server and client must agree upon the |
---|
2001 | time. Here we pass the address of the server itself, so the |
---|
2002 | client and server will both be using the same time: the server's |
---|
2003 | time. The argument can be |
---|
2004 | .I NULL , |
---|
2005 | which means \*Qdon't bother synchronizing.\*U You should only do this |
---|
2006 | if you are sure the client and server are already synchronized. |
---|
2007 | .LP |
---|
2008 | The final argument to |
---|
2009 | .I authdes_create() |
---|
2010 | is the address of a DES encryption key to use for encrypting |
---|
2011 | timestamps and data. If this argument is |
---|
2012 | .I NULL , |
---|
2013 | as it is in this example, a random key will be chosen. The client |
---|
2014 | may find out the encryption key being used by consulting the |
---|
2015 | .I ah_key |
---|
2016 | field of the authentication handle. |
---|
2017 | .sp |
---|
2018 | .IP "\fIServer Side\fP" |
---|
2019 | .LP |
---|
2020 | The server side is a lot simpler than the client side. Here is the |
---|
2021 | previous example rewritten to use |
---|
2022 | .I AUTH_DES |
---|
2023 | instead of |
---|
2024 | .I AUTH_UNIX : |
---|
2025 | .ie t .DS |
---|
2026 | .el .DS L |
---|
2027 | .ft CW |
---|
2028 | .vs 11 |
---|
2029 | #include <sys/time.h> |
---|
2030 | #include <rpc/auth_des.h> |
---|
2031 | . . . |
---|
2032 | . . . |
---|
2033 | nuser(rqstp, transp) |
---|
2034 | struct svc_req *rqstp; |
---|
2035 | SVCXPRT *transp; |
---|
2036 | { |
---|
2037 | struct authdes_cred *des_cred; |
---|
2038 | int uid; |
---|
2039 | int gid; |
---|
2040 | int gidlen; |
---|
2041 | int gidlist[10]; |
---|
2042 | .ft I |
---|
2043 | /* |
---|
2044 | * we don't care about authentication for null proc |
---|
2045 | */ |
---|
2046 | .ft CW |
---|
2047 | |
---|
2048 | if (rqstp->rq_proc == NULLPROC) { |
---|
2049 | /* \fIsame as before\fP */ |
---|
2050 | } |
---|
2051 | |
---|
2052 | .ft I |
---|
2053 | /* |
---|
2054 | * now get the uid |
---|
2055 | */ |
---|
2056 | .ft CW |
---|
2057 | switch (rqstp->rq_cred.oa_flavor) { |
---|
2058 | case AUTH_DES: |
---|
2059 | des_cred = |
---|
2060 | (struct authdes_cred *) rqstp->rq_clntcred; |
---|
2061 | if (! netname2user(des_cred->adc_fullname.name, |
---|
2062 | &uid, &gid, &gidlen, gidlist)) |
---|
2063 | { |
---|
2064 | fprintf(stderr, "unknown user: %s\n", |
---|
2065 | des_cred->adc_fullname.name); |
---|
2066 | svcerr_systemerr(transp); |
---|
2067 | return; |
---|
2068 | } |
---|
2069 | break; |
---|
2070 | case AUTH_NULL: |
---|
2071 | default: |
---|
2072 | svcerr_weakauth(transp); |
---|
2073 | return; |
---|
2074 | } |
---|
2075 | |
---|
2076 | .ft I |
---|
2077 | /* |
---|
2078 | * The rest is the same as before |
---|
2079 | */ |
---|
2080 | .ft CW |
---|
2081 | .vs |
---|
2082 | .DE |
---|
2083 | Note the use of the routine |
---|
2084 | .I netname2user (), |
---|
2085 | the inverse of |
---|
2086 | .I user2netname (): |
---|
2087 | it takes a network ID and converts to a unix ID. |
---|
2088 | .I netname2user () |
---|
2089 | also supplies the group IDs which we don't use in this example, |
---|
2090 | but which may be useful to other UNIX programs. |
---|
2091 | .NH 2 |
---|
2092 | \&Using Inetd |
---|
2093 | .IX inetd "" "using \fIinetd\fP" |
---|
2094 | .LP |
---|
2095 | An RPC server can be started from |
---|
2096 | .I inetd |
---|
2097 | The only difference from the usual code is that the service |
---|
2098 | creation routine should be called in the following form: |
---|
2099 | .ie t .DS |
---|
2100 | .el .DS L |
---|
2101 | .ft CW |
---|
2102 | transp = svcudp_create(0); /* \fIFor UDP\fP */ |
---|
2103 | transp = svctcp_create(0,0,0); /* \fIFor listener TCP sockets\fP */ |
---|
2104 | transp = svcfd_create(0,0,0); /* \fIFor connected TCP sockets\fP */ |
---|
2105 | .DE |
---|
2106 | since |
---|
2107 | .I inet |
---|
2108 | passes a socket as file descriptor 0. |
---|
2109 | Also, |
---|
2110 | .I svc_register() |
---|
2111 | should be called as |
---|
2112 | .ie t .DS |
---|
2113 | .el .DS L |
---|
2114 | .ft CW |
---|
2115 | svc_register(transp, PROGNUM, VERSNUM, service, 0); |
---|
2116 | .DE |
---|
2117 | with the final flag as 0, |
---|
2118 | since the program would already be registered by |
---|
2119 | .I inetd |
---|
2120 | Remember that if you want to exit |
---|
2121 | from the server process and return control to |
---|
2122 | .I inet |
---|
2123 | you need to explicitly exit, since |
---|
2124 | .I svc_run() |
---|
2125 | never returns. |
---|
2126 | .LP |
---|
2127 | The format of entries in |
---|
2128 | .I /etc/inetd.conf |
---|
2129 | for RPC services is in one of the following two forms: |
---|
2130 | .ie t .DS |
---|
2131 | .el .DS L |
---|
2132 | .ft CW |
---|
2133 | p_name/version dgram rpc/udp wait/nowait user server args |
---|
2134 | p_name/version stream rpc/tcp wait/nowait user server args |
---|
2135 | .DE |
---|
2136 | where |
---|
2137 | .I p_name |
---|
2138 | is the symbolic name of the program as it appears in |
---|
2139 | .I rpc(5) , |
---|
2140 | .I server |
---|
2141 | is the program implementing the server, |
---|
2142 | and |
---|
2143 | .I program |
---|
2144 | and |
---|
2145 | .I version |
---|
2146 | are the program and version numbers of the service. |
---|
2147 | For more information, see |
---|
2148 | .I inetd.conf(5) . |
---|
2149 | .LP |
---|
2150 | If the same program handles multiple versions, |
---|
2151 | then the version number can be a range, |
---|
2152 | as in this example: |
---|
2153 | .ie t .DS |
---|
2154 | .el .DS L |
---|
2155 | .ft CW |
---|
2156 | rstatd/1-2 dgram rpc/udp wait root /usr/etc/rpc.rstatd |
---|
2157 | .DE |
---|
2158 | .NH 1 |
---|
2159 | \&More Examples |
---|
2160 | .sp 1 |
---|
2161 | .NH 2 |
---|
2162 | \&Versions |
---|
2163 | .IX "versions" |
---|
2164 | .IX "RPC" "versions" |
---|
2165 | .LP |
---|
2166 | By convention, the first version number of program |
---|
2167 | .I PROG |
---|
2168 | is |
---|
2169 | .I PROGVERS_ORIG |
---|
2170 | and the most recent version is |
---|
2171 | .I PROGVERS |
---|
2172 | Suppose there is a new version of the |
---|
2173 | .I user |
---|
2174 | program that returns an |
---|
2175 | .I "unsigned short" |
---|
2176 | rather than a |
---|
2177 | .I long . |
---|
2178 | If we name this version |
---|
2179 | .I RUSERSVERS_SHORT |
---|
2180 | then a server that wants to support both versions |
---|
2181 | would do a double register. |
---|
2182 | .ie t .DS |
---|
2183 | .el .DS L |
---|
2184 | .ft CW |
---|
2185 | if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, |
---|
2186 | nuser, IPPROTO_TCP)) { |
---|
2187 | fprintf(stderr, "can't register RUSER service\en"); |
---|
2188 | exit(1); |
---|
2189 | } |
---|
2190 | if (!svc_register(transp, RUSERSPROG, RUSERSVERS_SHORT, |
---|
2191 | nuser, IPPROTO_TCP)) { |
---|
2192 | fprintf(stderr, "can't register RUSER service\en"); |
---|
2193 | exit(1); |
---|
2194 | } |
---|
2195 | .DE |
---|
2196 | Both versions can be handled by the same C procedure: |
---|
2197 | .ie t .DS |
---|
2198 | .el .DS L |
---|
2199 | .ft CW |
---|
2200 | .vs 11 |
---|
2201 | nuser(rqstp, transp) |
---|
2202 | struct svc_req *rqstp; |
---|
2203 | SVCXPRT *transp; |
---|
2204 | { |
---|
2205 | unsigned long nusers; |
---|
2206 | unsigned short nusers2; |
---|
2207 | |
---|
2208 | switch (rqstp->rq_proc) { |
---|
2209 | case NULLPROC: |
---|
2210 | if (!svc_sendreply(transp, xdr_void, 0)) { |
---|
2211 | fprintf(stderr, "can't reply to RPC call\en"); |
---|
2212 | return (1); |
---|
2213 | } |
---|
2214 | return; |
---|
2215 | case RUSERSPROC_NUM: |
---|
2216 | .ft I |
---|
2217 | /* |
---|
2218 | * Code here to compute the number of users |
---|
2219 | * and assign it to the variable \fInusers\fP |
---|
2220 | */ |
---|
2221 | .ft CW |
---|
2222 | nusers2 = nusers; |
---|
2223 | switch (rqstp->rq_vers) { |
---|
2224 | case RUSERSVERS_ORIG: |
---|
2225 | if (!svc_sendreply(transp, xdr_u_long, |
---|
2226 | &nusers)) { |
---|
2227 | fprintf(stderr,"can't reply to RPC call\en"); |
---|
2228 | } |
---|
2229 | break; |
---|
2230 | case RUSERSVERS_SHORT: |
---|
2231 | if (!svc_sendreply(transp, xdr_u_short, |
---|
2232 | &nusers2)) { |
---|
2233 | fprintf(stderr,"can't reply to RPC call\en"); |
---|
2234 | } |
---|
2235 | break; |
---|
2236 | } |
---|
2237 | default: |
---|
2238 | svcerr_noproc(transp); |
---|
2239 | return; |
---|
2240 | } |
---|
2241 | } |
---|
2242 | .vs |
---|
2243 | .DE |
---|
2244 | .KS |
---|
2245 | .NH 2 |
---|
2246 | \&TCP |
---|
2247 | .IX "TCP" |
---|
2248 | .LP |
---|
2249 | Here is an example that is essentially |
---|
2250 | .I rcp. |
---|
2251 | The initiator of the RPC |
---|
2252 | .I snd |
---|
2253 | call takes its standard input and sends it to the server |
---|
2254 | .I rcv |
---|
2255 | which prints it on standard output. |
---|
2256 | The RPC call uses TCP. |
---|
2257 | This also illustrates an XDR procedure that behaves differently |
---|
2258 | on serialization than on deserialization. |
---|
2259 | .ie t .DS |
---|
2260 | .el .DS L |
---|
2261 | .vs 11 |
---|
2262 | .ft I |
---|
2263 | /* |
---|
2264 | * The xdr routine: |
---|
2265 | * on decode, read from wire, write onto fp |
---|
2266 | * on encode, read from fp, write onto wire |
---|
2267 | */ |
---|
2268 | .ft CW |
---|
2269 | #include <stdio.h> |
---|
2270 | #include <rpc/rpc.h> |
---|
2271 | |
---|
2272 | xdr_rcp(xdrs, fp) |
---|
2273 | XDR *xdrs; |
---|
2274 | FILE *fp; |
---|
2275 | { |
---|
2276 | unsigned long size; |
---|
2277 | char buf[BUFSIZ], *p; |
---|
2278 | |
---|
2279 | if (xdrs->x_op == XDR_FREE)/* nothing to free */ |
---|
2280 | return 1; |
---|
2281 | while (1) { |
---|
2282 | if (xdrs->x_op == XDR_ENCODE) { |
---|
2283 | if ((size = fread(buf, sizeof(char), BUFSIZ, |
---|
2284 | fp)) == 0 && ferror(fp)) { |
---|
2285 | fprintf(stderr, "can't fread\en"); |
---|
2286 | return (1); |
---|
2287 | } |
---|
2288 | } |
---|
2289 | p = buf; |
---|
2290 | if (!xdr_bytes(xdrs, &p, &size, BUFSIZ)) |
---|
2291 | return 0; |
---|
2292 | if (size == 0) |
---|
2293 | return 1; |
---|
2294 | if (xdrs->x_op == XDR_DECODE) { |
---|
2295 | if (fwrite(buf, sizeof(char), size, |
---|
2296 | fp) != size) { |
---|
2297 | fprintf(stderr, "can't fwrite\en"); |
---|
2298 | return (1); |
---|
2299 | } |
---|
2300 | } |
---|
2301 | } |
---|
2302 | } |
---|
2303 | .vs |
---|
2304 | .DE |
---|
2305 | .KE |
---|
2306 | .ie t .DS |
---|
2307 | .el .DS L |
---|
2308 | .vs 11 |
---|
2309 | .ft I |
---|
2310 | /* |
---|
2311 | * The sender routines |
---|
2312 | */ |
---|
2313 | .ft CW |
---|
2314 | #include <stdio.h> |
---|
2315 | #include <netdb.h> |
---|
2316 | #include <rpc/rpc.h> |
---|
2317 | #include <sys/socket.h> |
---|
2318 | #include <sys/time.h> |
---|
2319 | |
---|
2320 | main(argc, argv) |
---|
2321 | int argc; |
---|
2322 | char **argv; |
---|
2323 | { |
---|
2324 | int xdr_rcp(); |
---|
2325 | int err; |
---|
2326 | |
---|
2327 | if (argc < 2) { |
---|
2328 | fprintf(stderr, "usage: %s servername\en", argv[0]); |
---|
2329 | exit(-1); |
---|
2330 | } |
---|
2331 | if ((err = callrpctcp(argv[1], RCPPROG, RCPPROC, |
---|
2332 | RCPVERS, xdr_rcp, stdin, xdr_void, 0) != 0)) { |
---|
2333 | clnt_perrno(err); |
---|
2334 | fprintf(stderr, "can't make RPC call\en"); |
---|
2335 | exit(1); |
---|
2336 | } |
---|
2337 | exit(0); |
---|
2338 | } |
---|
2339 | |
---|
2340 | callrpctcp(host, prognum, procnum, versnum, |
---|
2341 | inproc, in, outproc, out) |
---|
2342 | char *host, *in, *out; |
---|
2343 | xdrproc_t inproc, outproc; |
---|
2344 | { |
---|
2345 | struct sockaddr_in server_addr; |
---|
2346 | int socket = RPC_ANYSOCK; |
---|
2347 | enum clnt_stat clnt_stat; |
---|
2348 | struct hostent *hp; |
---|
2349 | register CLIENT *client; |
---|
2350 | struct timeval total_timeout; |
---|
2351 | |
---|
2352 | if ((hp = gethostbyname(host)) == NULL) { |
---|
2353 | fprintf(stderr, "can't get addr for '%s'\en", host); |
---|
2354 | return (-1); |
---|
2355 | } |
---|
2356 | bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, |
---|
2357 | hp->h_length); |
---|
2358 | server_addr.sin_family = AF_INET; |
---|
2359 | server_addr.sin_port = 0; |
---|
2360 | if ((client = clnttcp_create(&server_addr, prognum, |
---|
2361 | versnum, &socket, BUFSIZ, BUFSIZ)) == NULL) { |
---|
2362 | perror("rpctcp_create"); |
---|
2363 | return (-1); |
---|
2364 | } |
---|
2365 | total_timeout.tv_sec = 20; |
---|
2366 | total_timeout.tv_usec = 0; |
---|
2367 | clnt_stat = clnt_call(client, procnum, |
---|
2368 | inproc, in, outproc, out, total_timeout); |
---|
2369 | clnt_destroy(client); |
---|
2370 | return (int)clnt_stat; |
---|
2371 | } |
---|
2372 | .vs |
---|
2373 | .DE |
---|
2374 | .ie t .DS |
---|
2375 | .el .DS L |
---|
2376 | .vs 11 |
---|
2377 | .ft I |
---|
2378 | /* |
---|
2379 | * The receiving routines |
---|
2380 | */ |
---|
2381 | .ft CW |
---|
2382 | #include <stdio.h> |
---|
2383 | #include <rpc/rpc.h> |
---|
2384 | |
---|
2385 | main() |
---|
2386 | { |
---|
2387 | register SVCXPRT *transp; |
---|
2388 | int rcp_service(), xdr_rcp(); |
---|
2389 | |
---|
2390 | if ((transp = svctcp_create(RPC_ANYSOCK, |
---|
2391 | BUFSIZ, BUFSIZ)) == NULL) { |
---|
2392 | fprintf("svctcp_create: error\en"); |
---|
2393 | exit(1); |
---|
2394 | } |
---|
2395 | pmap_unset(RCPPROG, RCPVERS); |
---|
2396 | if (!svc_register(transp, |
---|
2397 | RCPPROG, RCPVERS, rcp_service, IPPROTO_TCP)) { |
---|
2398 | fprintf(stderr, "svc_register: error\en"); |
---|
2399 | exit(1); |
---|
2400 | } |
---|
2401 | svc_run(); /* \fInever returns\fP */ |
---|
2402 | fprintf(stderr, "svc_run should never return\en"); |
---|
2403 | } |
---|
2404 | |
---|
2405 | rcp_service(rqstp, transp) |
---|
2406 | register struct svc_req *rqstp; |
---|
2407 | register SVCXPRT *transp; |
---|
2408 | { |
---|
2409 | switch (rqstp->rq_proc) { |
---|
2410 | case NULLPROC: |
---|
2411 | if (svc_sendreply(transp, xdr_void, 0) == 0) { |
---|
2412 | fprintf(stderr, "err: rcp_service"); |
---|
2413 | return (1); |
---|
2414 | } |
---|
2415 | return; |
---|
2416 | case RCPPROC_FP: |
---|
2417 | if (!svc_getargs(transp, xdr_rcp, stdout)) { |
---|
2418 | svcerr_decode(transp); |
---|
2419 | return; |
---|
2420 | } |
---|
2421 | if (!svc_sendreply(transp, xdr_void, 0)) { |
---|
2422 | fprintf(stderr, "can't reply\en"); |
---|
2423 | return; |
---|
2424 | } |
---|
2425 | return (0); |
---|
2426 | default: |
---|
2427 | svcerr_noproc(transp); |
---|
2428 | return; |
---|
2429 | } |
---|
2430 | } |
---|
2431 | .vs |
---|
2432 | .DE |
---|
2433 | .NH 2 |
---|
2434 | \&Callback Procedures |
---|
2435 | .IX RPC "callback procedures" |
---|
2436 | .LP |
---|
2437 | Occasionally, it is useful to have a server become a client, |
---|
2438 | and make an RPC call back to the process which is its client. |
---|
2439 | An example is remote debugging, |
---|
2440 | where the client is a window system program, |
---|
2441 | and the server is a debugger running on the remote machine. |
---|
2442 | Most of the time, |
---|
2443 | the user clicks a mouse button at the debugging window, |
---|
2444 | which converts this to a debugger command, |
---|
2445 | and then makes an RPC call to the server |
---|
2446 | (where the debugger is actually running), |
---|
2447 | telling it to execute that command. |
---|
2448 | However, when the debugger hits a breakpoint, the roles are reversed, |
---|
2449 | and the debugger wants to make an rpc call to the window program, |
---|
2450 | so that it can inform the user that a breakpoint has been reached. |
---|
2451 | .LP |
---|
2452 | In order to do an RPC callback, |
---|
2453 | you need a program number to make the RPC call on. |
---|
2454 | Since this will be a dynamically generated program number, |
---|
2455 | it should be in the transient range, |
---|
2456 | .I "0x40000000 - 0x5fffffff" . |
---|
2457 | The routine |
---|
2458 | .I gettransient() |
---|
2459 | returns a valid program number in the transient range, |
---|
2460 | and registers it with the portmapper. |
---|
2461 | It only talks to the portmapper running on the same machine as the |
---|
2462 | .I gettransient() |
---|
2463 | routine itself. The call to |
---|
2464 | .I pmap_set() |
---|
2465 | is a test and set operation, |
---|
2466 | in that it indivisibly tests whether a program number |
---|
2467 | has already been registered, |
---|
2468 | and if it has not, then reserves it. On return, the |
---|
2469 | .I sockp |
---|
2470 | argument will contain a socket that can be used |
---|
2471 | as the argument to an |
---|
2472 | .I svcudp_create() |
---|
2473 | or |
---|
2474 | .I svctcp_create() |
---|
2475 | call. |
---|
2476 | .ie t .DS |
---|
2477 | .el .DS L |
---|
2478 | .ft CW |
---|
2479 | .vs 11 |
---|
2480 | #include <stdio.h> |
---|
2481 | #include <rpc/rpc.h> |
---|
2482 | #include <sys/socket.h> |
---|
2483 | |
---|
2484 | gettransient(proto, vers, sockp) |
---|
2485 | int proto, vers, *sockp; |
---|
2486 | { |
---|
2487 | static int prognum = 0x40000000; |
---|
2488 | int s, len, socktype; |
---|
2489 | struct sockaddr_in addr; |
---|
2490 | |
---|
2491 | switch(proto) { |
---|
2492 | case IPPROTO_UDP: |
---|
2493 | socktype = SOCK_DGRAM; |
---|
2494 | break; |
---|
2495 | case IPPROTO_TCP: |
---|
2496 | socktype = SOCK_STREAM; |
---|
2497 | break; |
---|
2498 | default: |
---|
2499 | fprintf(stderr, "unknown protocol type\en"); |
---|
2500 | return 0; |
---|
2501 | } |
---|
2502 | if (*sockp == RPC_ANYSOCK) { |
---|
2503 | if ((s = socket(AF_INET, socktype, 0)) < 0) { |
---|
2504 | perror("socket"); |
---|
2505 | return (0); |
---|
2506 | } |
---|
2507 | *sockp = s; |
---|
2508 | } |
---|
2509 | else |
---|
2510 | s = *sockp; |
---|
2511 | addr.sin_addr.s_addr = 0; |
---|
2512 | addr.sin_family = AF_INET; |
---|
2513 | addr.sin_port = 0; |
---|
2514 | len = sizeof(addr); |
---|
2515 | .ft I |
---|
2516 | /* |
---|
2517 | * may be already bound, so don't check for error |
---|
2518 | */ |
---|
2519 | .ft CW |
---|
2520 | bind(s, &addr, len); |
---|
2521 | if (getsockname(s, &addr, &len)< 0) { |
---|
2522 | perror("getsockname"); |
---|
2523 | return (0); |
---|
2524 | } |
---|
2525 | while (!pmap_set(prognum++, vers, proto, |
---|
2526 | ntohs(addr.sin_port))) continue; |
---|
2527 | return (prognum-1); |
---|
2528 | } |
---|
2529 | .vs |
---|
2530 | .DE |
---|
2531 | .SH |
---|
2532 | Note: |
---|
2533 | .I |
---|
2534 | The call to |
---|
2535 | .I ntohs() |
---|
2536 | is necessary to ensure that the port number in |
---|
2537 | .I "addr.sin_port" , |
---|
2538 | which is in |
---|
2539 | .I network |
---|
2540 | byte order, is passed in |
---|
2541 | .I host |
---|
2542 | byte order (as |
---|
2543 | .I pmap_set() |
---|
2544 | expects). See the |
---|
2545 | .I byteorder(3N) |
---|
2546 | man page for more details on the conversion of network |
---|
2547 | addresses from network to host byte order. |
---|
2548 | .KS |
---|
2549 | .LP |
---|
2550 | The following pair of programs illustrate how to use the |
---|
2551 | .I gettransient() |
---|
2552 | routine. |
---|
2553 | The client makes an RPC call to the server, |
---|
2554 | passing it a transient program number. |
---|
2555 | Then the client waits around to receive a callback |
---|
2556 | from the server at that program number. |
---|
2557 | The server registers the program |
---|
2558 | .I EXAMPLEPROG |
---|
2559 | so that it can receive the RPC call |
---|
2560 | informing it of the callback program number. |
---|
2561 | Then at some random time (on receiving an |
---|
2562 | .I ALRM |
---|
2563 | signal in this example), it sends a callback RPC call, |
---|
2564 | using the program number it received earlier. |
---|
2565 | .ie t .DS |
---|
2566 | .el .DS L |
---|
2567 | .vs 11 |
---|
2568 | .ft I |
---|
2569 | /* |
---|
2570 | * client |
---|
2571 | */ |
---|
2572 | .ft CW |
---|
2573 | #include <stdio.h> |
---|
2574 | #include <rpc/rpc.h> |
---|
2575 | |
---|
2576 | int callback(); |
---|
2577 | char hostname[256]; |
---|
2578 | |
---|
2579 | main() |
---|
2580 | { |
---|
2581 | int x, ans, s; |
---|
2582 | SVCXPRT *xprt; |
---|
2583 | |
---|
2584 | gethostname(hostname, sizeof(hostname)); |
---|
2585 | s = RPC_ANYSOCK; |
---|
2586 | x = gettransient(IPPROTO_UDP, 1, &s); |
---|
2587 | fprintf(stderr, "client gets prognum %d\en", x); |
---|
2588 | if ((xprt = svcudp_create(s)) == NULL) { |
---|
2589 | fprintf(stderr, "rpc_server: svcudp_create\en"); |
---|
2590 | exit(1); |
---|
2591 | } |
---|
2592 | .ft I |
---|
2593 | /* protocol is 0 - gettransient does registering |
---|
2594 | */ |
---|
2595 | .ft CW |
---|
2596 | (void)svc_register(xprt, x, 1, callback, 0); |
---|
2597 | ans = callrpc(hostname, EXAMPLEPROG, EXAMPLEVERS, |
---|
2598 | EXAMPLEPROC_CALLBACK, xdr_int, &x, xdr_void, 0); |
---|
2599 | if ((enum clnt_stat) ans != RPC_SUCCESS) { |
---|
2600 | fprintf(stderr, "call: "); |
---|
2601 | clnt_perrno(ans); |
---|
2602 | fprintf(stderr, "\en"); |
---|
2603 | } |
---|
2604 | svc_run(); |
---|
2605 | fprintf(stderr, "Error: svc_run shouldn't return\en"); |
---|
2606 | } |
---|
2607 | |
---|
2608 | callback(rqstp, transp) |
---|
2609 | register struct svc_req *rqstp; |
---|
2610 | register SVCXPRT *transp; |
---|
2611 | { |
---|
2612 | switch (rqstp->rq_proc) { |
---|
2613 | case 0: |
---|
2614 | if (!svc_sendreply(transp, xdr_void, 0)) { |
---|
2615 | fprintf(stderr, "err: exampleprog\en"); |
---|
2616 | return (1); |
---|
2617 | } |
---|
2618 | return (0); |
---|
2619 | case 1: |
---|
2620 | if (!svc_getargs(transp, xdr_void, 0)) { |
---|
2621 | svcerr_decode(transp); |
---|
2622 | return (1); |
---|
2623 | } |
---|
2624 | fprintf(stderr, "client got callback\en"); |
---|
2625 | if (!svc_sendreply(transp, xdr_void, 0)) { |
---|
2626 | fprintf(stderr, "err: exampleprog"); |
---|
2627 | return (1); |
---|
2628 | } |
---|
2629 | } |
---|
2630 | } |
---|
2631 | .vs |
---|
2632 | .DE |
---|
2633 | .KE |
---|
2634 | .ie t .DS |
---|
2635 | .el .DS L |
---|
2636 | .vs 11 |
---|
2637 | .ft I |
---|
2638 | /* |
---|
2639 | * server |
---|
2640 | */ |
---|
2641 | .ft CW |
---|
2642 | #include <stdio.h> |
---|
2643 | #include <rpc/rpc.h> |
---|
2644 | #include <sys/signal.h> |
---|
2645 | |
---|
2646 | char *getnewprog(); |
---|
2647 | char hostname[256]; |
---|
2648 | int docallback(); |
---|
2649 | int pnum; /* \fIprogram number for callback routine\fP */ |
---|
2650 | |
---|
2651 | main() |
---|
2652 | { |
---|
2653 | gethostname(hostname, sizeof(hostname)); |
---|
2654 | registerrpc(EXAMPLEPROG, EXAMPLEVERS, |
---|
2655 | EXAMPLEPROC_CALLBACK, getnewprog, xdr_int, xdr_void); |
---|
2656 | fprintf(stderr, "server going into svc_run\en"); |
---|
2657 | signal(SIGALRM, docallback); |
---|
2658 | alarm(10); |
---|
2659 | svc_run(); |
---|
2660 | fprintf(stderr, "Error: svc_run shouldn't return\en"); |
---|
2661 | } |
---|
2662 | |
---|
2663 | char * |
---|
2664 | getnewprog(pnump) |
---|
2665 | char *pnump; |
---|
2666 | { |
---|
2667 | pnum = *(int *)pnump; |
---|
2668 | return NULL; |
---|
2669 | } |
---|
2670 | |
---|
2671 | docallback() |
---|
2672 | { |
---|
2673 | int ans; |
---|
2674 | |
---|
2675 | ans = callrpc(hostname, pnum, 1, 1, xdr_void, 0, |
---|
2676 | xdr_void, 0); |
---|
2677 | if (ans != 0) { |
---|
2678 | fprintf(stderr, "server: "); |
---|
2679 | clnt_perrno(ans); |
---|
2680 | fprintf(stderr, "\en"); |
---|
2681 | } |
---|
2682 | } |
---|
2683 | .vs |
---|
2684 | .DE |
---|