: To unbundle, sh this file
echo asyncRls.c 1>&2
cat >asyncRls.c <<'End of asyncRls.c'
/*
 * asyncRls.c: remote directory listing client
 */
#include <stdio.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include "asyncRls.h"

#define SERVERFREE(argc, argv, h_name, k) for (k=1; k<argc; k+=2) \
	if (!strcmp(h_name, argv[k])) { k=(k-1)/2; break;} \
	if (k>=argc) {  \
	fprintf(stderr, "%s: where did you come from!\n", h_name); \
	exit(-1); }

char           *locald();
extern bool_t   xdr_dir();
char           *host[DIR_SIZE];

main(argc, argv)		/* Usage rls host dir host dir ... host dir */
  char          **argv;
  int             argc;
{
  extern int      errno;
  enum clnt_stat  clnt_stat;
  fd_set          readfds;
  struct timeval  timeout;
  int             i, j, k;
  int             ts = getdtablesize();	/* file descripptor table size */
  int             ServerBusy = 0;	/* outstanding requests bitmask */

  /*
   * Register a local UDP server daemon to collect results. We use
   * registerrpc() as we can afford to hide the server handle SVCXPRT *.
   * Data sent to this collection service includes the name of the
   * server asynchronously returning the results.  There are more
   * robust ways to retrieve the name of the calling party developed
   * elsewhere.  Note we really have no dispatcher here - just one
   * single procedure - locald().
   */


  registerrpc(DIRDPROG, DIRDVERS, LOCALD, locald, xdr_dir, xdr_void);

  /*
   * Clear the timeval to affect a poll when using select
   */
  timeout.tv_sec = timeout.tv_usec = 0;

  /* repeatedly call an async request to a server if registered */
  for (j = 0, i = 1; 1; i = (i + 2) % (argc - 1), j = (i - 1) >> 1) {
    /*
     * Send repeated requests to the servers specified.  BE CAREFUL here as
     * attempting to send a request to a machine that is blocked trying to
     * return results to locald will in turn block us!  There is another way
     * to fix this (use clnt_create timeouts), but that requires checking
     * with the client to see if it can answer (time consuming).  By creating
     * all the required client handles ahead of time, repeating the process
     * with callrpc() could be eliminated. Instead we keep-track locally.
     * Another alternative could have been async.  server child-processes.
     */
    if (!(ServerBusy & (0x1 << j))) {	/* server is free */
      clnt_stat = callrpc(argv[i], DIRPROG, DIRVERS, READDIR,
			  xdr_dir, argv[i + 1], xdr_void, 0);
      if (clnt_stat != 0)
	clnt_perrno(clnt_stat);
      else
	ServerBusy |= (0x1 << j);
    }
    /*
     * Look for a response from the other socket
     */
    readfds = svc_fdset;
    switch (select(ts, &readfds, (int *) 0, (int *) 0, &timeout)) {
    case -1:
      if (errno == EINTR)
	continue;
      perror("svc_run: - select failed");
      break;			/* leave beat server marked as busy */
    case 0:
      break;
    default:
      svc_getreqset(&readfds);
      /*
       * You never know who came back and in what order, you'll need some
       * kind of monitorig mechanism like this
       */
      SERVERFREE(argc, argv, host, k);
      printf("server %s, #%d returned!\n", host, k);
      ServerBusy ^= (0x1 << k);
    }
  }
}

char           *
locald(dir)			/* invoked if there is something at my
				 * socket... */
  char           *dir;		/* char dir[DIR_SIZE] */
{
  /*
   * As part of the protocol, we asked the server to send back it's host name
   * to expedite the monitoring process. It's rather involved to determine
   * autonomously from this side who it is.
   */
  sscanf(dir, "%s", host);

  /* spew-out the results and bail out of here! */
  printf("%s\n", dir);

  /* must send something back, so here's a dummy reply */
  return;
}
End of asyncRls.c
echo asyncRls.h 1>&2
cat >asyncRls.h <<'End of asyncRls.h'
#define DIR_SIZE 8192
#define DIRPROG ((u_long) 0x20000002)	/* server program (suite) number */
#define DIRVERS ((u_long) 1)	/* program version number */
#define READDIR ((u_long) 1)	/* procedure number for look-up */


#define DIRDPROG ((u_long) 0x20000003)	/* local daemon to collect returns */
#define DIRDVERS ((u_long) 1)
#define LOCALD   ((u_long) 1)

/*#define CHLDDIRPROC ((u_long) 0x20000004)	/* Child daemon */
End of asyncRls.h
echo asyncRls_svc.c 1>&2
cat >asyncRls_svc.c <<'End of asyncRls_svc.c'
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include "asyncRls.h"

static void     dispatch();	/* the server program - does dispatching */
extern bool_t   xdr_dir();
struct timeval  tval;

/*
 * Register the service, then wait for requests without consuming resources.
 */
main()
{
  SVCXPRT        *transp;

  (void) pmap_unset(DIRPROG, DIRVERS);

  transp = svcudp_create(RPC_ANYSOCK);
  if (transp == NULL) {
    (void) fprintf(stderr, "cannot create udp service.\n");
    exit(1);
  }
  if (!svc_register(transp, DIRPROG, DIRVERS, dispatch, IPPROTO_UDP)) {
    (void) fprintf(stderr,
		   "unable to register (DIRPROG, DIRVERS, udp).\n");
    exit(1);
  }
  /*
   * If we wished to simultaneously register the service w/ TCP transport,
   * we'd do the following, but since we use callrpc() which uses UDP, we
   * won't bother.
   */
  /*
   * transp = svctcp_create(RPC_ANYSOCK, 0, 0); if (transp == NULL) {
   * (void)fprintf(stderr, "cannot create tcp service.\n"); exit(1); } if
   * (!svc_register(transp, DIRPROG, DIRVERS, dispatch, IPPROTO_TCP)) {
   * (void)fprintf(stderr, "unable to register (DIRPROG, DIRVERS, tcp).\n");
   * exit(1); }
   */
  /*
   * Set the time-out limit to 0 - makes the servers thrash a little while
   * attempting toi return results, but after repeated retries, they get
   * there.  tval=0 also reduces the chance that the callrpc() will get stuck
   * when I ^C the client, reducing the need to restart the servers.
   */
  tval.tv_sec = tval.tv_usec = 0;

  svc_run();
  (void) fprintf(stderr, "svc_run returned\n");
  exit(1);
}

/*
 * Decode the requested service and provide it.
 */
static void
dispatch(rqstp, transp)
  struct svc_req *rqstp;
  SVCXPRT        *transp;
{
  char            dir[DIR_SIZE];
  char            dhost[DIR_SIZE];
  struct hostent *host;
  struct sockaddr_in *sock_in;
  enum clnt_stat  clnt_stat;
  CLIENT         *client;
  int             sock = RPC_ANYSOCK;

  switch (rqstp->rq_proc) {
  case NULLPROC:
    (void) svc_sendreply(transp, xdr_void, 0);
    return;

  case READDIR:
    if (!svc_getargs(transp, xdr_dir, dir)) {
      svcerr_decode(transp);
      return;
    }
    if (!svc_sendreply(transp, xdr_void, 0)) {
      svcerr_systemerr(transp);
      return;
    }
    read_dir(dir);
    /*
     * Pre-pend the host name to ease client-side tracking
     */
    (void) gethostname(dhost, DIR_SIZE);
    strcat(dhost, "\n");
    strcat(dhost, dir);

    /*
     * Return the result with a call to the requestor's local daemon - but
     * where does it live? Translate this into caller host info to call
     * client back
     */
    sock_in = svc_getcaller(transp);	/* get caller socket info */
    sock_in->sin_port = 0;	/* makes clntudp_create consult yp */
    client = clntudp_create(sock_in, DIRDPROG, DIRDVERS,
			    tval, &sock);
    clnt_stat = clnt_call(client, LOCALD, xdr_dir, dhost,
			  xdr_void, 0, tval);
    if (clnt_stat != 0)
      clnt_perrno(clnt_stat);

    /*
     * We'll reuse dhost, but should call this to free the XDR struct, plus
     * clean things up
     */
    svc_freeargs(transp, xdr_dir, dhost);
    clnt_destroy(client);
    return;

    /*
     * put more procedures here...  case A: case Z:
     */

  default:
    svcerr_noproc(transp);
    return;
  }
}
End of asyncRls_svc.c
echo makefile 1>&2
cat >makefile <<'End of makefile'
ALL = asyncRls asyncRls_svc
CFLAGS = -I../minimum

all: $(ALL)

asyncRls: asyncRls.c rls_xdr.o 
	$(CC) -o asyncRls asyncRls.c rls_xdr.o
	
asyncRls_svc: asyncRls_svc.c rls_xdr.o  read_dir.o
	$(CC) -o asyncRls_svc asyncRls_svc.c rls_xdr.o read_dir.o

clean:
	$(RM) $(ALL) *.o
End of makefile
echo read_dir.c 1>&2
cat >read_dir.c <<'End of read_dir.c'
/* note - RPC compliant procedure calls take one input and
   return one output. Everything is passed by pointer.  Return
   values should point to static data, as it might have to 
   survive some while. */
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>     /* use <xpg2include/sys/dirent.h> (SunOS4.1) or
        <sys/dirent.h> for X/Open Portability Guide, issue 2 conformance */
#include "rls.h"

read_dir(dir)
char    *dir;   /* char dir[DIR_SIZE] */
{
        DIR * dirp;
        struct direct *d;
		  printf("beginning ");

        /* open directory */
        dirp = opendir(dir);
        if (dirp == NULL)
                return(NULL);

        /* stuff filenames into dir buffer */
        dir[0] = NULL;
        while (d = readdir(dirp))
                sprintf(dir, "%s%s\n", dir, d->d_name);

        /* return the result */
		  printf("returning ");
        closedir(dirp);
        return((int)dir);  /* this is the only new line from Example 4-3 */
}
End of read_dir.c
echo rls_xdr.c 1>&2
cat >rls_xdr.c <<'End of rls_xdr.c'
#include <rpc/rpc.h>
#include "rls.h"
bool_t xdr_dir(xdrs, objp)
XDR *xdrs;
char *objp;
{ return ( xdr_string(xdrs, &objp, DIR_SIZE) ); }
End of rls_xdr.c
