rsalz@bbn.com (Rich Salz) (03/03/88)
Submitted-by: Stephen X. Nahm <sxn@Sun.COM> Posting-number: Volume 13, Issue 83 Archive-name: rpc3.9/part06 #! /bin/sh # This is a shell archive. To extract, remove the header and type "sh filename" # echo x - etc echo creating directory etc mkdir etc cd etc echo x - Makefile cat > Makefile <<'Funky_Stuff' # # @(#)Makefile 1.5 87/11/20 3.9 RPCSRC # # Files and programs for /etc. rpclib must have already been installed. # DESTDIR= CFLAGS= -O LIB = -lrpclib LDFLAGS= $(LIB) BIN = portmap rpcinfo MISC= rpc all: ${BIN} portmap: ${CC} ${CFLAGS} -o $@ $@.c ${LDFLAGS} rpcinfo: getopt.o ${CC} ${CFLAGS} -o $@ $@.c getopt.o ${LDFLAGS} install: ${BIN} -mkdir ${DESTDIR}/etc && chown bin ${DESTDIR}/etc && \ chmod 755 ${DESTDIR}/etc @echo "Installing RPC utility files in ${DESTDIR}/etc" @set -x;for i in ${BIN}; do \ (install -s $$i ${DESTDIR}/etc/$$i); done @echo "Installing ${DESTDIR}/etc/rpc" @set -x;for i in ${MISC}; do \ (install -c -m 644 $$i ${DESTDIR}/etc/$$i); done clean: rm -f core *.o rm -f ${BIN} depend: ${BIN} rm -f makedep for i in ${BIN}; do \ ${CC} -M ${INCPATH} $$i.c | sed 's/\.o//' | \ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \ else rec = rec " " $$2 } } \ END { print rec } ' >> makedep; done echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep echo '$$r makedep' >>eddep echo 'w' >>eddep cp Makefile Makefile.bak ed - Makefile < eddep rm eddep makedep echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile echo '# see make depend above' >> Makefile depend.42BSD depend.42bsd: cp /dev/null x.c for i in $(BIN) ; do \ (/bin/grep '^#[ ]*include' x.c $$i.c | sed \ -e 's,<\(.*\)>,"/usr/include/\1",' \ -e 's/:[^"]*"\([^"]*\)".*/: \1/' \ -e 's/\.c/\.o/' >>makedep); done echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep echo '$$r makedep' >>eddep echo 'w' >>eddep cp Makefile Makefile.bak ed - Makefile < eddep rm eddep makedep x.c echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile echo '# see make depend above' >> Makefile # DO NOT DELETE THIS LINE -- make depend uses it Funky_Stuff len=`wc -c < Makefile` if [ $len != 2046 ] ; then echo error: Makefile was $len bytes long, should have been 2046 fi echo x - getopt.c cat > getopt.c <<'Funky_Stuff' /* @(#)getopt.c 1.2 87/11/30 3.9 RPCSRC */ /* this is a public domain version of getopt */ /*LINTLIBRARY*/ #ifndef NULL #define NULL 0 #endif NULL #ifndef EOF #define EOF (-1) #endif EOF #define ERR(s, c) if(opterr){\ extern int strlen(), write();\ char errbuf[2];\ errbuf[0] = c; errbuf[1] = '\n';\ (void) write(2, argv[0], strlen(argv[0]));\ (void) write(2, s, strlen(s));\ (void) write(2, errbuf, 2);} #define strchr index extern int strcmp(); extern char *strchr(); int opterr = 1; int optind = 1; int optopt; char *optarg; int getopt(argc, argv, opts) int argc; char **argv, *opts; { static int sp = 1; register int c; register char *cp; if(sp == 1) if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return(EOF); else if(strcmp(argv[optind], "--") == NULL) { optind++; return(EOF); } optopt = c = argv[optind][sp]; if(c == ':' || (cp=strchr(opts, c)) == NULL) { ERR(": unknown option, -", c); if(argv[optind][++sp] == '\0') { optind++; sp = 1; } return('?'); } if(*++cp == ':') { if(argv[optind][sp+1] != '\0') optarg = &argv[optind++][sp+1]; else if(++optind >= argc) { ERR(": argument missing for -", c); sp = 1; return('?'); } else optarg = argv[optind++]; sp = 1; } else { if(argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = NULL; } return(c); } Funky_Stuff len=`wc -c < getopt.c` if [ $len != 1381 ] ; then echo error: getopt.c was $len bytes long, should have been 1381 fi echo x - portmap.c cat > portmap.c <<'Funky_Stuff' /* @(#)portmap.c 1.1 87/11/04 3.9 RPCSRC */ #ifndef lint static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro"; #endif /* * Copyright (c) 1984 by Sun Microsystems, Inc. */ /* * portmap.c, Implements the program,version to port number mapping for * rpc. */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #include <rpc/rpc.h> #include <rpc/pmap_prot.h> #include <stdio.h> #include <netdb.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/ioctl.h> #include <sys/wait.h> char *malloc(); int reg_service(); struct pmaplist *pmaplist; static int debugging = 0; main() { SVCXPRT *xprt; int sock, pid, t; struct sockaddr_in addr; int len = sizeof(struct sockaddr_in); register struct pmaplist *pml; #ifndef DEBUG pid = fork(); if (pid < 0) { perror("portmap: fork"); exit(1); } if (pid != 0) exit(0); for (t = 0; t < 20; t++) close(t); open("/", 0); dup2(0, 1); dup2(0, 2); t = open("/dev/tty", 2); if (t >= 0) { ioctl(t, TIOCNOTTY, (char *)0); close(t); } #endif if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("portmap cannot create socket"); exit(1); } addr.sin_addr.s_addr = 0; addr.sin_family = AF_INET; addr.sin_port = htons(PMAPPORT); if (bind(sock, (struct sockaddr *)&addr, len) != 0) { perror("portmap cannot bind"); exit(1); } if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) { fprintf(stderr, "couldn't do udp_create\n"); exit(1); } /* make an entry for ourself */ pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist)); pml->pml_next = 0; pml->pml_map.pm_prog = PMAPPROG; pml->pml_map.pm_vers = PMAPVERS; pml->pml_map.pm_prot = IPPROTO_UDP; pml->pml_map.pm_port = PMAPPORT; pmaplist = pml; if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("portmap cannot create socket"); exit(1); } if (bind(sock, (struct sockaddr *)&addr, len) != 0) { perror("portmap cannot bind"); exit(1); } if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == (SVCXPRT *)NULL) { fprintf(stderr, "couldn't do tcp_create\n"); exit(1); } /* make an entry for ourself */ pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist)); pml->pml_map.pm_prog = PMAPPROG; pml->pml_map.pm_vers = PMAPVERS; pml->pml_map.pm_prot = IPPROTO_TCP; pml->pml_map.pm_port = PMAPPORT; pml->pml_next = pmaplist; pmaplist = pml; (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE); svc_run(); fprintf(stderr, "run_svc returned unexpectedly\n"); abort(); } static struct pmaplist * find_service(prog, vers, prot) u_long prog; u_long vers; { register struct pmaplist *hit = NULL; register struct pmaplist *pml; for (pml = pmaplist; pml != NULL; pml = pml->pml_next) { if ((pml->pml_map.pm_prog != prog) || (pml->pml_map.pm_prot != prot)) continue; hit = pml; if (pml->pml_map.pm_vers == vers) break; } return (hit); } /* * 1 OK, 0 not */ reg_service(rqstp, xprt) struct svc_req *rqstp; SVCXPRT *xprt; { struct pmap reg; struct pmaplist *pml, *prevpml, *fnd; int ans, port; caddr_t t; #ifdef DEBUG fprintf(stderr, "server: about do a switch\n"); #endif switch (rqstp->rq_proc) { case PMAPPROC_NULL: /* * Null proc call */ if ((!svc_sendreply(xprt, xdr_void, NULL)) && debugging) { abort(); } break; case PMAPPROC_SET: /* * Set a program,version to port mapping */ if (!svc_getargs(xprt, xdr_pmap, ®)) svcerr_decode(xprt); else { /* * check to see if already used * find_service returns a hit even if * the versions don't match, so check for it */ fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot); if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) { if (fnd->pml_map.pm_port == reg.pm_port) { ans = 1; goto done; } else { ans = 0; goto done; } } else { /* * add to END of list */ pml = (struct pmaplist *) malloc((u_int)sizeof(struct pmaplist)); pml->pml_map = reg; pml->pml_next = 0; if (pmaplist == 0) { pmaplist = pml; } else { for (fnd= pmaplist; fnd->pml_next != 0; fnd = fnd->pml_next); fnd->pml_next = pml; } ans = 1; } done: if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) && debugging) { fprintf(stderr, "svc_sendreply\n"); abort(); } } break; case PMAPPROC_UNSET: /* * Remove a program,version to port mapping. */ if (!svc_getargs(xprt, xdr_pmap, ®)) svcerr_decode(xprt); else { ans = 0; for (prevpml = NULL, pml = pmaplist; pml != NULL; ) { if ((pml->pml_map.pm_prog != reg.pm_prog) || (pml->pml_map.pm_vers != reg.pm_vers)) { /* both pml & prevpml move forwards */ prevpml = pml; pml = pml->pml_next; continue; } /* found it; pml moves forward, prevpml stays */ ans = 1; t = (caddr_t)pml; pml = pml->pml_next; if (prevpml == NULL) pmaplist = pml; else prevpml->pml_next = pml; free(t); } if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) && debugging) { fprintf(stderr, "svc_sendreply\n"); abort(); } } break; case PMAPPROC_GETPORT: /* * Lookup the mapping for a program,version and return its port */ if (!svc_getargs(xprt, xdr_pmap, ®)) svcerr_decode(xprt); else { fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot); if (fnd) port = fnd->pml_map.pm_port; else port = 0; if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) && debugging) { fprintf(stderr, "svc_sendreply\n"); abort(); } } break; case PMAPPROC_DUMP: /* * Return the current set of mapped program,version */ if (!svc_getargs(xprt, xdr_void, NULL)) svcerr_decode(xprt); else { if ((!svc_sendreply(xprt, xdr_pmaplist, (caddr_t)&pmaplist)) && debugging) { fprintf(stderr, "svc_sendreply\n"); abort(); } } break; case PMAPPROC_CALLIT: /* * Calls a procedure on the local machine. If the requested * procedure is not registered this procedure does not return * error information!! * This procedure is only supported on rpc/udp and calls via * rpc/udp. It passes null authentication parameters. */ callit(rqstp, xprt); break; default: svcerr_noproc(xprt); break; } } /* * Stuff for the rmtcall service */ #define ARGSIZE 9000 typedef struct encap_parms { u_long arglen; char *args; }; static bool_t xdr_encap_parms(xdrs, epp) XDR *xdrs; struct encap_parms *epp; { return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE)); } typedef struct rmtcallargs { u_long rmt_prog; u_long rmt_vers; u_long rmt_port; u_long rmt_proc; struct encap_parms rmt_args; }; static bool_t xdr_rmtcall_args(xdrs, cap) register XDR *xdrs; register struct rmtcallargs *cap; { /* does not get a port number */ if (xdr_u_long(xdrs, &(cap->rmt_prog)) && xdr_u_long(xdrs, &(cap->rmt_vers)) && xdr_u_long(xdrs, &(cap->rmt_proc))) { return (xdr_encap_parms(xdrs, &(cap->rmt_args))); } return (FALSE); } static bool_t xdr_rmtcall_result(xdrs, cap) register XDR *xdrs; register struct rmtcallargs *cap; { if (xdr_u_long(xdrs, &(cap->rmt_port))) return (xdr_encap_parms(xdrs, &(cap->rmt_args))); return (FALSE); } /* * only worries about the struct encap_parms part of struct rmtcallargs. * The arglen must already be set!! */ static bool_t xdr_opaque_parms(xdrs, cap) XDR *xdrs; struct rmtcallargs *cap; { return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen)); } /* * This routine finds and sets the length of incoming opaque paraters * and then calls xdr_opaque_parms. */ static bool_t xdr_len_opaque_parms(xdrs, cap) register XDR *xdrs; struct rmtcallargs *cap; { register u_int beginpos, lowpos, highpos, currpos, pos; beginpos = lowpos = pos = xdr_getpos(xdrs); highpos = lowpos + ARGSIZE; while ((int)(highpos - lowpos) >= 0) { currpos = (lowpos + highpos) / 2; if (xdr_setpos(xdrs, currpos)) { pos = currpos; lowpos = currpos + 1; } else { highpos = currpos - 1; } } xdr_setpos(xdrs, beginpos); cap->rmt_args.arglen = pos - beginpos; return (xdr_opaque_parms(xdrs, cap)); } /* * Call a remote procedure service * This procedure is very quiet when things go wrong. * The proc is written to support broadcast rpc. In the broadcast case, * a machine should shut-up instead of complain, less the requestor be * overrun with complaints at the expense of not hearing a valid reply ... * * This now forks so that the program & process that it calls can call * back to the portmapper. */ static callit(rqstp, xprt) struct svc_req *rqstp; SVCXPRT *xprt; { struct rmtcallargs a; struct pmaplist *pml; u_short port; struct sockaddr_in me; int pid, socket = -1; CLIENT *client; struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred; struct timeval timeout; union wait pinfo; char buf[ARGSIZE]; timeout.tv_sec = 5; timeout.tv_usec = 0; a.rmt_args.args = buf; if (!svc_getargs(xprt, xdr_rmtcall_args, &a)) return; if ((pml = find_service(a.rmt_prog, a.rmt_vers, IPPROTO_UDP)) == NULL) return; /* * fork a child to do the work. Parent immediately returns. * Child exits upon completion. */ if ((pid = fork()) != 0) { if (debugging && (pid < 0)) { fprintf(stderr, "portmap CALLIT: cannot fork.\n"); } /* reap previous forks. */ for (; pid>0; pid = wait3(&pinfo, WNOHANG, (struct rusage *)0)); return; } port = pml->pml_map.pm_port; get_myaddress(&me); me.sin_port = htons(port); client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &socket); if (client != (CLIENT *)NULL) { if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) { client->cl_auth = authunix_create(au->aup_machname, au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids); } a.rmt_port = (u_long)port; if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a, xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) { svc_sendreply(xprt, xdr_rmtcall_result, &a); } AUTH_DESTROY(client->cl_auth); clnt_destroy(client); } (void)close(socket); exit(0); } Funky_Stuff len=`wc -c < portmap.c` if [ $len != 11403 ] ; then echo error: portmap.c was $len bytes long, should have been 11403 fi echo x - rpc cat > rpc <<'Funky_Stuff' # # rpc 87/12/02 3.9 RPCSRC # portmapper 100000 portmap sunrpc rstat_svc 100001 rstatd rstat rup perfmeter rusersd 100002 rusers nfs 100003 nfsprog ypserv 100004 ypprog mountd 100005 mount showmount ypbind 100007 walld 100008 rwall shutdown yppasswdd 100009 yppasswd etherstatd 100010 etherstat rquotad 100011 rquotaprog quota rquota sprayd 100012 spray 3270_mapper 100013 rje_mapper 100014 selection_svc 100015 selnsvc database_svc 100016 rexd 100017 rex alis 100018 sched 100019 llockmgr 100020 nlockmgr 100021 x25.inr 100022 statmon 100023 status 100024 bootparam 100026 ypupdated 100028 ypupdate keyserv 100029 keyserver Funky_Stuff len=`wc -c < rpc` if [ $len != 787 ] ; then echo error: rpc was $len bytes long, should have been 787 fi echo x - rpcinfo.c cat > rpcinfo.c <<'Funky_Stuff' /* @(#)rpcinfo.c 1.5 87/11/20 3.9 RPCSRC */ #ifndef lint static char sccsid[] = "@(#)rpcinfo.c 1.22 87/08/12 SMI"; #endif /* * Copyright (C) 1986, Sun Microsystems, Inc. */ /* * rpcinfo: ping a particular rpc program * or dump the portmapper */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #include <rpc/rpc.h> #include <stdio.h> #include <sys/socket.h> #include <sys/time.h> #include <netdb.h> #include <rpc/pmap_prot.h> #include <rpc/pmap_clnt.h> #include <signal.h> #include <ctype.h> #define MAXHOSTLEN 256 #define MIN_VERS ((u_long) 0) #define MAX_VERS ((u_long) 4294967295L) static void udpping(/*u_short portflag, int argc, char **argv*/); static void tcpping(/*u_short portflag, int argc, char **argv*/); static int pstatus(/*CLIENT *client, u_long prognum, u_long vers*/); static void pmapdump(/*int argc, char **argv*/); static bool_t reply_proc(/*void *res, struct sockaddr_in *who*/); static void brdcst(/*int argc, char **argv*/); static void usage(/*void*/); static u_long getprognum(/*char *arg*/); static u_long getvers(/*char *arg*/); static void get_inet_address(/*struct sockaddr_in *addr, char *host*/); extern u_long inet_addr(); /* in 4.2BSD, arpa/inet.h called that a in_addr */ extern char *inet_ntoa(); /* * Functions to be performed. */ #define NONE 0 /* no function */ #define PMAPDUMP 1 /* dump portmapper registrations */ #define TCPPING 2 /* ping TCP service */ #define UDPPING 3 /* ping UDP service */ #define BRDCST 4 /* ping broadcast UDP service */ int main(argc, argv) int argc; char **argv; { register int c; extern char *optarg; extern int optind; int errflg; int function; u_short portnum; function = NONE; portnum = 0; errflg = 0; while ((c = getopt(argc, argv, "ptubn:")) != EOF) { switch (c) { case 'p': if (function != NONE) errflg = 1; else function = PMAPDUMP; break; case 't': if (function != NONE) errflg = 1; else function = TCPPING; break; case 'u': if (function != NONE) errflg = 1; else function = UDPPING; break; case 'b': if (function != NONE) errflg = 1; else function = BRDCST; break; case 'n': portnum = (u_short) atoi(optarg); /* hope we don't get bogus # */ break; case '?': errflg = 1; } } if (errflg || function == NONE) { usage(); return (1); } switch (function) { case PMAPDUMP: if (portnum != 0) { usage(); return (1); } pmapdump(argc - optind, argv + optind); break; case UDPPING: udpping(portnum, argc - optind, argv + optind); break; case TCPPING: tcpping(portnum, argc - optind, argv + optind); break; case BRDCST: if (portnum != 0) { usage(); return (1); } brdcst(argc - optind, argv + optind); break; } return (0); } static void udpping(portnum, argc, argv) u_short portnum; int argc; char **argv; { struct timeval to; struct sockaddr_in addr; enum clnt_stat rpc_stat; CLIENT *client; u_long prognum, vers, minvers, maxvers; int sock = RPC_ANYSOCK; struct rpc_err rpcerr; int failure; if (argc < 2 || argc > 3) { usage(); exit(1); } prognum = getprognum(argv[1]); get_inet_address(&addr, argv[0]); /* Open the socket here so it will survive calls to clnt_destroy */ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { perror("rpcinfo: socket"); exit(1); } failure = 0; if (argc == 2) { /* * A call to version 0 should fail with a program/version * mismatch, and give us the range of versions supported. */ addr.sin_port = htons(portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create(&addr, prognum, (u_long)0, to, &sock)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu is not available\n", prognum); exit(1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ addr.sin_port = htons(portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create(&addr, prognum, MAX_VERS, to, &sock)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu version %lu is not available\n", prognum, MAX_VERS); exit(1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * It also supports version MAX_VERS. * Looks like we have a wise guy. * OK, we give them information on all * 4 billion versions they support... */ minvers = 0; maxvers = MAX_VERS; } else { (void) pstatus(client, prognum, MAX_VERS); exit(1); } } else { (void) pstatus(client, prognum, (u_long)0); exit(1); } clnt_destroy(client); for (vers = minvers; vers <= maxvers; vers++) { addr.sin_port = htons(portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create(&addr, prognum, vers, to, &sock)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu version %lu is not available\n", prognum, vers); exit(1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (pstatus(client, prognum, vers) < 0) failure = 1; clnt_destroy(client); } } else { vers = getvers(argv[2]); addr.sin_port = htons(portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create(&addr, prognum, vers, to, &sock)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu version %lu is not available\n", prognum, vers); exit(1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (pstatus(client, prognum, vers) < 0) failure = 1; } (void) close(sock); /* Close it up again */ if (failure) exit(1); } static void tcpping(portnum, argc, argv) u_short portnum; int argc; char **argv; { struct timeval to; struct sockaddr_in addr; enum clnt_stat rpc_stat; CLIENT *client; u_long prognum, vers, minvers, maxvers; int sock = RPC_ANYSOCK; struct rpc_err rpcerr; int failure; if (argc < 2 || argc > 3) { usage(); exit(1); } prognum = getprognum(argv[1]); get_inet_address(&addr, argv[0]); failure = 0; if (argc == 2) { /* * A call to version 0 should fail with a program/version * mismatch, and give us the range of versions supported. */ addr.sin_port = htons(portnum); if ((client = clnttcp_create(&addr, prognum, MIN_VERS, &sock, 0, 0)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu is not available\n", prognum); exit(1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ addr.sin_port = htons(portnum); if ((client = clnttcp_create(&addr, prognum, MAX_VERS, &sock, 0, 0)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu version %lu is not available\n", prognum, MAX_VERS); exit(1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * It also supports version MAX_VERS. * Looks like we have a wise guy. * OK, we give them information on all * 4 billion versions they support... */ minvers = 0; maxvers = MAX_VERS; } else { (void) pstatus(client, prognum, MAX_VERS); exit(1); } } else { (void) pstatus(client, prognum, MIN_VERS); exit(1); } clnt_destroy(client); (void) close(sock); sock = RPC_ANYSOCK; /* Re-initialize it for later */ for (vers = minvers; vers <= maxvers; vers++) { addr.sin_port = htons(portnum); if ((client = clnttcp_create(&addr, prognum, vers, &sock, 0, 0)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu version %lu is not available\n", prognum, vers); exit(1); } to.tv_usec = 0; to.tv_sec = 10; rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (pstatus(client, prognum, vers) < 0) failure = 1; clnt_destroy(client); (void) close(sock); sock = RPC_ANYSOCK; } } else { vers = getvers(argv[2]); addr.sin_port = htons(portnum); if ((client = clnttcp_create(&addr, prognum, vers, &sock, 0, 0)) == NULL) { clnt_pcreateerror("rpcinfo"); printf("program %lu version %lu is not available\n", prognum, vers); exit(1); } to.tv_usec = 0; to.tv_sec = 10; rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL, xdr_void, (char *)NULL, to); if (pstatus(client, prognum, vers) < 0) failure = 1; } if (failure) exit(1); } /* * This routine should take a pointer to an "rpc_err" structure, rather than * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to * a CLIENT structure rather than a pointer to an "rpc_err" structure. * As such, we have to keep the CLIENT structure around in order to print * a good error message. */ static int pstatus(client, prognum, vers) register CLIENT *client; u_long prognum; u_long vers; { struct rpc_err rpcerr; clnt_geterr(client, &rpcerr); if (rpcerr.re_status != RPC_SUCCESS) { clnt_perror(client, "rpcinfo"); printf("program %lu version %lu is not available\n", prognum, vers); return (-1); } else { printf("program %lu version %lu ready and waiting\n", prognum, vers); return (0); } } static void pmapdump(argc, argv) int argc; char **argv; { struct sockaddr_in server_addr; register struct hostent *hp; struct pmaplist *head = NULL; int socket = RPC_ANYSOCK; struct timeval minutetimeout; register CLIENT *client; struct rpcent *rpc; if (argc > 1) { usage(); exit(1); } if (argc == 1) get_inet_address(&server_addr, argv[0]); else { bzero((char *)&server_addr, sizeof server_addr); server_addr.sin_family = AF_INET; if ((hp = gethostbyname("localhost")) != NULL) bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, hp->h_length); else server_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); } minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; server_addr.sin_port = htons(PMAPPORT); if ((client = clnttcp_create(&server_addr, PMAPPROG, PMAPVERS, &socket, 50, 500)) == NULL) { clnt_pcreateerror("rpcinfo: can't contact portmapper"); exit(1); } if (clnt_call(client, PMAPPROC_DUMP, xdr_void, NULL, xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) { fprintf(stderr, "rpcinfo: can't contact portmapper: "); clnt_perror(client, "rpcinfo"); exit(1); } if (head == NULL) { printf("No remote programs registered.\n"); } else { printf(" program vers proto port\n"); for (; head != NULL; head = head->pml_next) { printf("%10ld%5ld", head->pml_map.pm_prog, head->pml_map.pm_vers); if (head->pml_map.pm_prot == IPPROTO_UDP) printf("%6s", "udp"); else if (head->pml_map.pm_prot == IPPROTO_TCP) printf("%6s", "tcp"); else printf("%6ld", head->pml_map.pm_prot); printf("%7ld", head->pml_map.pm_port); rpc = getrpcbynumber(head->pml_map.pm_prog); if (rpc) printf(" %s\n", rpc->r_name); else printf("\n"); } } } /* * reply_proc collects replies from the broadcast. * to get a unique list of responses the output of rpcinfo should * be piped through sort(1) and then uniq(1). */ /*ARGSUSED*/ static bool_t reply_proc(res, who) void *res; /* Nothing comes back */ struct sockaddr_in *who; /* Who sent us the reply */ { register struct hostent *hp; hp = gethostbyaddr((char *) &who->sin_addr, sizeof who->sin_addr, AF_INET); printf("%s %s\n", inet_ntoa(who->sin_addr), (hp == NULL) ? "(unknown)" : hp->h_name); return(FALSE); } static void brdcst(argc, argv) int argc; char **argv; { enum clnt_stat rpc_stat; u_long prognum, vers; if (argc != 2) { usage(); exit(1); } prognum = getprognum(argv[0]); vers = getvers(argv[1]); rpc_stat = clnt_broadcast(prognum, vers, NULLPROC, xdr_void, (char *)NULL, xdr_void, (char *)NULL, reply_proc); if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) { fprintf(stderr, "rpcinfo: broadcast failed: %s\n", clnt_sperrno(rpc_stat)); exit(1); } exit(0); } static void usage() { fprintf(stderr, "Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n"); fprintf(stderr, " rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n"); fprintf(stderr, " rpcinfo -p [ host ]\n"); fprintf(stderr, " rpcinfo -b prognum versnum\n"); } static u_long getprognum(arg) char *arg; { register struct rpcent *rpc; register u_long prognum; if (isalpha(*arg)) { rpc = getrpcbyname(arg); if (rpc == NULL) { fprintf(stderr, "rpcinfo: %s is unknown service\n", arg); exit(1); } prognum = rpc->r_number; } else { prognum = (u_long) atoi(arg); } return (prognum); } static u_long getvers(arg) char *arg; { register u_long vers; vers = (int) atoi(arg); return (vers); } static void get_inet_address(addr, host) struct sockaddr_in *addr; char *host; { register struct hostent *hp; bzero((char *)addr, sizeof *addr); addr->sin_addr.s_addr = (u_long) inet_addr(host); if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) { if ((hp = gethostbyname(host)) == NULL) { fprintf(stderr, "rpcinfo: %s is unknown host\n", host); exit(1); } bcopy(hp->h_addr, (char *)&addr->sin_addr, hp->h_length); } addr->sin_family = AF_INET; } Funky_Stuff len=`wc -c < rpcinfo.c` if [ $len != 15727 ] ; then echo error: rpcinfo.c was $len bytes long, should have been 15727 fi cd .. echo done with directory etc echo x - demo echo creating directory demo mkdir demo cd demo echo x - Makefile cat > Makefile <<'Funky_Stuff' # # @(#)Makefile 1.4 87/11/30 3.9 RPCSRC # # # Build all demo services # MAKE = make LIB=-lrpclib SUBDIR= dir msg sort all: ${SUBDIR} clean cleanup: cd dir; $(MAKE) ${MFLAGS} cleanup cd msg; $(MAKE) ${MFLAGS} cleanup cd sort; $(MAKE) ${MFLAGS} cleanup install: @echo "No installations done." ${SUBDIR}: FRC cd $@; $(MAKE) ${MFLAGS} LIB=$(LIB) FRC: Funky_Stuff len=`wc -c < Makefile` if [ $len != 361 ] ; then echo error: Makefile was $len bytes long, should have been 361 fi echo x - dir echo creating directory dir mkdir dir cd dir echo x - Makefile cat > Makefile <<'Funky_Stuff' # # @(#)Makefile 1.3 87/11/30 3.9 RPCSRC # BIN = dir_svc rls GEN = dir_clnt.c dir_svc.c dir_xdr.c dir.h LIB = -lrpclib RPCCOM = rpcgen all: $(BIN) $(GEN): dir.x $(RPCCOM) dir.x dir_svc: dir_proc.o dir_svc.o dir_xdr.o $(CC) -o $@ dir_proc.o dir_svc.o dir_xdr.o $(LIB) rls: rls.o dir_clnt.o dir_xdr.o $(CC) -o $@ rls.o dir_clnt.o dir_xdr.o $(LIB) rls.o: rls.c dir.h dir_proc.o: dir_proc.c dir.h clean cleanup: rm -f $(GEN) *.o $(BIN) Funky_Stuff len=`wc -c < Makefile` if [ $len != 444 ] ; then echo error: Makefile was $len bytes long, should have been 444 fi echo x - dir.x cat > dir.x <<'Funky_Stuff' /* @(#)dir.x 1.1 87/11/04 3.9 RPCSRC */ /* * dir.x: Remote directory listing protocol */ const MAXNAMELEN = 255; /* maximum length of a directory entry */ typedef string nametype<MAXNAMELEN>; /* a directory entry */ typedef struct namenode *namelist; /* a link in the listing */ /* * A node in the directory listing */ struct namenode { nametype name; /* name of directory entry */ namelist next; /* next entry */ }; /* * The result of a READDIR operation. */ union readdir_res switch (int errno) { case 0: namelist list; /* no error: return directory listing */ default: void; /* error occurred: nothing else to return */ }; /* * The directory program definition */ program DIRPROG { version DIRVERS { readdir_res READDIR(nametype) = 1; } = 1; } = 76; Funky_Stuff len=`wc -c < dir.x` if [ $len != 780 ] ; then echo error: dir.x was $len bytes long, should have been 780 fi echo x - dir_proc.c cat > dir_proc.c <<'Funky_Stuff' /* @(#)dir_proc.c 1.3 87/11/16 3.9 RPCSRC */ /* * dir_proc.c: remote readdir implementation */ #include <rpc/rpc.h> #include <sys/dir.h> #include "dir.h" extern int errno; extern char *malloc(); extern char *strcpy(); readdir_res * readdir_1(dirname) nametype *dirname; { DIR *dirp; struct direct *d; namelist nl; namelist *nlp; static readdir_res res; /* must be static! */ /* * Open directory */ dirp = opendir(*dirname); if (dirp == NULL) { res.errno = errno; return (&res); } /* * Free previous result */ xdr_free(xdr_readdir_res, &res); /* * Collect directory entries */ nlp = &res.readdir_res_u.list; while (d = readdir(dirp)) { nl = *nlp = (namenode *) malloc(sizeof(namenode)); nl->name = malloc(strlen(d->d_name)+1); strcpy(nl->name, d->d_name); nlp = &nl->next; } *nlp = NULL; /* * Return the result */ res.errno = 0; closedir(dirp); return (&res); } Funky_Stuff len=`wc -c < dir_proc.c` if [ $len != 919 ] ; then echo error: dir_proc.c was $len bytes long, should have been 919 fi echo x - rls.c cat > rls.c <<'Funky_Stuff' /* @(#)rls.c 1.1 87/11/04 3.9 RPCSRC */ /* * rls.c: Remote directory listing client */ #include <stdio.h> #include <rpc/rpc.h> /* always need this */ #include "dir.h" /* need this too: will be generated by rpcgen*/ extern int errno; main(argc, argv) int argc; char *argv[]; { CLIENT *cl; char *server; char *dir; readdir_res *result; namelist nl; if (argc != 3) { fprintf(stderr, "usage: %s host directory\n", argv[0]); exit(1); } /* * Remember what our command line arguments refer to */ server = argv[1]; dir = argv[2]; /* * Create client "handle" used for calling MESSAGEPROG on the * server designated on the command line. We tell the rpc package * to use the "tcp" protocol when contacting the server. */ cl = clnt_create(server, DIRPROG, DIRVERS, "tcp"); if (cl == NULL) { /* * Couldn't establish connection with server. * Print error message and die. */ clnt_pcreateerror(server); exit(1); } /* * Call the remote procedure "readdir" on the server */ result = readdir_1(&dir, cl); if (result == NULL) { /* * An error occurred while calling the server. * Print error message and die. */ clnt_perror(cl, server); exit(1); } /* * Okay, we successfully called the remote procedure. */ if (result->errno != 0) { /* * A remote system error occurred. * Print error message and die. */ errno = result->errno; perror(dir); exit(1); } /* * Successfuly got a directory listing. * Print it out. */ for (nl = result->readdir_res_u.list; nl != NULL; nl = nl->next) { printf("%s\n", nl->name); } } Funky_Stuff len=`wc -c < rls.c` if [ $len != 1611 ] ; then echo error: rls.c was $len bytes long, should have been 1611 fi cd .. echo done with directory dir echo x - msg echo creating directory msg mkdir msg cd msg echo x - Makefile cat > Makefile <<'Funky_Stuff' # # @(#)Makefile 1.4 87/11/30 3.9 RPCSRC # BIN = printmsg msg_svc rprintmsg GEN = msg_clnt.c msg_svc.c msg.h LIB = -lrpclib RPCCOM = rpcgen all: $(BIN) # # This is the non-networked version of the program # printmsg: printmsg.o $(CC) -o $@ printmsg.o # # note: no xdr routines are generated here, due this service's # use of basic data types. # $(GEN): msg.x $(RPCCOM) msg.x msg_svc: msg_proc.o msg_svc.o $(CC) -o $@ msg_proc.o msg_svc.o $(LIB) rprintmsg: rprintmsg.o msg_clnt.o $(CC) -o $@ rprintmsg.o msg_clnt.o $(LIB) rprintmsg.o: rprintmsg.c msg.h msg_proc.o: msg_proc.c msg.h clean cleanup: rm -f $(GEN) *.o $(BIN) Funky_Stuff len=`wc -c < Makefile` if [ $len != 640 ] ; then echo error: Makefile was $len bytes long, should have been 640 fi echo x - msg.x cat > msg.x <<'Funky_Stuff' /* @(#)msg.x 1.1 87/11/04 3.9 RPCSRC */ /* * msg.x: Remote message printing protocol */ program MESSAGEPROG { version MESSAGEVERS { int PRINTMESSAGE(string) = 1; } = 1; } = 99; Funky_Stuff len=`wc -c < msg.x` if [ $len != 183 ] ; then echo error: msg.x was $len bytes long, should have been 183 fi echo x - msg_proc.c cat > msg_proc.c <<'Funky_Stuff' /* @(#)msg_proc.c 1.1 87/11/04 3.9 RPCSRC */ /* * msg_proc.c: implementation of the remote procedure "printmessage" */ #include <stdio.h> #include <rpc/rpc.h> /* always need this here */ #include "msg.h" /* need this too: msg.h will be generated by rpcgen */ /* * Remote verson of "printmessage" */ int * printmessage_1(msg) char **msg; { static int result; /* must be static! */ FILE *f; f = fopen("/dev/console", "w"); if (f == NULL) { result = 0; return (&result); } fprintf(f, "%s\n", *msg); fclose(f); result = 1; return (&result); } Funky_Stuff len=`wc -c < msg_proc.c` if [ $len != 562 ] ; then echo error: msg_proc.c was $len bytes long, should have been 562 fi echo x - printmsg.c cat > printmsg.c <<'Funky_Stuff' /* @(#)printmsg.c 1.1 87/11/04 3.9 RPCSRC */ /* * printmsg.c: print a message on the console */ #include <stdio.h> main(argc, argv) int argc; char *argv[]; { char *message; if (argc < 2) { fprintf(stderr, "usage: %s <message>\n", argv[0]); exit(1); } message = argv[1]; if (!printmessage(message)) { fprintf(stderr, "%s: sorry, couldn't print your message\n", argv[0]); exit(1); } printf("Message delivered!\n"); } /* * Print a message to the console. * Return a boolean indicating whether the message was actually printed. */ printmessage(msg) char *msg; { FILE *f; f = fopen("/dev/console", "w"); if (f == NULL) { return (0); } fprintf(f, "%s\n", msg); fclose(f); return(1); } Funky_Stuff len=`wc -c < printmsg.c` if [ $len != 720 ] ; then echo error: printmsg.c was $len bytes long, should have been 720 fi echo x - rprintmsg.c cat > rprintmsg.c <<'Funky_Stuff' /* @(#)rprintmsg.c 1.1 87/11/04 3.9 RPCSRC */ /* * rprintmsg.c: remote version of "printmsg.c" */ #include <stdio.h> #include <rpc/rpc.h> /* always need this */ #include "msg.h" /* need this too: will be generated by rpcgen*/ main(argc, argv) int argc; char *argv[]; { CLIENT *cl; int *result; char *server; char *message; if (argc < 3) { fprintf(stderr, "usage: %s host message\n", argv[0]); exit(1); } /* * Remember what our command line arguments refer to */ server = argv[1]; message = argv[2]; /* * Create client "handle" used for calling MESSAGEPROG on the * server designated on the command line. We tell the rpc package * to use the "tcp" protocol when contacting the server. */ cl = clnt_create(server, MESSAGEPROG, MESSAGEVERS, "tcp"); if (cl == NULL) { /* * Couldn't establish connection with server. * Print error message and die. */ clnt_pcreateerror(server); exit(1); } /* * Call the remote procedure "printmessage" on the server */ result = printmessage_1(&message, cl); if (result == NULL) { /* * An error occurred while calling the server. * Print error message and die. */ clnt_perror(cl, server); exit(1); } /* * Okay, we successfully called the remote procedure. */ if (*result == 0) { /* * Server was unable to print our message. * Print error message and die. */ fprintf(stderr, "%s: sorry, %s couldn't print your message\n", argv[0], server); exit(1); } /* * The message got printed on the server's console */ printf("Message delivered to %s!\n", server); } Funky_Stuff len=`wc -c < rprintmsg.c` if [ $len != 1599 ] ; then echo error: rprintmsg.c was $len bytes long, should have been 1599 fi cd .. echo done with directory msg echo x - sort echo creating directory sort mkdir sort cd sort echo x - Makefile cat > Makefile <<'Funky_Stuff' # # @(#)Makefile 1.4 87/11/30 3.9 RPCSRC # BIN = rsort sort_svc GEN = sort_clnt.c sort_svc.c sort_xdr.c sort.h LIB = -lrpclib RPCCOM = rpcgen all: $(BIN) rsort: rsort.o sort_clnt.o sort_xdr.o $(CC) $(LDFLAGS) -o $@ rsort.o sort_clnt.o sort_xdr.o $(LIB) rsort.o: rsort.c sort.h sort_clnt.c: $(RPCCOM) -l sort.x >$@ sort_svc: sort_proc.o sort_svc.o sort_xdr.o $(CC) $(LDFLAGS) -o $@ sort_proc.o sort_svc.o sort_xdr.o $(LIB) sort_proc.o: sort_proc.c sort.h sort_svc.c: $(RPCCOM) -s udp sort.x >$@ sort_xdr.c: $(RPCCOM) -c sort.x >$@ sort.h: $(RPCCOM) -h sort.x >$@ clean cleanup: rm -f $(GEN) *.o $(BIN) Funky_Stuff len=`wc -c < Makefile` if [ $len != 621 ] ; then echo error: Makefile was $len bytes long, should have been 621 fi echo x - rsort.c cat > rsort.c <<'Funky_Stuff' /* @(#)rsort.c 1.2 87/11/24 3.9 RPCSRC */ /* * rsort.c * Client side application which sorts argc, argv. */ #include <stdio.h> #include <rpc/rpc.h> #include "sort.h" main(argc, argv) int argc; char **argv; { char *machinename; struct sortstrings args, res; int i; if (argc < 3) { fprintf(stderr, "usage: %s machinename [s1 ...]\n", argv[0]); exit(1); } machinename = argv[1]; args.ss.ss_len = argc - 2; /* substract off progname, machinename */ args.ss.ss_val = &argv[2]; res.ss.ss_val = (char **)NULL; if ((i = callrpc(machinename, SORTPROG, SORTVERS, SORT, xdr_sortstrings, &args, xdr_sortstrings, &res))) { fprintf(stderr, "%s: call to sort service failed. ", argv[0]); clnt_perrno(i); fprintf(stderr, "\n"); exit(1); } for (i = 0; i < res.ss.ss_len; i++) { printf("%s\n", res.ss.ss_val[i]); } /* should free res here */ exit(0); } Funky_Stuff len=`wc -c < rsort.c` if [ $len != 897 ] ; then echo error: rsort.c was $len bytes long, should have been 897 fi echo x - sort.x cat > sort.x <<'Funky_Stuff' /* @(#)sort.x 1.1 87/11/04 3.9 RPCSRC */ /* * The sort procedure receives an array of strings and returns an array * of strings. This toy service handles a maximum of 64 strings. */ const MAXSORTSIZE = 64; const MAXSTRINGLEN = 64; typedef string str<MAXSTRINGLEN>; /* the string itself */ struct sortstrings { str ss<MAXSORTSIZE>; }; program SORTPROG { version SORTVERS { sortstrings SORT(sortstrings) = 1; } = 1; } = 22855; Funky_Stuff len=`wc -c < sort.x` if [ $len != 455 ] ; then echo error: sort.x was $len bytes long, should have been 455 fi echo x - sort_proc.c cat > sort_proc.c <<'Funky_Stuff' /* @(#)sort_proc.c 1.2 87/11/24 3.9 RPCSRC */ #include <rpc/rpc.h> #include "sort.h" static int comparestrings(sp1, sp2) char **sp1, **sp2; { return (strcmp(*sp1, *sp2)); } struct sortstrings * sort_1(ssp) struct sortstrings *ssp; { static struct sortstrings ss_res; if (ss_res.ss.ss_val != (str *)NULL) free(ss_res.ss.ss_val); qsort(ssp->ss.ss_val, ssp->ss.ss_len, sizeof (char *), comparestrings); ss_res.ss.ss_len = ssp->ss.ss_len; ss_res.ss.ss_val = (str *)malloc(ssp->ss.ss_len * sizeof(str *)); bcopy(ssp->ss.ss_val, ss_res.ss.ss_val, ssp->ss.ss_len * sizeof(str *)); return(&ss_res); } Funky_Stuff len=`wc -c < sort_proc.c` if [ $len != 653 ] ; then echo error: sort_proc.c was $len bytes long, should have been 653 fi cd .. echo done with directory sort cd .. echo done with directory demo exit -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.