|
|
1.1 ! root 1: .OH 'RPC Programming''Page \\\\n(PN' ! 2: .EH 'Page \\\\n(PN''RPC Programming' ! 3: .OF 'Sun Microsystems''Release 2.0' ! 4: .EF 'Release 2.0''Sun Microsystems' ! 5: .RP ! 6: .rm DY ! 7: .TL ! 8: .ps 20 ! 9: Remote Procedure Call ! 10: .sp.5 ! 11: Programming Guide ! 12: . ! 13: .H 1 "Introduction" ! 14: .LP ! 15: Programs that communicate over a network ! 16: need a paradigm for communication. ! 17: A low-level mechanism might ! 18: send a signal on the arrival of incoming packets, ! 19: causing a network signal handler to execute. ! 20: A high-level mechanism would be the Ada ! 21: .L rendezvous . ! 22: The method used at Sun is the ! 23: Remote Procedure Call (RPC) paradigm, ! 24: in which a client communicates with a server. ! 25: In this process, ! 26: the client first calls a procedure to send a data packet to the server. ! 27: When the packet arrives, the server calls a dispatch routine, ! 28: performs whatever service is requested, sends back the reply, ! 29: and the procedure call returns to the client. ! 30: .LP ! 31: The RPC interface is divided into three layers. ! 32: The highest layer is totally transparent to the programmer. ! 33: To illustrate, ! 34: at this level a program can contain a call to ! 35: .L rnusers() , ! 36: which returns the number of users on a remote machine. ! 37: You don't have to be aware that RPC is being used, ! 38: since you simply make the call in a program, ! 39: just as you would call ! 40: .L malloc() . ! 41: .LP ! 42: At the middle layer, the routines ! 43: .L registerrpc() ! 44: and ! 45: .L callrpc() ! 46: are used to make RPC calls: ! 47: .L registerrpc() ! 48: obtains a unique system-wide number, while ! 49: .L callrpc() ! 50: executes a remote procedure call. ! 51: The ! 52: .L rnusers() ! 53: call is implemented using these two routines ! 54: The middle-layer routines are designed for most common applications, ! 55: and shield the user from knowing about sockets. ! 56: .LP ! 57: The lowest layer is used for more sophisticated applications, ! 58: which may want to alter the defaults of the routines. ! 59: At this layer, you can explicitly manipulate ! 60: sockets used for transmitting RPC messages. ! 61: This level should be avoided if possible. ! 62: .LP ! 63: Section 2 of this manual illustrates use of the highest two layers ! 64: while Section 3 presents the low-level interface. ! 65: Section 4 of the manual discusses miscellaneous topics. ! 66: The final section summarizes ! 67: all the entry points into the RPC system. ! 68: .LP ! 69: Although this document only discusses the interface to C, ! 70: remote procedure calls can be made from any language. ! 71: Even though this document discusses RPC ! 72: when it is used to communicate ! 73: between processes on different machines, ! 74: it works just as well for communication ! 75: between different processes on the same machine. ! 76: .bp ! 77: . ! 78: .H 1 "Introductory Examples" ! 79: .H 2 "Highest Layer" ! 80: .LP ! 81: Imagine you're writing a program that needs to know ! 82: how many users are logged into a remote machine. ! 83: You can do this by calling the library routine ! 84: .L rnusers() , ! 85: as illustrated below: ! 86: .LS ! 87: #include <stdio.h> ! 88: .sp.5 ! 89: main(argc, argv) ! 90: int argc; ! 91: char **argv; ! 92: { ! 93: unsigned num; ! 94: .sp.5 ! 95: if (argc < 2) { ! 96: fprintf(stderr, "usage: rnusers hostname\en"); ! 97: exit(1); ! 98: } ! 99: if ((num = rnusers(argv[1])) < 0) { ! 100: fprintf(stderr, "error: rnusers\en"); ! 101: exit(-1); ! 102: } ! 103: printf("%d users on %s\en", num, argv[1]); ! 104: exit(0); ! 105: } ! 106: .LE ! 107: RPC library routines such as ! 108: .L rnusers() ! 109: are included in the C library ! 110: .L libc.a . ! 111: Thus, the program above could be compiled with ! 112: .LS ! 113: % cc \fIprogram.c\fP ! 114: .LE ! 115: Some other library routines are ! 116: .L rstat() ! 117: to gather remote performance statistics, and ! 118: .L ypmatch() ! 119: to glean information from the yellow pages (YP). ! 120: The YP library routines are documented on the manual page ! 121: .I ypclnt (3N). ! 122: .bp ! 123: . ! 124: .H 2 "Intermediate Layer" ! 125: .LP ! 126: The simplest interface, which explicitly makes RPC ! 127: calls, uses the functions ! 128: .L callrpc() ! 129: and ! 130: .L registerrpc() . ! 131: Using this method, another way to get the number of remote users is: ! 132: .LS ! 133: #include <stdio.h> ! 134: #include <rpcsvc/rusers.h> ! 135: .sp.5 ! 136: main(argc, argv) ! 137: int argc; ! 138: char **argv; ! 139: { ! 140: unsigned long nusers; ! 141: .sp.5 ! 142: if (argc < 2) { ! 143: fprintf(stderr, "usage: nusers hostname\en"); ! 144: exit(-1); ! 145: } ! 146: if (callrpc(argv[1], RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, ! 147: xdr_void, 0, xdr_u_long, &nusers) != 0) { ! 148: fprintf(stderr, "error: callrpc\en"); ! 149: exit(1); ! 150: } ! 151: printf("number of users on %s is %d\en", argv[1], nusers); ! 152: exit(0); ! 153: } ! 154: .LE ! 155: A program number, version number, and procedure number ! 156: defines each RPC procedure. ! 157: The program number defines a group ! 158: of related remote procedures, each of which has a different ! 159: procedure number. ! 160: Each program also has a version number, ! 161: so when a minor change is made to a remote service ! 162: (adding a new procedure, for example), ! 163: a new program number doesn't have to be assigned. ! 164: When you want to call a procedure to ! 165: find the number of remote users, you look up the appropriate ! 166: program, version and procedure numbers ! 167: in a manual, similar to when you look up the name of memory ! 168: allocator when you want to allocate memory. ! 169: .LP ! 170: The simplest routine in the RPC library ! 171: used to make remote procedure calls is ! 172: .L callrpc() . ! 173: It has eight parameters. ! 174: The first is the name of the remote machine. ! 175: The next three parameters ! 176: are the program, version, and procedure numbers. ! 177: The following two parameters ! 178: define the argument of the RPC call, and the final two parameters ! 179: are for the return value of the call. ! 180: If it completes successfully, ! 181: .L callrpc() ! 182: returns zero, but nonzero otherwise. ! 183: The exact meaning of the return codes is found in ! 184: .L <rpc/clnt.h> , ! 185: and is in fact an ! 186: .L "enum clnt_stat" ! 187: cast into an integer. ! 188: .LP ! 189: Since data types may be represented differently on different machines, ! 190: .L callrpc() ! 191: needs both the type of the RPC argument, as well as ! 192: a pointer to the argument itself (and similarly for the result). ! 193: For RUSERSPROC_NUM, the return value is an ! 194: .L "unsigned long" , ! 195: so ! 196: .L callrpc() ! 197: has ! 198: .L xdr_u_long ! 199: as its first return parameter, which says ! 200: that the result is of type ! 201: .L "unsigned long", ! 202: and ! 203: .L &nusers ! 204: as its second return parameter, ! 205: which is a pointer to where the long result will be placed. ! 206: Since RUSERSPROC_NUM takes no argument, the argument parameter of ! 207: .L callrpc() ! 208: is ! 209: .L xdr_void . ! 210: .LP ! 211: After trying several times to deliver a message, if ! 212: .L callrpc() ! 213: gets no answer, it returns with an error code. ! 214: The delivery mechanism is UDP, ! 215: which stands for User Datagram Protocol. ! 216: Methods for adjusting the number of retries ! 217: or for using a different protocol require you to use the lower ! 218: layer of the RPC library, discussed later in this document. ! 219: The remote server procedure ! 220: corresponding to the above might look like this: ! 221: .LS ! 222: char * ! 223: nuser(indata) ! 224: char *indata; ! 225: { ! 226: static int nusers; ! 227: .sp.5 ! 228: /* ! 229: * code here to compute the number of users ! 230: * and place result in variable nusers ! 231: */ ! 232: return ((char *)&nusers); ! 233: } ! 234: .LE ! 235: .LP ! 236: It takes one argument, which is a pointer to the input ! 237: of the remote procedure call (ignored in our example), ! 238: and it returns a pointer to the result. ! 239: In the current version of C, ! 240: character pointers are the generic pointers, ! 241: so both the input argument and the return value are cast to ! 242: .L "char *" . ! 243: .LP ! 244: Normally, a server registers all of the RPC calls it plans ! 245: to handle, and then goes into an infinite loop waiting to service requests. ! 246: In this example, there is only a single procedure ! 247: to register, so the main body of the server would look like this: ! 248: .LS ! 249: #include <stdio.h> ! 250: #include <rpcsvc/rusers.h> ! 251: .sp.5 ! 252: char *nuser(); ! 253: .sp.5 ! 254: main() ! 255: { ! 256: registerrpc(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, nuser, ! 257: xdr_void, xdr_u_long); ! 258: svc_run(); /* never returns */ ! 259: fprintf(stderr, "Error: svc_run returned!\en"); ! 260: exit(1); ! 261: } ! 262: .LE ! 263: .LP ! 264: The ! 265: .L registerrpc() ! 266: routine establishes what C procedure ! 267: corresponds to each RPC procedure number. ! 268: The first three parameters, ! 269: RUSERPROG, RUSERSVERS, and RUSERSPROC_NUM ! 270: are the program, version, and procedure numbers ! 271: of the remote procedure to be registered; ! 272: .L nuser ! 273: is the name of the C procedure implementing it; ! 274: and ! 275: .L xdr_void ! 276: and ! 277: .L xdr_u_long ! 278: are the types of the input to and output from the procedure. ! 279: .LP ! 280: Only the UDP transport mechanism can use ! 281: .L registerrpc() ; ! 282: thus, it is always safe in conjunction with calls generated by ! 283: .L callrpc() . ! 284: .LP ! 285: Warning: the UDP transport mechanism can only deal with ! 286: arguments and results less than 8K bytes in length. ! 287: . ! 288: .H 2 "Assigning Program Numbers" ! 289: .LP ! 290: Program numbers are assigned in groups of 0x20000000 (536870912) ! 291: according to the following chart: ! 292: .LS ! 293: 0 - 1fffffff defined by sun ! 294: 20000000 - 3fffffff defined by user ! 295: 40000000 - 5fffffff transient ! 296: 60000000 - 7fffffff reserved ! 297: 80000000 - 9fffffff reserved ! 298: a0000000 - bfffffff reserved ! 299: c0000000 - dfffffff reserved ! 300: e0000000 - ffffffff reserved ! 301: .LE ! 302: Sun Microsystems administers the first group of numbers, ! 303: which should be identical for all Sun customers. ! 304: If a customer develops an application that might be of general ! 305: interest, that application should be given an assigned number ! 306: in the first range. ! 307: The second group of numbers is reserved for specific customer ! 308: applications. ! 309: This range is intended primarily for debugging new programs. ! 310: The third group is reserved for applications that ! 311: generate program numbers dynamically. ! 312: The final groups ! 313: are reserved for future use, and should not be used. ! 314: .LP ! 315: The exact registration process for Sun defined numbers is yet ! 316: to be established. ! 317: . ! 318: .H 2 "Passing Arbitrary Data Types" ! 319: .LP ! 320: In the previous example, the RPC call passes a single ! 321: .L "unsigned long." ! 322: RPC can handle arbitrary data structures, regardless of ! 323: different machines' byte orders or structure layout conventions, ! 324: by always converting them to a network standard called ! 325: .I "eXternal Data Representation" ! 326: (XDR) before ! 327: sending them over the wire. ! 328: The process of converting from a particular machine representation ! 329: to XDR format is called ! 330: .I serializing , ! 331: and the reverse process is called ! 332: .I deserializing . ! 333: The type field parameters of ! 334: .L callrpc() ! 335: and ! 336: .L registerrpc() ! 337: can be a built-in procedure like ! 338: .L xdr_u_long() ! 339: in the previous example, or a user supplied one. ! 340: XDR has these built-in type routines: ! 341: .LS ! 342: xdr_int() xdr_u_int() xdr_enum() ! 343: xdr_long() xdr_u_long() xdr_bool() ! 344: xdr_short() xdr_u_short() xdr_string() ! 345: .LE ! 346: As an example of a user-defined type routine, ! 347: if you wanted to send the structure ! 348: .LS ! 349: struct simple { ! 350: int a; ! 351: short b; ! 352: } simple; ! 353: .LE ! 354: then you would call ! 355: .L callrpc ! 356: as ! 357: .LS ! 358: callrpc(hostname, PROGNUM, VERSNUM, PROCNUM, xdr_simple, &simple ...); ! 359: .LE ! 360: where ! 361: .L xdr_simple() ! 362: is written as: ! 363: .LS ! 364: #include <rpc/rpc.h> ! 365: .sp.5 ! 366: xdr_simple(xdrsp, simplep) ! 367: XDR *xdrsp; ! 368: struct simple *simplep; ! 369: { ! 370: if (!xdr_int(xdrsp, &simplep->a)) ! 371: return (0); ! 372: if (!xdr_short(xdrsp, &simplep->b)) ! 373: return (0); ! 374: return (1); ! 375: } ! 376: .LE ! 377: .LP ! 378: An XDR routine returns nonzero (true in the sense of C) ! 379: if it completes successfully, and zero otherwise. ! 380: A complete description of XDR is in the ! 381: .I "XDR Protocol Specification" , ! 382: so this section only gives a few examples of XDR implementation. ! 383: .LP ! 384: In addition to the built-in primitives, ! 385: there are also the prefabricated building blocks: ! 386: .LS ! 387: xdr_array() xdr_bytes() ! 388: xdr_reference() xdr_union() ! 389: .LE ! 390: To send a variable array of integers, ! 391: you might package them up as a structure like this ! 392: .LS ! 393: struct varintarr { ! 394: int *data; ! 395: int arrlnth; ! 396: } arr; ! 397: .LE ! 398: and make an RPC call such as ! 399: .LS ! 400: callrpc(hostname, PROGNUM, VERSNUM, PROCNUM, xdr_varintarr, &arr...); ! 401: .LE ! 402: with ! 403: .L xdr_varintarr() ! 404: defined as: ! 405: .LS ! 406: xdr_varintarr(xdrsp, varintarr) ! 407: XDR *xdrsp; ! 408: struct varintarr *arrp; ! 409: { ! 410: xdr_array(xdrsp, &arrp->data, &arrp->arrlnth, MAXLEN, ! 411: sizeof(int), xdr_int); ! 412: } ! 413: .LE ! 414: This routine takes as parameters the XDR handle, ! 415: a pointer to the array, a pointer to the size of the array, ! 416: the maximum allowable array size, ! 417: the size of each array element, ! 418: and an XDR routine for handling each array element. ! 419: .LP ! 420: If the size of the array is known in advance, then ! 421: the following could also be used to send ! 422: out an array of length SIZE: ! 423: .LS ! 424: int intarr[SIZE]; ! 425: .sp.5 ! 426: xdr_intarr(xdrsp, intarr) ! 427: XDR *xdrsp; ! 428: int intarr[]; ! 429: { ! 430: int i; ! 431: .sp.5 ! 432: for (i = 0; i < SIZE; i++) { ! 433: if (!xdr_int(xdrsp, &intarr[i])) ! 434: return (0); ! 435: } ! 436: return (1); ! 437: } ! 438: .LE ! 439: .LP ! 440: XDR always converts quantities to 4-byte multiples when deserializing. ! 441: Thus, if either of the examples above involved characters ! 442: instead of integers, each character would occupy 32 bits. ! 443: That is the reason for the XDR routine ! 444: .L xdr_bytes() , ! 445: which is like ! 446: .L xdr_array() ! 447: except that it packs characters. ! 448: It has four parameters, ! 449: the same as the first four parameters of ! 450: .L xdr_array() . ! 451: For null-terminated strings, there is also the ! 452: .L xdr_string() ! 453: routine, which is the same as ! 454: .L xdr_bytes() ! 455: without the length parameter. ! 456: On serializing it gets the string length from ! 457: .L strlen() , ! 458: and on deserializing it creates a null-terminated string. ! 459: .LP ! 460: Here is a final example that calls the previously written ! 461: .L xdr_simple() ! 462: as well as the built-in functions ! 463: .L xdr_string() ! 464: and ! 465: .L xdr_reference() , ! 466: which chases pointers: ! 467: .LS ! 468: struct finalexample { ! 469: char *string; ! 470: struct simple *simplep; ! 471: } finalexample; ! 472: .LE ! 473: .LS ! 474: xdr_finalexample(xdrsp, finalp) ! 475: XDR *xdrsp; ! 476: struct finalexample *finalp; ! 477: { ! 478: int i; ! 479: .sp.5 ! 480: if (!xdr_string(xdrsp, &finalp->string, MAXSTRLEN)) ! 481: return (0); ! 482: if (!xdr_reference(xdrsp, &finalp->simplep, ! 483: sizeof(struct simple), xdr_simple); ! 484: return (0); ! 485: return (1); ! 486: } ! 487: .LE ! 488: .bp ! 489: . ! 490: .H 1 "Lower Layers of RPC" ! 491: .LP ! 492: In the examples given so far, ! 493: RPC takes care of many details automatically for you. ! 494: In this section, we'll show you how you can change the defaults ! 495: by using lower layers of the RPC library. ! 496: It is assumed that you are familiar with sockets ! 497: and the system calls for dealing with them. ! 498: If not, consult ! 499: .I "The IPC Tutorial" . ! 500: .H 2 "More on the Server Side" ! 501: .LP ! 502: There are a number of assumptions built into ! 503: .L registerrpc() . ! 504: One is that you are using the UDP datagram protocol. ! 505: Another is that ! 506: you don't want to do anything unusual while deserializing, ! 507: since the deserialization process happens automatically ! 508: before the user's server routine is called. ! 509: The server for the ! 510: .L nusers ! 511: program shown below ! 512: is written using a lower layer of the RPC package, ! 513: which does not make these assumptions. ! 514: .LS ! 515: #include <stdio.h> ! 516: #include <rpc/rpc.h> ! 517: #include <rpcsvc/rusers.h> ! 518: .sp.5 ! 519: int nuser(); ! 520: .sp.5 ! 521: main() ! 522: { ! 523: SVCXPRT *transp; ! 524: .sp.5 ! 525: transp = svcudp_create(RPC_ANYSOCK); ! 526: if (transp == NULL){ ! 527: fprintf(stderr, "couldn't create an RPC server\en"); ! 528: exit(1); ! 529: } ! 530: pmap_unset(RUSERSPROG, RUSERSVERS); ! 531: if (!svc_register(transp, RUSERSPROG, RUSERSVERS, nuser, ! 532: IPPROTO_UDP)) { ! 533: fprintf(stderr, "couldn't register RUSER service\en"); ! 534: exit(1); ! 535: } ! 536: svc_run(); /* never returns */ ! 537: fprintf(stderr, "should never reach this point\en"); ! 538: } ! 539: .LE ! 540: .LS ! 541: nuser(rqstp, tranp) ! 542: struct svc_req *rqstp; ! 543: SVCXPRT *transp; ! 544: { ! 545: unsigned long nusers; ! 546: .sp.5 ! 547: switch (rqstp->rq_proc) { ! 548: case NULLPROC: ! 549: if (!svc_sendreply(transp, xdr_void, 0)) { ! 550: fprintf(stderr, "couldn't reply to RPC call\en"); ! 551: exit(1); ! 552: } ! 553: return; ! 554: case RUSERSPROC_NUM: ! 555: /* ! 556: * code here to compute the number of users ! 557: * and put in variable nusers ! 558: */ ! 559: if (!svc_sendreply(transp, xdr_u_long, &nusers) { ! 560: fprintf(stderr, "couldn't reply to RPC call\en"); ! 561: exit(1); ! 562: } ! 563: return; ! 564: default: ! 565: svcerr_noproc(transp); ! 566: return; ! 567: } ! 568: } ! 569: .LE ! 570: .LP ! 571: First, the server gets a transport handle, which is used ! 572: for sending out RPC messages. ! 573: .L registerrpc() ! 574: uses ! 575: .L svcudp_create() ! 576: to get a UDP handle. ! 577: If you require a reliable protocol, call ! 578: .L svctcp_create() ! 579: instead. ! 580: If the argument to ! 581: .L svcudp_create() ! 582: is RPC_ANYSOCK, ! 583: the RPC library creates a socket ! 584: on which to send out RPC calls. ! 585: Otherwise, ! 586: .L svcudp_create() ! 587: expects its argument to be a valid socket number. ! 588: If you specify your own socket, it can be bound or unbound. ! 589: If it is bound to a port by the user, the port numbers of ! 590: .L svcudp_create() ! 591: and ! 592: .L clntudp_create() ! 593: (the low-level client routine) must match. ! 594: .LP ! 595: When the user specifies RPC_ANYSOCK for a socket ! 596: or gives an unbound socket, the system determines ! 597: port numbers in the following way: ! 598: when a server starts up, ! 599: it advertises to a port mapper demon on its local machine, ! 600: which picks a port number for the RPC procedure ! 601: if the socket specified to ! 602: .L svcudp_create() ! 603: isn't already bound. ! 604: When the ! 605: .L clntudp_create() ! 606: call is made with an unbound socket, ! 607: the system queries the port mapper on ! 608: the machine to which the call is being made, ! 609: and gets the appropriate port number. ! 610: If the port mapper is not running ! 611: or has no port corresponding to the RPC call, ! 612: the RPC call fails. ! 613: Users can make RPC calls ! 614: to the port mapper themselves. ! 615: The appropriate procedure ! 616: numbers are in the include file ! 617: .L <rpc/pmap_prot.h> . ! 618: .LP ! 619: After creating an SVCXPRT, the next step is to call ! 620: .L pmap_unset() ! 621: so that if the ! 622: .L nusers ! 623: server crashed earlier, ! 624: any previous trace of it is erased before restarting. ! 625: More precisely, ! 626: .L pmap_unset() ! 627: erases the entry for RUSERS from the port mapper's tables. ! 628: .LP ! 629: Finally, we associate the program number for ! 630: .L nusers ! 631: with the procedure ! 632: .L nuser() . ! 633: The final argument to ! 634: .L svc_register() ! 635: is normally the protocol being used, ! 636: which, in this case, is IPPROTO_UDP. ! 637: Notice that unlike ! 638: .L registerrpc() , ! 639: there are no XDR routines involved ! 640: in the registration process. ! 641: Also, registration is done on the program, ! 642: rather than procedure, level. ! 643: .LP ! 644: The user routine ! 645: .L nuser() ! 646: must call and dispatch the appropriate XDR routines ! 647: based on the procedure number. ! 648: Note that ! 649: two things are handled by ! 650: .L nuser() ! 651: that ! 652: .L registerrpc() ! 653: handles automatically. ! 654: The first is that procedure NULLPROC ! 655: (currently zero) returns with no arguments. ! 656: This can be used as a simple test ! 657: for detecting if a remote program is running. ! 658: Second, there is a check for invalid procedure numbers. ! 659: If one is detected, ! 660: .L svcerr_noproc() ! 661: is called to handle the error. ! 662: .LP ! 663: The user service routine serializes the results and returns ! 664: them to the RPC caller via ! 665: .L svc_sendreply() . ! 666: Its first parameter is the SVCXPRT handle, ! 667: the second is the XDR routine, ! 668: and the third is a pointer to the data to be returned. ! 669: Not illustrated above is how a server ! 670: handles an RPC program that passes data. ! 671: As an example, we can add a procedure RUSERSPROC_BOOL, ! 672: which has an argument ! 673: .L nusers , ! 674: and returns TRUE or FALSE depending on ! 675: whether there are nusers logged on. ! 676: It would look like this: ! 677: .LS ! 678: case RUSERSPROC_BOOL: { ! 679: int bool; ! 680: unsigned nuserquery; ! 681: .sp.5 ! 682: if (!svc_getargs(transp, xdr_u_int, &nuserquery) { ! 683: svcerr_decode(transp); ! 684: return; ! 685: } ! 686: /* ! 687: * code to set nusers = number of users ! 688: */ ! 689: if (nuserquery == nusers) ! 690: bool = TRUE; ! 691: else ! 692: bool = FALSE; ! 693: if (!svc_sendreply(transp, xdr_bool, &bool){ ! 694: fprintf(stderr, "couldn't reply to RPC call\en"); ! 695: exit(1); ! 696: } ! 697: return; ! 698: } ! 699: .LE ! 700: .LP ! 701: The relevant routine is ! 702: .L svc_getargs() , ! 703: which takes an SVCXPRT handle, the XDR routine, ! 704: and a pointer to where the input is to be placed as arguments. ! 705: .H 2 "Memory Allocation with XDR" ! 706: .LP ! 707: XDR routines not only do input and output, ! 708: they also do memory allocation. ! 709: This is why the second parameter of ! 710: .L xdr_array() ! 711: is a pointer to an array, rather than the array itself. ! 712: If it is NULL, then ! 713: .L xdr_array() ! 714: allocates space for the array and returns a pointer to it, ! 715: putting the size of the array in the third argument. ! 716: As an example, consider the following XDR routine ! 717: .L xdr_chararr1() , ! 718: which deals with a fixed array of bytes with length SIZE: ! 719: .LS ! 720: xdr_chararr1(xdrsp, chararr) ! 721: XDR *xdrsp; ! 722: char chararr[]; ! 723: { ! 724: char *p; ! 725: int len; ! 726: .sp.5 ! 727: p = chararr; ! 728: len = SIZE; ! 729: return (xdr_bytes(xdrsp, &p, &len, SIZE)); ! 730: } ! 731: .LE ! 732: It might be called from a server like this, ! 733: .LS ! 734: char chararr[SIZE]; ! 735: .sp.5 ! 736: svc_getargs(transp, xdr_chararr1, chararr); ! 737: .LE ! 738: where ! 739: .L chararr ! 740: has already allocated space. ! 741: If you want XDR to do the allocation, ! 742: you would have to rewrite this routine in the following way: ! 743: .LS ! 744: xdr_chararr2(xdrsp, chararrp) ! 745: XDR *xdrsp; ! 746: char **chararrp; ! 747: { ! 748: int len; ! 749: .sp.5 ! 750: len = SIZE; ! 751: return (xdr_bytes(xdrsp, charrarrp, &len, SIZE)); ! 752: } ! 753: .LE ! 754: Then the RPC call might look like this: ! 755: .LS ! 756: char *arrptr; ! 757: .sp.5 ! 758: arrptr = NULL; ! 759: svc_getargs(transp, xdr_chararr2, &arrptr); ! 760: /* ! 761: * use the result here ! 762: */ ! 763: svc_freeargs(xdrsp, xdr_chararr2, &arrptr); ! 764: .LE ! 765: After using the character array, it can be freed with ! 766: .L svc_freeargs() . ! 767: In the routine ! 768: .L xdr_finalexample() ! 769: given earlier, if ! 770: .L finalp->string ! 771: was NULL in the call ! 772: .LS ! 773: svc_getargs(transp, xdr_finalexample, &finalp); ! 774: .LE ! 775: then ! 776: .LS ! 777: svc_freeargs(xdrsp, xdr_finalexample, &finalp); ! 778: .LE ! 779: frees the array allocated to hold ! 780: .L finalp->string ; ! 781: otherwise, it frees nothing. ! 782: The same is true for ! 783: .L finalp->simplep . ! 784: .LP ! 785: To summarize, each XDR routine is responsible ! 786: for serializing, deserializing, and allocating memory. ! 787: When an XDR routine is called from ! 788: .L callrpc() , ! 789: the serializing part is used. ! 790: When called from ! 791: .L svc_getargs() , ! 792: the deserializer is used. ! 793: And when called from ! 794: .L svc_freeargs() , ! 795: the memory deallocator is used. ! 796: When building simple examples like those in this section, ! 797: a user doesn't have to worry about the three modes. ! 798: The XDR reference manual has examples of more ! 799: sophisticated XDR routines that ! 800: determine which of the three modes they are in ! 801: to function correctly. ! 802: . ! 803: .H 2 "The Calling Side" ! 804: .LP ! 805: When you use ! 806: .L callrpc, ! 807: you have no control over the RPC delivery ! 808: mechanism or the socket used to transport the data. ! 809: To illustrate the layer of RPC that lets you adjust these ! 810: parameters, consider the following code to call the ! 811: .L nusers ! 812: service: ! 813: .LS ! 814: #include <stdio.h> ! 815: #include <rpc/rpc.h> ! 816: #include <rpcsvc/rusers.h> ! 817: #include <sys/socket.h> ! 818: #include <sys/time.h> ! 819: #include <netdb.h> ! 820: .sp.5 ! 821: main(argc, argv) ! 822: int argc; ! 823: char **argv; ! 824: { ! 825: struct hostent *hp; ! 826: struct timeval pertry_timeout, total_timeout; ! 827: struct sockaddr_in server_addr; ! 828: int addrlen, sock = RPC_ANYSOCK; ! 829: register CLIENT *client; ! 830: enum clnt_stat clnt_stat; ! 831: unsigned long nusers; ! 832: .sp.5 ! 833: if (argc < 2) { ! 834: fprintf(stderr, "usage: nusers hostname\en"); ! 835: exit(-1); ! 836: } ! 837: if ((hp = gethostbyname(argv[1])) == NULL) { ! 838: fprintf(stderr, "cannot get addr for '%s'\en", argv[1]); ! 839: exit(-1); ! 840: } ! 841: pertry_timeout.tv_sec = 3; ! 842: pertry_timeout.tv_usec = 0; ! 843: addrlen = sizeof(struct sockaddr_in); ! 844: bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, hp->h_length); ! 845: server_addr.sin_family = AF_INET; ! 846: server_addr.sin_port = 0; ! 847: if ((client = clntudp_create(&server_addr, RUSERSPROG, ! 848: RUSERSVERS, pertry_timeout, &sock)) == NULL) { ! 849: perror("clntudp_create"); ! 850: exit(-1); ! 851: } ! 852: total_timeout.tv_sec = 20; ! 853: total_timeout.tv_usec = 0; ! 854: clnt_stat = clnt_call(client, RUSERSPROC_NUM, xdr_void, 0, ! 855: xdr_u_long, &nusers, total_timeout); ! 856: if (clnt_stat != RPC_SUCCESS) { ! 857: clnt_perror(client, "rpc"); ! 858: exit(-1); ! 859: } ! 860: clnt_destroy(client); ! 861: } ! 862: .LE ! 863: The low-level version of ! 864: .L callrpc() ! 865: is ! 866: .L clnt_call() , ! 867: which takes a CLIENT pointer rather than a host name. ! 868: The parameters to ! 869: .L clnt_call() ! 870: are a CLIENT pointer, the procedure number, ! 871: the XDR routine for serializing the argument, ! 872: a pointer to the argument, ! 873: the XDR routine for deserializing the return value, ! 874: a pointer to where the return value will be placed, ! 875: and the time in seconds to wait for a reply. ! 876: .LP ! 877: The CLIENT pointer is encoded with ! 878: the transport mechanism. ! 879: .L callrpc() ! 880: uses UDP, thus it calls ! 881: .L clntudp_create() ! 882: to get a CLIENT pointer. ! 883: To get TCP (Transport Control Protocol), you would use ! 884: .L clnttcp_create() . ! 885: .LP ! 886: The parameters to ! 887: .L clntudp_create() ! 888: are the server address, the length of the server address, ! 889: the program number, the version number, ! 890: a timeout value (between tries), and a pointer to a socket. ! 891: The final argument to ! 892: .L clnt_call() ! 893: is the total time to wait for a response. ! 894: Thus, the number of tries is the ! 895: .L clnt_call() ! 896: timeout divided by the ! 897: .L clntudp_create() ! 898: timeout. ! 899: .LP ! 900: There is one thing to note when using the ! 901: .L clnt_destroy() ! 902: call. ! 903: It deallocates any space associated with the CLIENT handle, ! 904: but it does not close the socket associated with it, ! 905: which was passed as an argument to ! 906: .L clntudp_create() . ! 907: The reason is that if ! 908: there are multiple client handles using the same socket, ! 909: then it is possible to close one handle ! 910: without destroying the socket that other handles ! 911: are using. ! 912: .LP ! 913: To make a stream connection, the call to ! 914: .L clntudp_create() ! 915: is replaced with a call to ! 916: .L clnttcp_create(). ! 917: .LS ! 918: clnttcp_create(&server_addr, prognum, versnum, &socket, inputsize, ! 919: outputsize); ! 920: .LE ! 921: There is no timeout argument; instead, the receive and send buffer ! 922: sizes must be specified. When the ! 923: .L clnttcp_create() ! 924: call is made, a TCP connection is established. ! 925: All RPC calls using that CLIENT handle would use this connection. ! 926: The server side of an RPC call using TCP has ! 927: .L svcudp_create() ! 928: replaced by ! 929: .L svctcp_create() . ! 930: .bp ! 931: . ! 932: .H 1 "Other RPC Features" ! 933: .LP ! 934: This section discusses some other aspects of RPC ! 935: that are occasionally useful. ! 936: . ! 937: .H 2 "Select on the Server Side" ! 938: .LP ! 939: Suppose a process is processing RPC requests ! 940: while performing some other activity. ! 941: If the other activity involves periodically updating a data structure, ! 942: the process can set an alarm signal before calling ! 943: .L svc_run() . ! 944: But if the other activity ! 945: involves waiting on a a file descriptor, the ! 946: .L svc_run() ! 947: call won't work. ! 948: The code for ! 949: .L svc_run() ! 950: is as follows: ! 951: .LS ! 952: void ! 953: svc_run() ! 954: { ! 955: int readfds; ! 956: .sp.5 ! 957: for (;;) { ! 958: readfds = svc_fds; ! 959: switch (select(32, &readfds, NULL, NULL, NULL)) { ! 960: .sp.5 ! 961: case -1: ! 962: if (errno == EINTR) ! 963: continue; ! 964: perror("rstat: select"); ! 965: return; ! 966: case 0: ! 967: break; ! 968: default: ! 969: svc_getreq(readfds); ! 970: } ! 971: } ! 972: } ! 973: .LE ! 974: .LP ! 975: You can bypass ! 976: .L svc_run() ! 977: and call ! 978: .L svc_getreq() ! 979: yourself. ! 980: All you need to know are the file descriptors ! 981: of the socket(s) associated with the programs you are waiting on. ! 982: Thus you can have your own ! 983: .L select() ! 984: that waits on both the RPC socket, ! 985: and your own descriptors. ! 986: . ! 987: .H 2 "Broadcast RPC" ! 988: .LP ! 989: The ! 990: .L pmap ! 991: and RPC protocols implement broadcast RPC. ! 992: Here are the main differences between broadcast RPC ! 993: and normal RPC calls: ! 994: .IP 1) ! 995: Normal RPC expects one answer, whereas ! 996: broadcast RPC expects many answers ! 997: (one or more answer from each responding machine). ! 998: .IP 2) ! 999: Broadcast RPC can only be supported by packet-oriented (connectionless) ! 1000: transport protocols like UPD/IP. ! 1001: .IP 3) ! 1002: The implementation of broadcast RPC ! 1003: treats all unsuccessful responses as garbage by filtering them out. ! 1004: Thus, if there is a version mismatch between the ! 1005: broadcaster and a remote service, ! 1006: the user of broadcast RPC never knows. ! 1007: .IP 4) ! 1008: All broadcast messages are sent to the portmap port. ! 1009: Thus, only services that register themselves with their portmapper ! 1010: are accessible via the broadcast RPC mechanism. ! 1011: . ! 1012: .H 3 "Broadcast RPC Synopsis" ! 1013: .LP ! 1014: .LS ! 1015: #include <rpc/pmap_clnt.h> ! 1016: .sp.5 ! 1017: enum clnt_stat clnt_stat; ! 1018: .sp.5 ! 1019: clnt_stat = ! 1020: clnt_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp, eachresult) ! 1021: u_long prog; /* program number */ ! 1022: u_long vers; /* version number */ ! 1023: u_long proc; /* procedure number */ ! 1024: xdrproc_t xargs; /* xdr routine for args */ ! 1025: caddr_t argsp; /* pointer to args */ ! 1026: xdrproc_t xresults; /* xdr routine for results */ ! 1027: caddr_t resultsp; /* pointer to results */ ! 1028: bool_t (*eachresult)(); /* call with each result obtained */ ! 1029: .LE ! 1030: The procedure ! 1031: .L eachresult() ! 1032: is called each time a valid result is obtained. ! 1033: It returns a boolean that indicates ! 1034: whether or not the client wants more responses. ! 1035: .LS ! 1036: bool_t done; ! 1037: .sp.5 ! 1038: done = ! 1039: eachresult(resultsp, raddr) ! 1040: caddr_t resultsp; ! 1041: struct sockaddr_in *raddr; /* address of machine that sent response */ ! 1042: .LE ! 1043: If ! 1044: .L done ! 1045: is TRUE, then broadcasting stops and ! 1046: .L clnt_broadcast() ! 1047: returns successfully. ! 1048: Otherwise, the routine waits for another response. ! 1049: The request is rebroadcast ! 1050: after a few seconds of waiting. ! 1051: If no responses come back, ! 1052: the routine returns with RPC_TIMEDOUT. ! 1053: To interpret ! 1054: .L clnt_stat ! 1055: errors, feed the error code to ! 1056: .L clnt_perrno() . ! 1057: . ! 1058: .H 2 "Batching" ! 1059: .LP ! 1060: The RPC architecture is designed so that clients send a call message, ! 1061: and wait for servers to reply that the call succeeded. ! 1062: This implies that clients do not compute ! 1063: while servers are processing a call. ! 1064: This is inefficient if the client does not want or need ! 1065: an acknowledgement for every message sent. ! 1066: It is possible for clients to continue computing ! 1067: while waiting for a response, ! 1068: using RPC batch facilities. ! 1069: .LP ! 1070: RPC messages can be placed in a ``pipeline'' of calls ! 1071: to a desired server; this is called batching. ! 1072: Batching assumes that: ! 1073: 1) each RPC call in the pipeline requires no response from the server, ! 1074: and the server does not send a response message; and ! 1075: 2) the pipeline of calls is transported on a reliable ! 1076: byte stream transport such as TCP/IP. ! 1077: Since the server does not respond to every call, ! 1078: the client can generate new calls in parallel ! 1079: with the server executing previous calls. ! 1080: Furthermore, the TCP/IP implementation can buffer up ! 1081: many call messages, and send them to the server in one ! 1082: .L write ! 1083: system call. This overlapped execution ! 1084: greatly decreases the interprocess communication overhead of ! 1085: the client and server processes, ! 1086: and the total elapsed time of a series of calls. ! 1087: .LP ! 1088: Since the batched calls are buffered, ! 1089: the client should eventually do a legitimate call ! 1090: in order to flush the pipeline. ! 1091: .LP ! 1092: A contrived example of batching follows. ! 1093: Assume a string rendering service (like a window system) ! 1094: has two similar calls: one renders a string and returns void results, ! 1095: while the other renders a string and remains silent. ! 1096: The service (using the TCP/IP transport) may look like: ! 1097: .LS ! 1098: #include <stdio.h> ! 1099: #include <rpc/rpc.h> ! 1100: #include <rpcsvc/windows.h> ! 1101: .sp.5 ! 1102: void windowdispatch(); ! 1103: .sp.5 ! 1104: main() ! 1105: { ! 1106: SVCXPRT *transp; ! 1107: .sp.5 ! 1108: transp = svctcp_create(RPC_ANYSOCK, 0, 0); ! 1109: if (transp == NULL){ ! 1110: fprintf(stderr, "couldn't create an RPC server\en"); ! 1111: exit(1); ! 1112: } ! 1113: pmap_unset(WINDOWPROG, WINDOWVERS); ! 1114: if (!svc_register(transp, WINDOWPROG, WINDOWVERS, windowdispatch, ! 1115: IPPROTO_TCP)) { ! 1116: fprintf(stderr, "couldn't register WINDOW service\en"); ! 1117: exit(1); ! 1118: } ! 1119: svc_run(); /* never returns */ ! 1120: fprintf(stderr, "should never reach this point\en"); ! 1121: } ! 1122: .LE ! 1123: .LS no ! 1124: void ! 1125: windowdispatch(rqstp, transp) ! 1126: struct svc_req *rqstp; ! 1127: SVCXPRT *transp; ! 1128: { ! 1129: char *s = NULL; ! 1130: .sp.5 ! 1131: switch (rqstp->rq_proc) { ! 1132: case NULLPROC: ! 1133: if (!svc_sendreply(transp, xdr_void, 0)) { ! 1134: fprintf(stderr, "couldn't reply to RPC call\en"); ! 1135: exit(1); ! 1136: } ! 1137: return; ! 1138: case RENDERSTRING: ! 1139: if (!svc_getargs(transp, xdr_wrapstring, &s)) { ! 1140: fprintf(stderr, "couldn't decode arguments\en"); ! 1141: svcerr_decode(transp); /* tell caller he screwed up */ ! 1142: break; ! 1143: } ! 1144: /* ! 1145: * call here to to render the string s ! 1146: */ ! 1147: if (!svc_sendreply(transp, xdr_void, NULL)) { ! 1148: fprintf(stderr, "couldn't reply to RPC call\en"); ! 1149: exit(1); ! 1150: } ! 1151: break; ! 1152: case RENDERSTRING_BATCHED: ! 1153: if (!svc_getargs(transp, xdr_wrapstring, &s)) { ! 1154: fprintf(stderr, "couldn't decode arguments\en"); ! 1155: /* ! 1156: * we are silent in the face of protocol errors ! 1157: */ ! 1158: break; ! 1159: } ! 1160: /* ! 1161: * call here to to render the string s, ! 1162: * but sends no reply! ! 1163: */ ! 1164: break; ! 1165: default: ! 1166: svcerr_noproc(transp); ! 1167: return; ! 1168: } ! 1169: /* ! 1170: * now free string allocated while decoding arguments ! 1171: */ ! 1172: svc_freeargs(transp, xdr_wrapstring, &s); ! 1173: } ! 1174: .LE ! 1175: Of course the service could have one procedure ! 1176: that takes the string and a boolean ! 1177: to indicate whether or not the procedure should respond. ! 1178: .LP ! 1179: In order for a client to take advantage of batching, ! 1180: the client must perform RPC calls on a TCP-based transport ! 1181: and the actual calls must have the following attributes: ! 1182: 1) the result's XDR routine must be zero (NULL), and ! 1183: 2) the RPC call's timeout must be zero. ! 1184: .LP ! 1185: Here is an example of a client that uses batching ! 1186: to render a bunch of strings; ! 1187: the batching is flushed when the client gets a null string: ! 1188: .LS ! 1189: #include <stdio.h> ! 1190: #include <rpc/rpc.h> ! 1191: #include <rpcsvc/windows.h> ! 1192: #include <sys/socket.h> ! 1193: #include <sys/time.h> ! 1194: #include <netdb.h> ! 1195: .sp.5 ! 1196: main(argc, argv) ! 1197: int argc; ! 1198: char **argv; ! 1199: { ! 1200: struct hostent *hp; ! 1201: struct timeval pertry_timeout, total_timeout; ! 1202: struct sockaddr_in server_addr; ! 1203: int addrlen, sock = RPC_ANYSOCK; ! 1204: register CLIENT *client; ! 1205: enum clnt_stat clnt_stat; ! 1206: char buf[1000]; ! 1207: char *s = buf; ! 1208: .sp.5 ! 1209: /* ! 1210: * initial as in example 3.3 ! 1211: */ ! 1212: if ((client = clnttcp_create(&server_addr, WINDOWPROG, ! 1213: WINDOWVERS, &sock, 0, 0)) == NULL) { ! 1214: perror("clnttcp_create"); ! 1215: exit(-1); ! 1216: } ! 1217: total_timeout.tv_sec = 0; ! 1218: total_timeout.tv_usec = 0; ! 1219: while (scanf("%s", s) != EOF) { ! 1220: clnt_stat = clnt_call(client, RENDERSTRING_BATCHED, ! 1221: xdr_wrapstring, &s, NULL, NULL, total_timeout); ! 1222: if (clnt_stat != RPC_SUCCESS) { ! 1223: clnt_perror(client, "batched rpc"); ! 1224: exit(-1); ! 1225: } ! 1226: } ! 1227: /* ! 1228: * now flush the pipeline ! 1229: */ ! 1230: total_timeout.tv_sec = 20; ! 1231: clnt_stat = clnt_call(client, NULLPROC, ! 1232: xdr_void, NULL, xdr_void, NULL, total_timeout); ! 1233: if (clnt_stat != RPC_SUCCESS) { ! 1234: clnt_perror(client, "rpc"); ! 1235: exit(-1); ! 1236: } ! 1237: ! 1238: clnt_destroy(client); ! 1239: } ! 1240: .LE ! 1241: Since the server sends no message, ! 1242: the clients cannot be notified of any of the failures that may occur. ! 1243: Therefore, clients are on their own when it comes to handling errors. ! 1244: .LP ! 1245: The above example was completed to render ! 1246: all of the (2000) lines in the file ! 1247: .I /etc/termcap . ! 1248: The rendering service did nothing but to throw the lines away. ! 1249: The example was run in the following four configurations: ! 1250: 1) machine to itself, regular RPC; ! 1251: 2) machine to itself, batched RPC; ! 1252: 3) machine to another, regular RPC; and ! 1253: 4) machine to another, batched RPC. ! 1254: The results are as follows: ! 1255: 1) 50 seconds; ! 1256: 2) 16 seconds; ! 1257: 3) 52 seconds; ! 1258: 4) 10 seconds. ! 1259: Running ! 1260: .L fscanf() ! 1261: on ! 1262: .I /etc/termcap ! 1263: only requires six seconds. ! 1264: These timings show the advantage of protocols ! 1265: that allow for overlapped execution, ! 1266: though these protocols are often hard to design. ! 1267: . ! 1268: .H 2 "Authentication" ! 1269: .LP ! 1270: In the examples presented so far, ! 1271: the caller never identified itself to the server, ! 1272: and the server never required an ID from the caller. ! 1273: Clearly, some network services, such as a network filesystem, ! 1274: require stronger security than what has been presented so far. ! 1275: .LP ! 1276: In reality, every RPC call is authenticated by ! 1277: the RPC package on the server, and similarly, ! 1278: the RPC client package generates and sends authentication parameters. ! 1279: Just as different transports (TCP/IP or UDP/IP) ! 1280: can be used when creating RPC clients and servers, ! 1281: different forms of authentication can be associated with RPC clients; ! 1282: the default authentication type used as a default is type ! 1283: .I none . ! 1284: .LP ! 1285: The authentication subsystem of the RPC package is open ended. ! 1286: That is, numerous types of authentication are easy to support. ! 1287: However, this section deals only with ! 1288: .I unix ! 1289: type authentication, which besides ! 1290: .I none ! 1291: is the only supported type. ! 1292: . ! 1293: .H 3 "The Client Side" ! 1294: .LP ! 1295: When a caller creates a new RPC client handle as in: ! 1296: .LS ! 1297: clnt = clntudp_create(address, prognum, versnum, wait, sockp) ! 1298: .LE ! 1299: the appropriate transport instance defaults ! 1300: the associate authentication handle to be ! 1301: .LS ! 1302: clnt->cl_auth = authnone_create(); ! 1303: .LE ! 1304: The RPC client can choose to use ! 1305: .I unix ! 1306: style authentication by setting ! 1307: .L clnt->cl_auth ! 1308: after creating the RPC client handle: ! 1309: .LS ! 1310: clnt->cl_auth = authunix_create_default(); ! 1311: .LE ! 1312: This causes each RPC call associated with ! 1313: .L clnt ! 1314: to carry with it the following authentication credentials structure: ! 1315: .LS ! 1316: /* ! 1317: * Unix style credentials. ! 1318: */ ! 1319: struct authunix_parms { ! 1320: u_long aup_time; /* credentials creation time */ ! 1321: char *aup_machname; /* host name of where the client is calling */ ! 1322: int aup_uid; /* client's UNIX effective uid */ ! 1323: int aup_gid; /* client's current UNIX group id */ ! 1324: u_int aup_len; /* the element length of aup_gids array */ ! 1325: int *aup_gids; /* array of 4.2 groups to which user belongs */ ! 1326: }; ! 1327: .LE ! 1328: These fields are set by ! 1329: .L authunix_create_default() ! 1330: by invoking the appropriate system calls. ! 1331: .LP ! 1332: Since the RPC user created this new style of authentication, ! 1333: he is responsible for destroying it with: ! 1334: .LS ! 1335: auth_destroy(clnt->cl_auth); ! 1336: .LE ! 1337: . ! 1338: .H 3 "The Server Side" ! 1339: .LP ! 1340: Service implementors have a harder time dealing with authentication issues ! 1341: since the RPC package passes the service dispatch routine a request ! 1342: that has an arbitrary authentication style associated with it. ! 1343: Consider the fields of a request handle passed to a service dispatch routine: ! 1344: .LS ! 1345: /* ! 1346: * An RPC Service request ! 1347: */ ! 1348: struct svc_req { ! 1349: u_long rq_prog; /* service program number */ ! 1350: u_long rq_vers; /* service protocol version number*/ ! 1351: u_long rq_proc; /* the desired procedure number*/ ! 1352: struct opaque_auth rq_cred; /* raw credentials from the ``wire'' */ ! 1353: caddr_t rq_clntcred; /* read only, cooked credentials */ ! 1354: }; ! 1355: .LE ! 1356: The ! 1357: .L rq_cred ! 1358: is mostly opaque, except for one field of interest: ! 1359: the style of authentication credentials: ! 1360: .LS ! 1361: /* ! 1362: * Authentication info. Mostly opaque to the programmer. ! 1363: */ ! 1364: struct opaque_auth { ! 1365: enum_t oa_flavor; /* style of credentials */ ! 1366: caddr_t oa_base; /* address of more auth stuff */ ! 1367: u_int oa_length; /* not to exceed MAX_AUTH_BYTES */ ! 1368: }; ! 1369: .LE ! 1370: The RPC package guarantees the following ! 1371: to the service dispatch routine: ! 1372: .IP 1) ! 1373: That the request's ! 1374: .L rq_cred ! 1375: is well formed. Thus the service implementor may inspect the request's ! 1376: .L rq_cred.oa_flavor ! 1377: to determine which style of authentication the caller used. ! 1378: The service implementor may also wish to inspect the other fields of ! 1379: .L rq_cred ! 1380: if the style is not one of the styles supported by the RPC package. ! 1381: .IP 2) ! 1382: That the request's ! 1383: .L rq_clntcred ! 1384: field is either NULL or points to a well formed structure ! 1385: that corresponds to a supported style of authentication credentials. ! 1386: Remember that only ! 1387: .I unix ! 1388: style is currently supported, so (currently) ! 1389: .L rq_clntcred ! 1390: could be cast to a pointer to an ! 1391: .L authunix_parms ! 1392: structure. If ! 1393: .L rq_clntcred ! 1394: is NULL, the service implementor may wish to inspect ! 1395: the other (opaque) fileds of ! 1396: .L rq_cred ! 1397: in case the service knows about a new type of authentication ! 1398: that the RPC package does not know about. ! 1399: .LP ! 1400: Our remote users service example can be extended so that ! 1401: it computes results for all users except UID 16: ! 1402: .LS ! 1403: nuser(rqstp, tranp) ! 1404: struct svc_req *rqstp; ! 1405: SVCXPRT *transp; ! 1406: { ! 1407: struct authunix_parms *unix_cred; ! 1408: int uid; ! 1409: unsigned long nusers; ! 1410: .sp.5 ! 1411: /* ! 1412: * we don't care about authentication for the null procedure ! 1413: */ ! 1414: if (rqstp->rq_proc == NULLPROC) { ! 1415: if (!svc_sendreply(transp, xdr_void, 0)) { ! 1416: fprintf(stderr, "couldn't reply to RPC call\en"); ! 1417: exit(1); ! 1418: } ! 1419: return; ! 1420: } ! 1421: /* ! 1422: * now get the uid ! 1423: */ ! 1424: switch (rqstp->rq_cred.oa_flavor) { ! 1425: case AUTH_UNIX: ! 1426: unix_cred = (struct authunix_parms *) rqstp->rq_clntcred; ! 1427: uid = unix_cred->aup_uid; ! 1428: break; ! 1429: case AUTH_NULL: ! 1430: default: ! 1431: svcerr_weakauth(transp); ! 1432: return; ! 1433: } ! 1434: switch (rqstp->rq_proc) { ! 1435: case RUSERSPROC_NUM: ! 1436: /* ! 1437: * make sure the caller is allow to call this procedure. ! 1438: */ ! 1439: if (uid == 16) { ! 1440: svcerr_systemerr(transp); ! 1441: return; ! 1442: } ! 1443: /* ! 1444: * code here to compute the number of users ! 1445: * and put in variable nusers ! 1446: */ ! 1447: if (!svc_sendreply(transp, xdr_u_long, &nusers) { ! 1448: fprintf(stderr, "couldn't reply to RPC call\en"); ! 1449: exit(1); ! 1450: } ! 1451: return; ! 1452: default: ! 1453: svcerr_noproc(transp); ! 1454: return; ! 1455: } ! 1456: } ! 1457: .LE ! 1458: A few things should be noted here. ! 1459: First, it is customary not to check the authentication parameters ! 1460: associated with the NULLPROC (procedure number zero). ! 1461: Second, if the authentication parameter's type is not suitable ! 1462: for your service, you should call ! 1463: .L svcerr_weakauth() . ! 1464: And finally, the service protocol itself should return status ! 1465: for access denied; in the case of our example, the protocol ! 1466: does not have such a status, so we call the service primitive ! 1467: .L svcerr_systemerr() ! 1468: instead. ! 1469: .LP ! 1470: The last point underscores the relation between ! 1471: the RPC authentication package and the services; ! 1472: RPC deals only with authentication and not with ! 1473: individual services' access control. ! 1474: The services themselves must implement their own access control policies ! 1475: and reflect these policies as return statuses in their protocols. ! 1476: . ! 1477: .H 2 "Using Inetd" ! 1478: .LP ! 1479: An RPC server can be started from ! 1480: .L inetd . ! 1481: The only difference ! 1482: from the usual code is that ! 1483: .L svcudp_create() ! 1484: should be called as ! 1485: .LS ! 1486: transp = svcudp_create(0); ! 1487: .LE ! 1488: since ! 1489: .L inet ! 1490: passes a socket as file descriptor 0. ! 1491: Also, ! 1492: .L svc_register() ! 1493: should be called as ! 1494: .LS ! 1495: svc_register(PROGNUM, VERSNUM, service, transp, 0); ! 1496: .LE ! 1497: with the final flag as 0, ! 1498: since the program would already be registered by ! 1499: .L inetd . ! 1500: Remember that if you want to exit ! 1501: from the server process and return control to ! 1502: .L inet , ! 1503: you need to explicitly exit, since ! 1504: .L svc_run() ! 1505: never returns. ! 1506: .LP ! 1507: The format of entries in /etc/servers for RPC services is ! 1508: .LS ! 1509: rpc udp \fIserver \0program \0version\fP ! 1510: .LE ! 1511: where ! 1512: .I server ! 1513: is the C code implementing the server, ! 1514: and ! 1515: .I program ! 1516: and ! 1517: .I version ! 1518: are the program and version numbers of the service. ! 1519: The key word ! 1520: .L udp ! 1521: can be replaced by ! 1522: .L tcp ! 1523: for TCP-based RPC services. ! 1524: .LP ! 1525: If the same program handles multiple versions, ! 1526: then the version number can be a range, ! 1527: as in this example: ! 1528: .LS ! 1529: rpc udp /usr/etc/rstatd 100001 1-2 ! 1530: .LE ! 1531: .bp ! 1532: . ! 1533: .H 1 "More Examples" ! 1534: .H 2 "Versions" ! 1535: .LP ! 1536: By convention, the first version number of program FOO is ! 1537: FOOVERS_ORIG and the most recent version is FOOVERS. ! 1538: Suppose there is a new version of the ! 1539: .L user ! 1540: program that returns an ! 1541: .L "unsigned short" ! 1542: rather than a ! 1543: .L long . ! 1544: If we name this version ! 1545: RUSERSVERS_SHORT, then ! 1546: a server that wants to support both versions ! 1547: would do a double register. ! 1548: .LS ! 1549: .sp.5 ! 1550: if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, nuser, ! 1551: IPPROTO_TCP)) { ! 1552: fprintf(stderr, "couldn't register RUSER service\en"); ! 1553: exit(1); ! 1554: } ! 1555: if (!svc_register(transp, RUSERSPROG, RUSERSVERS_SHORT, nuser, ! 1556: IPPROTO_TCP)) { ! 1557: fprintf(stderr, "couldn't register RUSER service\en"); ! 1558: exit(1); ! 1559: } ! 1560: .LE ! 1561: .bp ! 1562: Both versions can be handled by the same C procedure: ! 1563: .LS 0 ! 1564: nuser(rqstp, tranp) ! 1565: struct svc_req *rqstp; ! 1566: SVCXPRT *transp; ! 1567: { ! 1568: unsigned long nusers; ! 1569: unsigned short nusers2 ! 1570: .sp.5 ! 1571: switch (rqstp->rq_proc) { ! 1572: case NULLPROC: ! 1573: if (!svc_sendreply(transp, xdr_void, 0)) { ! 1574: fprintf(stderr, "couldn't reply to RPC call\en"); ! 1575: exit(1); ! 1576: } ! 1577: return; ! 1578: case RUSERSPROC_NUM: ! 1579: /* ! 1580: * code here to compute the number of users ! 1581: * and put in variable nusers ! 1582: */ ! 1583: nusers2 = nusers; ! 1584: if (rqstp->rq_vers == RUSERSVERS_ORIG) ! 1585: if (!svc_sendreply(transp, xdr_u_long, &nusers) { ! 1586: fprintf(stderr, "couldn't reply to RPC call\en"); ! 1587: exit(1); ! 1588: } ! 1589: else ! 1590: if (!svc_sendreply(transp, xdr_u_short, &nusers2) { ! 1591: fprintf(stderr, "couldn't reply to RPC call\en"); ! 1592: exit(1); ! 1593: return; ! 1594: default: ! 1595: svcerr_noproc(transp); ! 1596: return; ! 1597: } ! 1598: } ! 1599: .LE ! 1600: .H 2 "TCP" ! 1601: .LP ! 1602: Here is an example that is essentially ! 1603: .L rcp . ! 1604: The initiator of the RPC ! 1605: .L snd() ! 1606: call takes its standard input and sends it to the server ! 1607: .L rcv() , ! 1608: which prints it on standard output. ! 1609: The RPC call uses TCP. ! 1610: This also illustrates an XDR procedure that behaves differently ! 1611: on serialization than on deserialization. ! 1612: .LS 0 ! 1613: /* ! 1614: * The xdr routine: ! 1615: * ! 1616: * on decode, read from wire, write onto fp ! 1617: * on encode, read from fp, write onto wire ! 1618: */ ! 1619: #include <stdio.h> ! 1620: #include <rpc/rpc.h> ! 1621: .sp.5 ! 1622: xdr_rcp(xdrs, fp) ! 1623: XDR *xdrs; ! 1624: FILE *fp; ! 1625: { ! 1626: unsigned long size; ! 1627: char buf[MAXCHUNK], *p; ! 1628: .sp.5 ! 1629: if (xdrs->x_op == XDR_FREE)/* nothing to free */ ! 1630: return 1; ! 1631: while (1) { ! 1632: if (xdrs->x_op == XDR_ENCODE) { ! 1633: if ((size = fread (buf, sizeof(char), MAXCHUNK, fp)) ! 1634: == 0 && ferror(fp)) { ! 1635: fprintf(stderr, "couldn't fread\en"); ! 1636: exit(1); ! 1637: } ! 1638: } ! 1639: p = buf; ! 1640: if (!xdr_bytes(xdrs, &p, &size, MAXCHUNK)) ! 1641: return 0; ! 1642: if (size == 0) ! 1643: return 1; ! 1644: if (xdrs->x_op == XDR_DECODE) { ! 1645: if (fwrite(buf, sizeof(char), size, fp) != size) { ! 1646: fprintf(stderr, "couldn't fwrite\en"); ! 1647: exit(1); ! 1648: } ! 1649: } ! 1650: } ! 1651: } ! 1652: .LE ! 1653: .LS 0 ! 1654: /* ! 1655: * The sender routines ! 1656: */ ! 1657: #include <stdio.h> ! 1658: #include <netdb.h> ! 1659: #include <rpc/rpc.h> ! 1660: #include <sys/socket.h> ! 1661: #include <sys/time.h> ! 1662: .sp.5 ! 1663: main(argc, argv) ! 1664: int argc; ! 1665: char **argv; ! 1666: { ! 1667: int err; ! 1668: .sp.5 ! 1669: if (argc < 2) { ! 1670: fprintf(stderr, "usage: %s server-name\en", argv[0]); ! 1671: exit(-1); ! 1672: } ! 1673: if ((err = callrpctcp(argv[1], RCPPROG, RCPPROC_FP, RCPVERS, ! 1674: xdr_rcp, stdin, xdr_void, 0) != 0)) { ! 1675: clnt_perrno(err); ! 1676: fprintf(stderr, " couldn't make RPC call\en"); ! 1677: exit(1); ! 1678: } ! 1679: } ! 1680: .sp.5 ! 1681: callrpctcp(host, prognum, procnum, versnum, inproc, in, outproc, out) ! 1682: char *host, *in, *out; ! 1683: xdrproc_t inproc, outproc; ! 1684: { ! 1685: struct sockaddr_in server_addr; ! 1686: int socket = RPC_ANYSOCK; ! 1687: enum clnt_stat clnt_stat; ! 1688: struct hostent *hp; ! 1689: register CLIENT *client; ! 1690: struct timeval total_timeout; ! 1691: .sp.5 ! 1692: if ((hp = gethostbyname(host)) == NULL) { ! 1693: fprintf(stderr, "cannot get addr for '%s'\en", host); ! 1694: exit(-1); ! 1695: } ! 1696: bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, hp->h_length); ! 1697: server_addr.sin_family = AF_INET; ! 1698: server_addr.sin_port = 0; ! 1699: if ((client = clnttcp_create(&server_addr, prognum, ! 1700: versnum, &socket, BUFSIZ, BUFSIZ)) == NULL) { ! 1701: perror("rpctcp_create"); ! 1702: exit(-1); ! 1703: } ! 1704: total_timeout.tv_sec = 20; ! 1705: total_timeout.tv_usec = 0; ! 1706: clnt_stat = clnt_call(client, procnum, inproc, in, outproc, out, total_timeout); ! 1707: clnt_destroy(client) ! 1708: return (int)clnt_stat; ! 1709: } ! 1710: .LE ! 1711: .LS 0 ! 1712: /* ! 1713: * The receiving routines ! 1714: */ ! 1715: #include <stdio.h> ! 1716: #include <rpc/rpc.h> ! 1717: .sp.5 ! 1718: main() ! 1719: { ! 1720: register SVCXPRT *transp; ! 1721: .sp.5 ! 1722: if ((transp = svctcp_create(RPC_ANYSOCK, 1024, 1024)) == NULL) { ! 1723: fprintf("svctcp_create: error\en"); ! 1724: exit(1); ! 1725: } ! 1726: pmap_unset(RCPPROG, RCPVERS); ! 1727: if (!svc_register(transp, RCPPROG, RCPVERS, rcp_service, IPPROTO_TCP)) { ! 1728: fprintf(stderr, "svc_register: error\en"); ! 1729: exit(1); ! 1730: } ! 1731: svc_run(); /* never returns */ ! 1732: fprintf(stderr, "svc_run should never return\en"); ! 1733: } ! 1734: .sp.5 ! 1735: rcp_service(rqstp, transp) ! 1736: register struct svc_req *rqstp; ! 1737: register SVCXPRT *transp; ! 1738: { ! 1739: switch (rqstp->rq_proc) { ! 1740: case NULLPROC: ! 1741: if (svc_sendreply(transp, xdr_void, 0) == 0) { ! 1742: fprintf(stderr, "err: rcp_service"); ! 1743: exit(1); ! 1744: } ! 1745: return; ! 1746: case RCPPROC_FP: ! 1747: if (!svc_getargs(transp, xdr_rcp, stdout)) { ! 1748: svcerr_decode(transp); ! 1749: return; ! 1750: } ! 1751: if (!svc_sendreply(transp, xdr_void, 0)) { ! 1752: fprintf(stderr, "can't reply\en"); ! 1753: return; ! 1754: } ! 1755: exit(0); ! 1756: default: ! 1757: svcerr_noproc(transp); ! 1758: return; ! 1759: } ! 1760: } ! 1761: .LE ! 1762: .H 2 "Callback Procedures" ! 1763: .LP ! 1764: Occasionally, it is useful to have a server become a client, ! 1765: and make an RPC call back the process which is its client. ! 1766: An example is remote debugging, ! 1767: where the client is a window system program, ! 1768: and the server is a debugger running on the remote machine. ! 1769: Most of the time, ! 1770: the user clicks a mouse button at the debugging window, ! 1771: which converts this to a debugger command, ! 1772: and then makes an RPC call to the server ! 1773: (where the debugger is actually running), ! 1774: telling it to execute that command. ! 1775: However, when the debugger hits a breakpoint, the roles are reversed, ! 1776: and the debugger wants to make an rpc call to the window program, ! 1777: so that it can inform the user that a breakpoint has been reached. ! 1778: .LP ! 1779: In order to do an RPC callback, ! 1780: you need a program number to make the RPC call on. ! 1781: Since this will be a dynamically generated program number, ! 1782: it should be in the transient range, 0x40000000 - 0x5fffffff. ! 1783: The routine ! 1784: .L gettransient() ! 1785: returns a valid program number in the transient range, ! 1786: and registers it with the portmapper. ! 1787: It only talks to the portmapper running on the same machine as the ! 1788: .L gettransient() ! 1789: routine itself. ! 1790: The call to ! 1791: .L pmap_set() ! 1792: is a test and set operation, ! 1793: in that it indivisibly tests whether a program number ! 1794: has already been registered, ! 1795: and if it has not, then reserves it. ! 1796: On return, the ! 1797: .L sockp ! 1798: argument will contain a socket that can be used ! 1799: as the argument to an ! 1800: .L svcudp_create() ! 1801: or ! 1802: .L svctcp_create() ! 1803: call. ! 1804: .LS ! 1805: #include <stdio.h> ! 1806: #include <rpc/rpc.h> ! 1807: #include <sys/socket.h> ! 1808: .sp.5 ! 1809: gettransient(proto, vers, sockp) ! 1810: int *sockp; ! 1811: { ! 1812: static int prognum = 0x40000000; ! 1813: int s, len, socktype; ! 1814: struct sockaddr_in addr; ! 1815: .sp.5 ! 1816: switch(proto) { ! 1817: case IPPROTO_UDP: ! 1818: socktype = SOCK_DGRAM; ! 1819: break; ! 1820: case IPPROTO_TCP: ! 1821: socktype = SOCK_STREAM; ! 1822: break; ! 1823: default: ! 1824: fprintf(stderr, "unknown protocol type\en"); ! 1825: return 0; ! 1826: } ! 1827: if (*sockp == RPC_ANYSOCK) { ! 1828: if ((s = socket(AF_INET, socktype, 0)) < 0) { ! 1829: perror("socket"); ! 1830: return (0); ! 1831: } ! 1832: *sockp = s; ! 1833: } ! 1834: else ! 1835: s = *sockp; ! 1836: addr.sin_addr.s_addr = 0; ! 1837: addr.sin_family = AF_INET; ! 1838: addr.sin_port = 0; ! 1839: len = sizeof(addr); ! 1840: /* ! 1841: * may be already bound, so don't check for err ! 1842: */ ! 1843: bind(s, &addr, len); ! 1844: if (getsockname(s, &addr, &len)< 0) { ! 1845: perror("getsockname"); ! 1846: return (0); ! 1847: } ! 1848: while (pmap_set(prognum++, vers, proto, addr.sin_port) == 0) ! 1849: continue; ! 1850: return (prognum-1); ! 1851: } ! 1852: .LE ! 1853: The following pair of programs illustrate how to use the ! 1854: .L gettransient() ! 1855: routine. ! 1856: The client makes an RPC call to the server, ! 1857: passing it a transient program number. ! 1858: Then the client waits around to receive a callback ! 1859: from the server at that program number. ! 1860: The server registers the program EXAMPELPROG, ! 1861: so that it can receive the RPC call ! 1862: informing it of the callback program number. ! 1863: Then at some random time (on receiving an ALRM signal in this example), ! 1864: it sends a callback RPC call, ! 1865: using the program number it received earlier. ! 1866: .LS ! 1867: /* ! 1868: * client ! 1869: */ ! 1870: #include <stdio.h> ! 1871: #include <rpc/rpc.h> ! 1872: .sp.5 ! 1873: int callback(); ! 1874: char hostname[256]; ! 1875: .sp.5 ! 1876: main(argc, argv) ! 1877: char **argv; ! 1878: { ! 1879: int x, ans, s; ! 1880: SVCXPRT *xprt; ! 1881: .sp.5 ! 1882: gethostname(hostname, sizeof(hostname)); ! 1883: s = RPC_ANYSOCK; ! 1884: x = gettransient(IPPROTO_UDP, 1, &s); ! 1885: fprintf(stderr, "client gets prognum %d\en", x); ! 1886: ! 1887: if ((xprt = svcudp_create(s)) == NULL) { ! 1888: fprintf(stderr, "rpc_server: svcudp_create\en"); ! 1889: exit(1); ! 1890: } ! 1891: (void)svc_register(xprt, x, 1, callback, 0); ! 1892: ! 1893: ans = callrpc(hostname, EXAMPLEPROG, EXAMPLEPROC_CALLBACK, ! 1894: EXAMPLEVERS, xdr_int, &x, xdr_void, 0); ! 1895: if (ans != 0) { ! 1896: fprintf(stderr, "call: "); ! 1897: clnt_perrno(ans); ! 1898: fprintf(stderr, "\en"); ! 1899: } ! 1900: svc_run(); ! 1901: fprintf(stderr, "Error: svc_run shouldn't have returned\en"); ! 1902: } ! 1903: .LE ! 1904: .LS ! 1905: callback(rqstp, transp) ! 1906: register struct svc_req *rqstp; ! 1907: register SVCXPRT *transp; ! 1908: { ! 1909: switch (rqstp->rq_proc) { ! 1910: case 0: ! 1911: if (svc_sendreply(transp, xdr_void, 0) == FALSE) { ! 1912: fprintf(stderr, "err: rusersd\en"); ! 1913: exit(1); ! 1914: } ! 1915: exit(0); ! 1916: case 1: ! 1917: if (!svc_getargs(transp, xdr_void, 0)) { ! 1918: svcerr_decode(transp); ! 1919: exit(1); ! 1920: } ! 1921: fprintf(stderr, "client got callback\en"); ! 1922: if (svc_sendreply(transp, xdr_void, 0) == FALSE) { ! 1923: fprintf(stderr, "err: rusersd"); ! 1924: exit(1); ! 1925: } ! 1926: } ! 1927: } ! 1928: .LE ! 1929: .LS ! 1930: /* ! 1931: * server ! 1932: */ ! 1933: #include <stdio.h> ! 1934: #include <rpc/rpc.h> ! 1935: #include <sys/signal.h> ! 1936: .sp.5 ! 1937: char *getnewprog(); ! 1938: char hostname[256]; ! 1939: int docallback(); ! 1940: int pnum; /*program number for callback routine */ ! 1941: .sp.5 ! 1942: main(argc, argv) ! 1943: char **argv; ! 1944: { ! 1945: gethostname(hostname, sizeof(hostname)); ! 1946: registerrpc(EXAMPLEPROG, EXAMPLEPROC_CALLBACK, EXAMPLEVERS, ! 1947: getnewprog, xdr_int, xdr_void); ! 1948: fprintf(stderr, "server going into svc_run\en"); ! 1949: alarm(10); ! 1950: signal(SIGALRM, docallback); ! 1951: svc_run(); ! 1952: fprintf(stderr, "Error: svc_run shouldn't have returned\en"); ! 1953: } ! 1954: .sp.5 ! 1955: char * ! 1956: getnewprog(pnump) ! 1957: char *pnump; ! 1958: { ! 1959: pnum = *(int *)pnump; ! 1960: return NULL; ! 1961: } ! 1962: .sp.5 ! 1963: docallback() ! 1964: { ! 1965: int ans; ! 1966: .sp.5 ! 1967: ans = callrpc(hostname, pnum, 1, 1, xdr_void, 0, xdr_void, 0); ! 1968: if (ans != 0) { ! 1969: fprintf(stderr, "server: "); ! 1970: clnt_perrno(ans); ! 1971: fprintf(stderr, "\en"); ! 1972: } ! 1973: } ! 1974: .LE
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.