• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <rpc/rpc.h>
2 #include <arpa/inet.h>
3 #include <rpc/rpc_router_ioctl.h>
4 #include <debug.h>
5 #include <pthread.h>
6 #include <sys/select.h>
7 
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <errno.h>
12 
13 struct CLIENT {
14     xdr_s_type *xdr;
15     struct CLIENT *next;
16     /* common attribute struct for setting up recursive mutexes */
17     pthread_mutexattr_t lock_attr;
18 
19     /* We insist that there is only one outstanding call for a client at any
20        time, and we use this mutex to enforce the rule.  When we start
21        supporting multiple outstanding RPCs on a client, we will have to
22        maintain a queue of them, and match incoming replies (by the XID of the
23        incoming packet).  For now, we just block until we get that reply.
24     */
25     pthread_mutex_t lock;
26 
27     pthread_mutex_t wait_reply_lock;
28     pthread_cond_t wait_reply;
29 
30     pthread_mutex_t input_xdr_lock;
31     pthread_cond_t input_xdr_wait;
32     volatile int input_xdr_busy;
33 
34     pthread_mutex_t wait_cb_lock;
35     pthread_cond_t wait_cb;
36     pthread_t cb_thread;
37     volatile int got_cb;
38     volatile int cb_stop;
39 };
40 
41 extern void* svc_find(void *xprt, rpcprog_t prog, rpcvers_t vers);
42 extern void svc_dispatch(void *svc, void *xprt);
43 extern int  r_open();
44 extern void r_close();
45 extern xdr_s_type *xdr_init_common(const char *name, int is_client);
46 extern xdr_s_type *xdr_clone(xdr_s_type *);
47 extern void xdr_destroy_common(xdr_s_type *xdr);
48 extern bool_t xdr_recv_reply_header (xdr_s_type *xdr, rpc_reply_header *reply);
49 extern void *the_xprt;
50 
51 
52 static pthread_mutex_t rx_mutex = PTHREAD_MUTEX_INITIALIZER;
53 static pthread_t rx_thread;
54 static volatile unsigned int num_clients;
55 static volatile fd_set rx_fdset;
56 static volatile int max_rxfd;
57 static volatile struct CLIENT *clients;
58 
59 /* There's one of these for each RPC client which has received an RPC call. */
cb_context(void * __u)60 static void *cb_context(void *__u)
61 {
62     CLIENT *client = (CLIENT *)__u;
63     D("RPC-callback thread for %08x:%08x starting.\n",
64       (client->xdr->x_prog | 0x01000000),
65       client->xdr->x_vers);
66     pthread_mutex_lock(&client->wait_cb_lock);
67     while (client->cb_stop == 0) {
68         if (!client->got_cb)
69             pthread_cond_wait(&client->wait_cb,
70                               &client->wait_cb_lock);
71         /* We tell the thread it's time to exit by setting cb_stop to nonzero
72            and signalling the conditional variable.  When there's no data, we
73            skip to the top of the loop and exit.
74         */
75         if (!client->got_cb) {
76             D("RPC-callback thread for %08x:%08x: signalled but no data.\n",
77               (client->xdr->x_prog | 0x01000000),
78               client->xdr->x_vers);
79             continue;
80         }
81         client->got_cb = 0;
82 
83         /* We dispatch the message to the server representing the callback
84          * client.
85          */
86         if (the_xprt) {
87             void *svc;
88             rpcprog_t prog =
89                 ntohl(((uint32 *)(client->xdr->in_msg))[RPC_OFFSET+3]);
90             rpcvers_t vers =
91                 ntohl(((uint32 *)(client->xdr->in_msg))[RPC_OFFSET+4]);
92 
93             svc = svc_find(the_xprt, prog, vers);
94             if (svc) {
95                 XDR **svc_xdr = (XDR **)svc;
96                 D("%08x:%08x dispatching RPC call (XID %d, xdr %p) for "
97                   "callback client %08x:%08x.\n",
98                   client->xdr->x_prog,
99                   client->xdr->x_vers,
100                   ntohl(((uint32 *)(client->xdr->in_msg))[RPC_OFFSET]),
101                   client->xdr,
102                   (uint32_t)prog, (int)vers);
103                 /* We transplant the xdr of the client into the entry
104                    representing the callback client in the list of servers.
105                    Note that since we hold the wait_cb_lock for this client,
106                    if another call for this callback client arrives before
107                    we've finished processing this call, that will block until
108                    we're done with this one.  If this happens, it would be
109                    most likely a bug in the arm9 rpc router.
110                 */
111                 if (*svc_xdr) {
112                     D("%08x:%08x expecting XDR == NULL"
113                       "callback client %08x:%08x!\n",
114                       client->xdr->x_prog,
115                       client->xdr->x_vers,
116                       (uint32_t)prog, (int)vers);
117                     xdr_destroy_common(*svc_xdr);
118                 }
119 
120                 D("%08x:%08x cloning XDR for "
121                   "callback client %08x:%08x.\n",
122                   client->xdr->x_prog,
123                   client->xdr->x_vers,
124                   (uint32_t)prog, (int)vers);
125                 *svc_xdr = xdr_clone(client->xdr);
126 
127                 (*svc_xdr)->x_prog = prog;
128                 (*svc_xdr)->x_vers = vers;
129                 memcpy((*svc_xdr)->in_msg,
130                        client->xdr->in_msg, client->xdr->in_len);
131                 memcpy((*svc_xdr)->out_msg,
132                        client->xdr->out_msg, client->xdr->out_next);
133                 (*svc_xdr)->in_len = client->xdr->in_len;
134                 (*svc_xdr)->out_next = client->xdr->out_next;
135 
136                 pthread_mutex_lock(&client->input_xdr_lock);
137                 D("%08x:%08x marking input buffer as free.\n",
138                   client->xdr->x_prog, client->xdr->x_vers);
139                 client->input_xdr_busy = 0;
140                 pthread_cond_signal(&client->input_xdr_wait);
141                 pthread_mutex_unlock(&client->input_xdr_lock);
142 
143                 svc_dispatch(svc, the_xprt);
144                 xdr_destroy_common(*svc_xdr);
145                 *svc_xdr = NULL;
146             }
147             else E("%08x:%08x call packet arrived, but there's no "
148                    "RPC server registered for %08x:%08x.\n",
149                    client->xdr->x_prog,
150                    client->xdr->x_vers,
151                    (uint32_t)prog, (int)vers);
152         }
153         else E("%08x:%08x call packet arrived, but there's "
154                "no RPC transport!\n",
155                client->xdr->x_prog,
156                client->xdr->x_vers);
157     }
158     pthread_mutex_unlock(&client->wait_cb_lock);
159 
160 
161     D("RPC-callback thread for %08x:%08x terminating.\n",
162       (client->xdr->x_prog | 0x01000000),
163       client->xdr->x_vers);
164     return NULL;
165 }
166 
rx_context(void * __u)167 static void *rx_context(void *__u __attribute__((unused)))
168 {
169     int n;
170     struct timeval tv;
171     fd_set rfds;
172     while(num_clients) {
173         pthread_mutex_lock(&rx_mutex);
174         rfds = rx_fdset;
175         pthread_mutex_unlock(&rx_mutex);
176         tv.tv_sec = 0; tv.tv_usec = 500 * 1000;
177         n = select(max_rxfd + 1, (fd_set *)&rfds, NULL, NULL, &tv);
178         if (n < 0) {
179             E("select() error %s (%d)\n", strerror(errno), errno);
180             continue;
181         }
182 
183         if (n) {
184             pthread_mutex_lock(&rx_mutex); /* sync access to the client list */
185             CLIENT *client = (CLIENT *)clients;
186             for (; client; client = client->next) {
187                 if (FD_ISSET(client->xdr->fd, &rfds)) {
188 
189                     /* We need to make sure that the XDR's in_buf is not in
190                        use before we read into it.  The in_buf may be in use
191                        in a race between processing an incoming call and
192                        receiving a reply to an outstanding call, or processing
193                        an incoming reply and receiving a call.
194                     */
195 
196                     pthread_mutex_lock(&client->input_xdr_lock);
197                     while (client->input_xdr_busy) {
198                         D("%08x:%08x waiting for XDR input buffer "
199                           "to be consumed.\n",
200                           client->xdr->x_prog, client->xdr->x_vers);
201                         pthread_cond_wait(
202                             &client->input_xdr_wait,
203                             &client->input_xdr_lock);
204                     }
205                     D("%08x:%08x reading data.\n",
206                       client->xdr->x_prog, client->xdr->x_vers);
207                     client->xdr->xops->read(client->xdr);
208                     client->input_xdr_busy = 1;
209                     pthread_mutex_unlock(&client->input_xdr_lock);
210 
211                     if (((uint32 *)(client->xdr->in_msg))[RPC_OFFSET+1] ==
212                         htonl(RPC_MSG_REPLY)) {
213                         /* Wake up the RPC client to receive its data. */
214                         D("%08x:%08x received REPLY (XID %d), "
215                           "grabbing mutex to wake up client.\n",
216                           client->xdr->x_prog,
217                           client->xdr->x_vers,
218                           ntohl(((uint32 *)client->xdr->in_msg)[RPC_OFFSET]));
219                         pthread_mutex_lock(&client->wait_reply_lock);
220                         D("%08x:%08x got mutex, waking up client.\n",
221                           client->xdr->x_prog,
222                           client->xdr->x_vers);
223                         pthread_cond_signal(&client->wait_reply);
224                         pthread_mutex_unlock(&client->wait_reply_lock);
225                     }
226                     else {
227                         pthread_mutex_lock(&client->wait_cb_lock);
228                         D("%08x:%08x received CALL.\n",
229                           client->xdr->x_prog,
230                           client->xdr->x_vers);
231                         client->got_cb = 1;
232                         if (client->cb_stop < 0) {
233                             D("%08x:%08x starting callback thread.\n",
234                               client->xdr->x_prog,
235                               client->xdr->x_vers);
236                             client->cb_stop = 0;
237                             pthread_create(&client->cb_thread,
238                                            NULL,
239                                            cb_context, client);
240                         }
241                         D("%08x:%08x waking up callback thread.\n",
242                           client->xdr->x_prog,
243                           client->xdr->x_vers);
244                         pthread_cond_signal(&client->wait_cb);
245                         pthread_mutex_unlock(&client->wait_cb_lock);
246                     }
247                 }
248             }
249             pthread_mutex_unlock(&rx_mutex);
250         }
251         else {
252             V("rx thread timeout (%d clients):\n", num_clients);
253 #if 0
254             {
255                 CLIENT *trav = (CLIENT *)clients;
256                 for(; trav; trav = trav->next) {
257                     if (trav->xdr)
258                         V("\t%08x:%08x fd %02d\n",
259                           trav->xdr->x_prog,
260                           trav->xdr->x_vers,
261                           trav->xdr->fd);
262                     else V("\t(unknown)\n");
263                 }
264             }
265 #endif
266         }
267     }
268     D("RPC-client RX thread exiting!\n");
269     return NULL;
270 }
271 
272 enum clnt_stat
clnt_call(CLIENT * client,u_long proc,xdrproc_t xdr_args,caddr_t args_ptr,xdrproc_t xdr_results,caddr_t rets_ptr,struct timeval timeout)273 clnt_call(
274     CLIENT       * client,
275     u_long         proc,
276     xdrproc_t      xdr_args,
277     caddr_t        args_ptr,
278     xdrproc_t      xdr_results,
279     caddr_t        rets_ptr,
280     struct timeval timeout)
281 {
282     opaque_auth cred;
283     opaque_auth verf;
284     rpc_reply_header reply_header;
285     enum clnt_stat ret = RPC_SUCCESS;
286 
287     xdr_s_type *xdr = client->xdr;
288 
289     pthread_mutex_lock(&client->lock);
290 
291 
292     cred.oa_flavor = AUTH_NONE;
293     cred.oa_length = 0;
294     verf.oa_flavor = AUTH_NONE;
295     verf.oa_length = 0;
296 
297     xdr->x_op = XDR_ENCODE;
298 
299     /* Send message header */
300 
301     if (!xdr_call_msg_start (xdr, xdr->x_prog, xdr->x_vers,
302                              proc, &cred, &verf)) {
303         XDR_MSG_ABORT (xdr);
304         ret = RPC_CANTENCODEARGS;
305         E("%08x:%08x error in xdr_call_msg_start()\n",
306           client->xdr->x_prog,
307           client->xdr->x_vers);
308         goto out;
309     }
310 
311     /* Send arguments */
312 
313     if (!xdr_args (xdr, args_ptr)) {
314         XDR_MSG_ABORT(xdr);
315         ret = RPC_CANTENCODEARGS;
316         E("%08x:%08x error in xdr_args()\n",
317           client->xdr->x_prog,
318           client->xdr->x_vers);
319         goto out;
320     }
321 
322     /* Finish message - blocking */
323     pthread_mutex_lock(&client->wait_reply_lock);
324     D("%08x:%08x sending call (XID %d).\n",
325       client->xdr->x_prog, client->xdr->x_vers, client->xdr->xid);
326     if (!XDR_MSG_SEND(xdr)) {
327         ret = RPC_CANTSEND;
328         E("error in XDR_MSG_SEND\n");
329         goto out_unlock;
330     }
331 
332     D("%08x:%08x waiting for reply.\n",
333       client->xdr->x_prog, client->xdr->x_vers);
334     pthread_cond_wait(&client->wait_reply, &client->wait_reply_lock);
335     D("%08x:%08x received reply.\n", client->xdr->x_prog, client->xdr->x_vers);
336 
337     if (((uint32 *)xdr->out_msg)[RPC_OFFSET] !=
338         ((uint32 *)xdr->in_msg)[RPC_OFFSET]) {
339         E("%08x:%08x XID mismatch: got %d, expecting %d.\n",
340           client->xdr->x_prog, client->xdr->x_vers,
341           ntohl(((uint32 *)xdr->in_msg)[RPC_OFFSET]),
342           ntohl(((uint32 *)xdr->out_msg)[RPC_OFFSET]));
343         ret = RPC_CANTRECV;
344         goto out_unlock;
345     }
346 
347     D("%08x:%08x decoding reply header.\n",
348       client->xdr->x_prog, client->xdr->x_vers);
349     if (!xdr_recv_reply_header (client->xdr, &reply_header)) {
350         E("%08x:%08x error reading reply header.\n",
351           client->xdr->x_prog, client->xdr->x_vers);
352         ret = RPC_CANTRECV;
353         goto out_unlock;
354     }
355 
356     /* Check that other side accepted and responded */
357     if (reply_header.stat != RPC_MSG_ACCEPTED) {
358         /* Offset to map returned error into clnt_stat */
359         ret = reply_header.u.dr.stat + RPC_VERSMISMATCH;
360         E("%08x:%08x call was not accepted.\n",
361           (uint32_t)client->xdr->x_prog, client->xdr->x_vers);
362         goto out_unlock;
363     } else if (reply_header.u.ar.stat != RPC_ACCEPT_SUCCESS) {
364         /* Offset to map returned error into clnt_stat */
365         ret = reply_header.u.ar.stat + RPC_AUTHERROR;
366         E("%08x:%08x call failed with an authentication error.\n",
367           (uint32_t)client->xdr->x_prog, client->xdr->x_vers);
368         goto out_unlock;
369     }
370 
371     xdr->x_op = XDR_DECODE;
372     /* Decode results */
373     if (!xdr_results(xdr, rets_ptr) || ! XDR_MSG_DONE(xdr)) {
374         ret = RPC_CANTDECODERES;
375         E("%08x:%08x error decoding results.\n",
376           client->xdr->x_prog, client->xdr->x_vers);
377         goto out_unlock;
378     }
379 
380     D("%08x:%08x call success.\n",
381       client->xdr->x_prog, client->xdr->x_vers);
382 
383   out_unlock:
384     pthread_mutex_lock(&client->input_xdr_lock);
385     D("%08x:%08x marking input buffer as free.\n",
386       client->xdr->x_prog, client->xdr->x_vers);
387     client->input_xdr_busy = 0;
388     pthread_cond_signal(&client->input_xdr_wait);
389     pthread_mutex_unlock(&client->input_xdr_lock);
390 
391     pthread_mutex_unlock(&client->wait_reply_lock);
392   out:
393     pthread_mutex_unlock(&client->lock);
394     return ret;
395 } /* clnt_call */
396 
xdr_recv_auth(xdr_s_type * xdr,opaque_auth * auth)397 bool_t xdr_recv_auth (xdr_s_type *xdr, opaque_auth *auth)
398 {
399     switch(sizeof(auth->oa_flavor)) {
400     case 1:
401         if(!XDR_RECV_INT8(xdr, (int8_t *)&(auth->oa_flavor))) return FALSE;
402         break;
403     case 2:
404         if(!XDR_RECV_INT16(xdr, (int16_t *)&(auth->oa_flavor))) return FALSE;
405         break;
406     case 4:
407         if(!XDR_RECV_INT32(xdr, (int32_t *)&(auth->oa_flavor))) return FALSE;
408         break;
409     }
410     if (!XDR_RECV_UINT (xdr, (unsigned *)&(auth->oa_length))) {
411         return FALSE;
412     }
413 
414     if (auth->oa_length != 0) {
415         /* We throw away the auth stuff--it's always the default. */
416         auth->oa_base = NULL;
417         if (!XDR_RECV_BYTES (xdr, NULL, auth->oa_length))
418             return FALSE;
419         else
420             return FALSE;
421     }
422 
423     return TRUE;
424 } /* xdr_recv_auth */
425 
426 static bool_t
xdr_recv_accepted_reply_header(xdr_s_type * xdr,struct rpc_accepted_reply_header * accreply)427 xdr_recv_accepted_reply_header(xdr_s_type *xdr,
428                                struct rpc_accepted_reply_header *accreply)
429 {
430     if (!xdr_recv_auth(xdr, &accreply->verf)) {
431         return FALSE;
432     }
433 
434     if (!XDR_RECV_ENUM(xdr, &accreply->stat)) {
435         return FALSE;
436     }
437 
438     switch ((*accreply).stat) {
439     case RPC_PROG_MISMATCH:
440         if (!XDR_RECV_UINT32(xdr, &accreply->u.versions.low)) {
441             return FALSE;
442         }
443 
444         if (!XDR_RECV_UINT32(xdr, &accreply->u.versions.high)) {
445             return FALSE;
446         }
447         break;
448 
449     case RPC_ACCEPT_SUCCESS:
450     case RPC_PROG_UNAVAIL:
451     case RPC_PROC_UNAVAIL:
452     case RPC_GARBAGE_ARGS:
453     case RPC_SYSTEM_ERR:
454     case RPC_PROG_LOCKED:
455         // case ignored
456         break;
457 
458     default:
459         return FALSE;
460     }
461 
462     return TRUE;
463 } /* xdr_recv_accepted_reply_header */
464 
xdr_recv_denied_reply(xdr_s_type * xdr,struct rpc_denied_reply * rejreply)465 static bool_t xdr_recv_denied_reply(xdr_s_type *xdr,
466                                     struct rpc_denied_reply *rejreply)
467 {
468     if (!XDR_RECV_ENUM (xdr, &rejreply->stat))
469         return FALSE;
470 
471     switch ((*rejreply).stat) {
472     case RPC_MISMATCH:
473         if (!XDR_RECV_UINT32(xdr, &rejreply->u.versions.low))
474             return FALSE;
475         if (!XDR_RECV_UINT32(xdr, &rejreply->u.versions.high))
476             return FALSE;
477         break;
478     case RPC_AUTH_ERROR:
479         if (!XDR_RECV_ENUM (xdr, &rejreply->u.why))
480             return FALSE;
481         break;
482     default:
483         return FALSE;
484     }
485 
486     return TRUE;
487 } /* xdr_recv_denied_reply */
488 
xdr_recv_reply_header(xdr_s_type * xdr,rpc_reply_header * reply)489 bool_t xdr_recv_reply_header (xdr_s_type *xdr, rpc_reply_header *reply)
490 {
491     if (!XDR_RECV_ENUM(xdr, &reply->stat)) {
492         return FALSE;
493     }
494 
495     switch ((*reply).stat) {
496     case RPC_MSG_ACCEPTED:
497         if (!xdr_recv_accepted_reply_header(xdr, &reply->u.ar)) {
498             return FALSE;
499 	}
500         break;
501     case RPC_MSG_DENIED:
502         if (!xdr_recv_denied_reply(xdr, &reply->u.dr)) {
503             return FALSE;
504 	}
505         break;
506     default:
507         return FALSE;
508     }
509 
510     return TRUE;
511 } /* xdr_recv_reply_header */
512 
clnt_create(char * host,uint32 prog,uint32 vers,char * proto)513 CLIENT *clnt_create(
514     char * host,
515     uint32 prog,
516     uint32 vers,
517     char * proto)
518 {
519     CLIENT *client = calloc(1, sizeof(CLIENT));
520     if (client) {
521         char name[256];
522 
523         /* for versions like 0x00010001, only compare against major version */
524         if ((vers & 0xFFF00000) == 0)
525             vers &= 0xFFFF0000;
526 
527         pthread_mutex_lock(&rx_mutex);
528 
529         snprintf(name, sizeof(name), "/dev/oncrpc/%08x:%08x",
530                  (uint32_t)prog, (int)vers);
531         client->xdr = xdr_init_common(name, 1 /* client XDR */);
532         if (!client->xdr) {
533             E("failed to initialize client (permissions?)!\n");
534             free(client);
535             pthread_mutex_unlock(&rx_mutex);
536             return NULL;
537         }
538         client->xdr->x_prog = prog;
539         client->xdr->x_vers = vers;
540         client->cb_stop = -1; /* callback thread has not been started */
541 
542         if (!num_clients) {
543             FD_ZERO(&rx_fdset);
544             max_rxfd = 0;
545         }
546 
547         FD_SET(client->xdr->fd, &rx_fdset);
548         if (max_rxfd < client->xdr->fd)
549             max_rxfd = client->xdr->fd;
550         client->next = (CLIENT *)clients;
551         clients = client;
552         if (!num_clients++) {
553             D("launching RX thread.\n");
554             pthread_create(&rx_thread, NULL, rx_context, NULL);
555         }
556 
557         pthread_mutexattr_init(&client->lock_attr);
558 //      pthread_mutexattr_settype(&client->lock_attr, PTHREAD_MUTEX_RECURSIVE);
559         pthread_mutex_init(&client->lock, &client->lock_attr);
560         pthread_mutex_init(&client->wait_reply_lock, &client->lock_attr);
561         pthread_cond_init(&client->wait_reply, NULL);
562         pthread_mutex_init(&client->wait_cb_lock, &client->lock_attr);
563         pthread_cond_init(&client->wait_cb, NULL);
564         pthread_mutex_init(&client->input_xdr_lock, &client->lock_attr);
565         pthread_cond_init(&client->input_xdr_wait, NULL);
566 
567         pthread_mutex_unlock(&rx_mutex);
568     }
569 
570     return client;
571 }
572 
clnt_destroy(CLIENT * client)573 void clnt_destroy(CLIENT *client) {
574     if (client) {
575         pthread_mutex_lock(&client->lock);
576         D("%08x:%08x destroying client\n",
577           client->xdr->x_prog,
578           client->xdr->x_vers);
579 
580 
581         if (!client->cb_stop) {
582             /* The callback thread is running, we need to stop it */
583             client->cb_stop = 1;
584             D("%08x:%08x stopping callback thread\n",
585               client->xdr->x_prog,
586               client->xdr->x_vers);
587             pthread_mutex_lock(&client->wait_cb_lock);
588             pthread_cond_signal(&client->wait_cb);
589             pthread_mutex_unlock(&client->wait_cb_lock);
590             D("%08x:%08x joining callback thread\n",
591               client->xdr->x_prog,
592               client->xdr->x_vers);
593             pthread_join(client->cb_thread, NULL);
594         }
595 
596         pthread_mutex_lock(&rx_mutex); /* sync access to the client list */
597         {
598             CLIENT *trav = (CLIENT *)clients, *prev = NULL;
599             for(; trav; trav = trav->next) {
600                 if (trav == client) {
601                      D("%08x:%08x removing from client list\n",
602                       client->xdr->x_prog,
603                       client->xdr->x_vers);
604                     if (prev)
605                         prev->next = trav->next;
606                     else
607                         clients = trav->next;
608                     num_clients--;
609                     FD_CLR(client->xdr->fd, &rx_fdset);
610                     break;
611                 }
612                 prev = trav;
613             }
614         }
615         if (!num_clients) {
616             D("stopping rx thread!\n");
617             pthread_join(rx_thread, NULL);
618             D("stopped rx thread\n");
619         }
620         pthread_mutex_unlock(&rx_mutex); /* sync access to the client list */
621 
622         pthread_mutex_destroy(&client->input_xdr_lock);
623         pthread_cond_destroy(&client->input_xdr_wait);
624 
625         pthread_mutex_destroy(&client->wait_reply_lock);
626         pthread_cond_destroy(&client->wait_reply);
627         xdr_destroy_common(client->xdr);
628 
629         // FIXME: what happens when we lock the client while destroying it,
630         // and another thread locks the mutex in clnt_call, and then we
631         // call pthread_mutex_destroy?  Does destroy automatically unlock and
632         // then cause the lock in clnt_call() to return an error?  When we
633         // unlock the mutex here there can be a context switch to the other
634         // thread, which will cause it to obtain the mutex on the destroyed
635         // client (and probably crash), and then we get to the destroy call
636         // here... will that call fail?
637         pthread_mutex_unlock(&client->lock);
638         pthread_mutex_destroy(&client->lock);
639         pthread_mutexattr_destroy(&client->lock_attr);
640         D("client destroy done\n");
641         free(client);
642     }
643 }
644