clnt_thrd.c

00001 /* @(#)clnt_tcp.c       2.2 88/08/01 4.0 RPCSRC */
00002 /*
00003  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
00004  * unrestricted use provided that this legend is included on all tape
00005  * media and as a part of the software program in whole or part.  Users
00006  * may copy or modify Sun RPC without charge, but are not authorized
00007  * to license or distribute it to anyone else except as part of a product or
00008  * program developed by the user.
00009  *
00010  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
00011  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
00012  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
00013  *
00014  * Sun RPC is provided with no support and without any obligation on the
00015  * part of Sun Microsystems, Inc. to assist in its use, correction,
00016  * modification or enhancement.
00017  *
00018  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
00019  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
00020  * OR ANY PART THEREOF.
00021  *
00022  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
00023  * or profits or other special, indirect and consequential damages, even if
00024  * Sun has been advised of the possibility of such damages.
00025  *
00026  * Sun Microsystems, Inc.
00027  * 2550 Garcia Avenue
00028  * Mountain View, California  94043
00029  */
00030 #if !defined(lint) && defined(SCCSIDS)
00031 static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
00032 #endif
00033 
00034 /*
00035  * clnt_thrd.c, Implements a Threaded, TCP/IP based, client side RPC.
00036  *
00037  * Copyright (C) 1984, Sun Microsystems, Inc.
00038  *
00039  * TCP based RPC supports 'batched calls'.
00040  * A sequence of calls may be batched-up in a send buffer.  The rpc call
00041  * return immediately to the client even though the call was not necessarily
00042  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
00043  * the rpc timeout value is zero (see clnt.h, rpc).
00044  *
00045  * Clients should NOT casually batch calls that in fact return results; that is,
00046  * the server side should be aware that a call is batched and not produce any
00047  * return message.  Batched calls that produce many result messages can
00048  * deadlock (netlock) the client and the server....
00049  *
00050  * Now go hang yourself.
00051  */
00052 
00053 #include <netdb.h>
00054 #include <errno.h>
00055 #include <stdio.h>
00056 #include <unistd.h>
00057 #include <rpc/rpc.h>
00058 #include <sys/poll.h>
00059 #include <sys/socket.h>
00060 #include <rpc/pmap_clnt.h>
00061 
00062 #include "xprt_thrd.h"
00063 #include "clnt_thrd.h"
00064 
00065 #define MCALL_MSG_SIZE 24
00066 
00067 struct ct_data
00068   {
00069     bool_t ct_waitset;          /* wait set by clnt_control? */
00070     struct timeval ct_wait;
00071     struct rpc_err ct_error;
00072     char ct_mcall[MCALL_MSG_SIZE];      /* marshalled callmsg header */
00073     u_int ct_mpos;              /* length of marshalled header */
00074     u_long ct_prog;             /* Program number */
00075     u_long ct_vers;             /* Version Number */
00076     TXPRT_WAIT *ct_repl;        /* A reply wait object */
00077     TXPRT *ct_xprt;             /* Transport Object */
00078   };
00079 
00080 static enum clnt_stat clntthrd_call (CLIENT *, u_long, xdrproc_t, caddr_t,
00081                                     xdrproc_t, caddr_t, struct timeval);
00082 static void clntthrd_abort (void);
00083 static void clntthrd_geterr (CLIENT *, struct rpc_err *);
00084 static bool_t clntthrd_freeres (CLIENT *, xdrproc_t, caddr_t);
00085 static bool_t clntthrd_control (CLIENT *, int, char *);
00086 static void clntthrd_destroy (CLIENT *);
00087 
00088 static struct clnt_ops thrd_ops =
00089 {
00090   clntthrd_call,
00091   clntthrd_abort,
00092   clntthrd_geterr,
00093   clntthrd_freeres,
00094   clntthrd_destroy,
00095   clntthrd_control
00096 };
00097 
00098 /*
00099  * Create a client handle for a threaded tcp/ip connection.
00100  * transport is the Threaded Transport we wish to attach this client to.
00101  * NB: It is the client's responsibility to open and close the transport
00102  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set
00103  * this to something more useful.
00104  */
00105 CLIENT *
00106 clntthrd_create (TXPRT *transport, u_long prog, u_long vers)
00107 {
00108   CLIENT *h = NULL;
00109   struct ct_data *ct = NULL;
00110   struct rpc_msg call_msg;
00111   struct timeval now;
00112   XDR xdr;
00113 
00114   if (transport == NULL)
00115     return (CLIENT *)NULL;
00116 
00117   h = (CLIENT *) malloc (sizeof (*h));
00118   if (h == NULL)
00119     {
00120       (void) fprintf (stderr, "clntthrd_create: out of memory\n");
00121       goto fooy;
00122     }
00123   ct = (struct ct_data *) malloc (sizeof (*ct));
00124   if (ct == NULL)
00125     {
00126       (void) fprintf (stderr, "clntthrd_create: out of memory\n");
00127       goto fooy;
00128     }
00129 
00130   memset (ct, 0, sizeof (*ct));
00131   memset (h, 0, sizeof (*h));
00132 
00133   /*
00134    * Set up private data structure
00135    */
00136   ct->ct_repl = xprt_thrd_new_wait ();
00137   if (ct->ct_repl == NULL)
00138     {
00139       (void) fprintf (stderr, "clntthrd_create: out of memory\n");
00140       goto fooy;
00141     }
00142 
00143   ct->ct_wait.tv_usec = 0;
00144   ct->ct_waitset = FALSE;
00145   ct->ct_xprt = transport;
00146 
00147   /*
00148    * Initialize call message
00149    */
00150   gettimeofday (&now, NULL);
00151   srand48 (now.tv_sec ^ now.tv_usec);
00152   call_msg.rm_xid = lrand48();
00153   call_msg.rm_direction = CALL;
00154   call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
00155   ct->ct_prog = call_msg.rm_call.cb_prog = prog;
00156   ct->ct_vers = call_msg.rm_call.cb_vers = vers;
00157 
00158   /*
00159    * pre-serialize the static part of the call msg and stash it away
00160    */
00161   xdrmem_create (&xdr, ct->ct_mcall, MCALL_MSG_SIZE, XDR_ENCODE);
00162   if (!xdr_callhdr (&xdr, &call_msg))
00163     {
00164       goto fooy;
00165     }
00166   ct->ct_mpos = XDR_GETPOS (&xdr);
00167   XDR_DESTROY (&xdr);
00168 
00169   /*
00170    * Build the CLIENT structure
00171    */
00172   h->cl_auth = authnone_create ();
00173   h->cl_ops = &thrd_ops;
00174   h->cl_private = (caddr_t) ct;
00175   return h;
00176 
00177 fooy:
00178   /*
00179    * Something goofed, free stuff and barf
00180    */
00181   if (ct != NULL) {
00182     if (ct->ct_repl != NULL)
00183       xprt_destroy_wait (ct->ct_repl);
00184     free ((caddr_t) ct);
00185   }
00186   if (h != NULL) free ((caddr_t) h);
00187   return ((CLIENT *) NULL);
00188 }
00189 
00190 static enum clnt_stat
00191 clntthrd_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
00192      CLIENT *h;
00193      u_long proc;
00194      xdrproc_t xdr_args;
00195      caddr_t args_ptr;
00196      xdrproc_t xdr_results;
00197      caddr_t results_ptr;
00198      struct timeval timeout;
00199 {
00200   struct ct_data *ct = (struct ct_data *) h->cl_private;
00201   XDR *xdrs;
00202   struct rpc_msg reply_msg;
00203   u_long x_id;
00204   u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall);   /* yuk */
00205   bool_t shipnow;
00206   int refreshes = 2;
00207 
00208   if (!ct->ct_waitset)
00209     {
00210       ct->ct_wait = timeout;
00211     }
00212 
00213   shipnow =
00214     (xdr_results == (xdrproc_t) 0 && timeout.tv_sec == 0
00215      && timeout.tv_usec == 0) ? FALSE : TRUE;
00216 
00217   /* Obtain and lock XDR object */
00218   xdrs = TXPRT_GET_XDR (ct->ct_xprt);
00219 
00220 call_again:
00221   xdrs->x_op = XDR_ENCODE;
00222   ct->ct_error.re_status = RPC_SUCCESS;
00223   x_id = ntohl (--(*msg_x_id));
00224   if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
00225       (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
00226       (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
00227       (!(*xdr_args) (xdrs, args_ptr)))
00228     {
00229       if (ct->ct_error.re_status == RPC_SUCCESS)
00230         ct->ct_error.re_status = RPC_CANTENCODEARGS;
00231       (void) xdrrec_endofrecord (xdrs, TRUE);
00232       TXPRT_REL_XDR (ct->ct_xprt);
00233       return (ct->ct_error.re_status);
00234     }
00235   if (!xdrrec_endofrecord (xdrs, shipnow)) {
00236     TXPRT_REL_XDR (ct->ct_xprt);
00237     return ct->ct_error.re_status = RPC_CANTSEND;
00238   }
00239   if (!shipnow) {
00240     TXPRT_REL_XDR (ct->ct_xprt);
00241     return RPC_SUCCESS;
00242   }
00243   /*
00244    * Hack to provide rpc-based message passing
00245    */
00246   if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
00247     {
00248       TXPRT_REL_XDR (ct->ct_xprt);
00249       return ct->ct_error.re_status = RPC_TIMEDOUT;
00250     }
00251 
00252   /* Setup reply-state for Transport callback */
00253   ct->ct_repl->tw_x_id = x_id;
00254   reply_msg.acpted_rply.ar_verf = _null_auth;
00255   reply_msg.acpted_rply.ar_results.where = NULL;
00256   reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
00257 
00258   /* Now register the callback and wait for our reply message */
00259   TXPRT_WAIT_REPLY (ct->ct_xprt, &reply_msg, &xdrs, ct->ct_repl, timeout);
00260 
00261   /*
00262    * Ok, we're back.  That means that our reply has come in.  We expect
00263    * that reply_msg and xdrs have been filled in for us, and the XDR
00264    * Object is locked.  That means we don't have to look for the response
00265    * on our own, nor lock XDR on our own, so....
00266    */
00267 
00268   /*
00269    * process header
00270    */
00271   _seterr_reply (&reply_msg, &(ct->ct_error));
00272   if (ct->ct_error.re_status == RPC_SUCCESS)
00273     {
00274       if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
00275         {
00276           ct->ct_error.re_status = RPC_AUTHERROR;
00277           ct->ct_error.re_why = AUTH_INVALIDRESP;
00278         }
00279       else if (!(*xdr_results) (xdrs, results_ptr))
00280         {
00281           if (ct->ct_error.re_status == RPC_SUCCESS)
00282             ct->ct_error.re_status = RPC_CANTDECODERES;
00283         }
00284       /* free verifier ... */
00285       if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
00286         {
00287           xdrs->x_op = XDR_FREE;
00288           (void) xdr_opaque_auth (xdrs, &(reply_msg.acpted_rply.ar_verf));
00289         }
00290     }                           /* end successful completion */
00291   else
00292     {
00293       /* maybe our credentials need to be refreshed ... */
00294       if (refreshes-- && AUTH_REFRESH (h->cl_auth))
00295         /* We still have the XDR lock */
00296         goto call_again;
00297     }                           /* end of unsuccessful completion */
00298   TXPRT_REL_XDR (ct->ct_xprt);
00299   return ct->ct_error.re_status;
00300 }
00301 
00302 static void
00303 clntthrd_geterr (h, errp)
00304      CLIENT *h;
00305      struct rpc_err *errp;
00306 {
00307   struct ct_data *ct =
00308   (struct ct_data *) h->cl_private;
00309 
00310   *errp = ct->ct_error;
00311 }
00312 
00313 static bool_t
00314 clntthrd_freeres (cl, xdr_res, res_ptr)
00315      CLIENT *cl;
00316      xdrproc_t xdr_res;
00317      caddr_t res_ptr;
00318 {
00319   bool_t res;
00320   struct ct_data *ct = (struct ct_data *) cl->cl_private;
00321   XDR *xdrs = TXPRT_GET_XDR (ct->ct_xprt);
00322   xdrs->x_op = XDR_FREE;
00323   res = (*xdr_res) (xdrs, res_ptr);
00324   TXPRT_REL_XDR (ct->ct_xprt);
00325   return res;
00326 }
00327 
00328 static void
00329 clntthrd_abort ()
00330 {
00331 }
00332 
00333 static bool_t
00334 clntthrd_control (CLIENT *cl, int request, char *info)
00335 {
00336   struct ct_data *ct = (struct ct_data *) cl->cl_private;
00337 
00338 
00339   switch (request)
00340     {
00341     case CLSET_TIMEOUT:
00342       ct->ct_wait = *(struct timeval *) info;
00343       ct->ct_waitset = TRUE;
00344       break;
00345     case CLGET_TIMEOUT:
00346       *(struct timeval *) info = ct->ct_wait;
00347       break;
00348     case CLGET_XID:
00349       /*
00350        * use the knowledge that xid is the
00351        * first element in the call structure *.
00352        * This will get the xid of the PREVIOUS call
00353        */
00354       *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall);
00355       break;
00356     case CLSET_XID:
00357       /* This will set the xid of the NEXT call */
00358       *(u_long *)ct->ct_mcall =  htonl (*(u_long *)info - 1);
00359       /* decrement by 1 as clntthrd_call() increments once */
00360     case CLGET_VERS:
00361       /*
00362        * This RELIES on the information that, in the call body,
00363        * the version number field is the fifth field from the
00364        * begining of the RPC header. MUST be changed if the
00365        * call_struct is changed
00366        */
00367       *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall +
00368                                            4 * BYTES_PER_XDR_UNIT));
00369       break;
00370     case CLSET_VERS:
00371       *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
00372         = htonl (*(u_long *)info);
00373       break;
00374     case CLGET_PROG:
00375       /*
00376        * This RELIES on the information that, in the call body,
00377        * the program number field is the  field from the
00378        * begining of the RPC header. MUST be changed if the
00379        * call_struct is changed
00380        */
00381       *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
00382                                           3 * BYTES_PER_XDR_UNIT));
00383       break;
00384     case CLSET_PROG:
00385       *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
00386         = htonl(*(u_long *)info);
00387       break;
00388 
00389     /* We don't support these, yet */
00390     case CLSET_FD_CLOSE:
00391     case CLSET_FD_NCLOSE:
00392     case CLGET_SERVER_ADDR:
00393     case CLGET_FD:
00394       return FALSE;
00395 
00396     /* The following are only possible with TI-RPC */
00397     case CLGET_RETRY_TIMEOUT:
00398     case CLSET_RETRY_TIMEOUT:
00399     case CLGET_SVC_ADDR:
00400     case CLSET_SVC_ADDR:
00401     case CLSET_PUSH_TIMOD:
00402     case CLSET_POP_TIMOD:
00403     default:
00404       return FALSE;
00405     }
00406   return TRUE;
00407 }
00408 
00409 
00410 static void
00411 clntthrd_destroy (CLIENT *h)
00412 {
00413   struct ct_data *ct = (struct ct_data *) h->cl_private;
00414 
00415   xprt_destroy_wait (ct->ct_repl);
00416   free ((caddr_t) ct);
00417   free ((caddr_t) h);
00418 }

Generated on Sun Sep 4 18:07:34 2005 for GnuCash by  doxygen 1.4.3-20050530