mount.nfs: Treat another UDP/TCP pair of strings like the rest
[nfs-utils.git] / utils / mount / network.c
1 /*
2  * network.c -- Provide common network functions for NFS mount/umount
3  *
4  * Copyright (C) 2007 Oracle.  All rights reserved.
5  * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 021110-1307, USA.
21  *
22  */
23
24 #include <ctype.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <time.h>
32 #include <rpc/rpc.h>
33 #include <rpc/pmap_prot.h>
34 #include <rpc/pmap_clnt.h>
35 #include <sys/socket.h>
36
37 #include "xcommon.h"
38 #include "mount.h"
39 #include "nls.h"
40 #include "nfs_mount.h"
41 #include "mount_constants.h"
42 #include "network.h"
43
44 #ifdef HAVE_RPCSVC_NFS_PROT_H
45 #include <rpcsvc/nfs_prot.h>
46 #else
47 #include <linux/nfs.h>
48 #define nfsstat nfs_stat
49 #endif
50
51 #ifndef NFS_PORT
52 #define NFS_PORT 2049
53 #endif
54
55 #define PMAP_TIMEOUT    (10)
56 #define CONNECT_TIMEOUT (20)
57 #define MOUNT_TIMEOUT   (30)
58
59 #if SIZEOF_SOCKLEN_T - 0 == 0
60 #define socklen_t unsigned int
61 #endif
62
63 extern int nfs_mount_data_version;
64 extern char *progname;
65 extern int verbose;
66
67 static const unsigned long nfs_to_mnt[] = {
68         0,
69         0,
70         1,
71         3,
72 };
73
74 static const unsigned long mnt_to_nfs[] = {
75         0,
76         2,
77         2,
78         3,
79 };
80
81 /*
82  * Map an NFS version into the corresponding Mountd version
83  */
84 unsigned long nfsvers_to_mnt(const unsigned long vers)
85 {
86         if (vers <= 3)
87                 return nfs_to_mnt[vers];
88         return 0;
89 }
90
91 /*
92  * Map a Mountd version into the corresponding NFS version
93  */
94 static unsigned long mntvers_to_nfs(const unsigned long vers)
95 {
96         if (vers <= 3)
97                 return mnt_to_nfs[vers];
98         return 0;
99 }
100
101 static const unsigned int probe_udp_only[] = {
102         IPPROTO_UDP,
103         0,
104 };
105
106 static const unsigned int probe_udp_first[] = {
107         IPPROTO_UDP,
108         IPPROTO_TCP,
109         0,
110 };
111
112 static const unsigned int probe_tcp_first[] = {
113         IPPROTO_TCP,
114         IPPROTO_UDP,
115         0,
116 };
117
118 static const unsigned long probe_nfs2_only[] = {
119         2,
120         0,
121 };
122
123 static const unsigned long probe_nfs3_first[] = {
124         3,
125         2,
126         0,
127 };
128
129 static const unsigned long probe_mnt1_first[] = {
130         1,
131         2,
132         0,
133 };
134
135 static const unsigned long probe_mnt3_first[] = {
136         3,
137         1,
138         2,
139         0,
140 };
141
142 int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
143 {
144         struct hostent *hp;
145
146         saddr->sin_family = AF_INET;
147         if (!inet_aton(hostname, &saddr->sin_addr)) {
148                 if ((hp = gethostbyname(hostname)) == NULL) {
149                         nfs_error(_("%s: can't get address for %s\n"),
150                                         progname, hostname);
151                         return 0;
152                 } else {
153                         if (hp->h_length > sizeof(*saddr)) {
154                                 nfs_error(_("%s: got bad hp->h_length\n"),
155                                                 progname);
156                                 hp->h_length = sizeof(*saddr);
157                         }
158                         memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length);
159                 }
160         }
161         return 1;
162 }
163
164 /*
165  * Attempt to connect a socket, but time out after "timeout" seconds.
166  *
167  * On error return, caller closes the socket.
168  */
169 static int connect_to(int fd, struct sockaddr *addr,
170                         socklen_t addrlen, int timeout)
171 {
172         int ret, saved;
173         fd_set rset, wset;
174         struct timeval tv = {
175                 .tv_sec = timeout,
176         };
177
178         saved = fcntl(fd, F_GETFL, 0);
179         fcntl(fd, F_SETFL, saved | O_NONBLOCK);
180
181         ret = connect(fd, addr, addrlen);
182         if (ret < 0 && errno != EINPROGRESS)
183                 return -1;
184         if (ret == 0)
185                 goto out;
186
187         FD_ZERO(&rset);
188         FD_SET(fd, &rset);
189         wset = rset;
190         ret = select(fd + 1, &rset, &wset, NULL, &tv);
191         if (ret == 0) {
192                 errno = ETIMEDOUT;
193                 return -1;
194         }
195         if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
196                 int error;
197                 socklen_t len = sizeof(error);
198                 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
199                         return -1;
200                 if (error) {
201                         errno = error;
202                         return -1;
203                 }
204         } else
205                 return -1;
206
207 out:
208         fcntl(fd, F_SETFL, saved);
209         return 0;
210 }
211
212 /*
213  * Create a socket that is locally bound to a reserved or non-reserved port.
214  *
215  * The caller should check rpc_createerr to determine the cause of any error.
216  */
217 static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
218                         unsigned int timeout, int resvp, int conn)
219 {
220         int so, cc, type;
221         struct sockaddr_in laddr;
222         socklen_t namelen = sizeof(laddr);
223
224         type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
225         if ((so = socket (AF_INET, type, p_prot)) < 0)
226                 goto err_socket;
227
228         laddr.sin_family = AF_INET;
229         laddr.sin_port = 0;
230         laddr.sin_addr.s_addr = htonl(INADDR_ANY);
231         if (resvp) {
232                 if (bindresvport(so, &laddr) < 0)
233                         goto err_bindresvport;
234         } else {
235                 cc = bind(so, (struct sockaddr *)&laddr, namelen);
236                 if (cc < 0)
237                         goto err_bind;
238         }
239         if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
240                 cc = connect_to(so, (struct sockaddr *)saddr, namelen,
241                                 timeout);
242                 if (cc < 0)
243                         goto err_connect;
244         }
245         return so;
246
247 err_socket:
248         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
249         rpc_createerr.cf_error.re_errno = errno;
250         if (verbose) {
251                 nfs_error(_("%s: Unable to create %s socket: errno %d (%s)\n"),
252                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
253                         errno, strerror(errno));
254         }
255         return RPC_ANYSOCK;
256
257 err_bindresvport:
258         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
259         rpc_createerr.cf_error.re_errno = errno;
260         if (verbose) {
261                 nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
262                                 " (%s)\n"),
263                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
264                         errno, strerror(errno));
265         }
266         close(so);
267         return RPC_ANYSOCK;
268
269 err_bind:
270         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
271         rpc_createerr.cf_error.re_errno = errno;
272         if (verbose) {
273                 nfs_error(_("%s: Unable to bind to %s socket: errno %d (%s)\n"),
274                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
275                         errno, strerror(errno));
276         }
277         close(so);
278         return RPC_ANYSOCK;
279
280 err_connect:
281         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
282         rpc_createerr.cf_error.re_errno = errno;
283         if (verbose) {
284                 nfs_error(_("%s: Unable to connect to %s:%d, errno %d (%s)\n"),
285                         progname, inet_ntoa(saddr->sin_addr),
286                         ntohs(saddr->sin_port), errno, strerror(errno));
287         }
288         close(so);
289         return RPC_ANYSOCK;
290 }
291
292 /*
293  * getport() is very similar to pmap_getport() with the exception that
294  * this version tries to use an ephemeral port, since reserved ports are
295  * not needed for GETPORT queries.  This conserves the very limited
296  * reserved port space, which helps reduce failed socket binds
297  * during mount storms.
298  *
299  * A side effect of calling this function is that rpccreateerr is set.
300  */
301 static unsigned short getport(struct sockaddr_in *saddr,
302                                 unsigned long program,
303                                 unsigned long version,
304                                 unsigned int proto)
305 {
306         unsigned short port = 0;
307         int socket;
308         CLIENT *clnt = NULL;
309         enum clnt_stat stat;
310
311         saddr->sin_port = htons(PMAPPORT);
312
313         socket = get_socket(saddr, proto, PMAP_TIMEOUT, FALSE, FALSE);
314         if (socket == RPC_ANYSOCK) {
315                 if (proto == IPPROTO_TCP &&
316                     rpc_createerr.cf_error.re_errno == ETIMEDOUT)
317                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
318                 return 0;
319         }
320
321         switch (proto) {
322         case IPPROTO_UDP:
323                 clnt = clntudp_bufcreate(saddr,
324                                          PMAPPROG, PMAPVERS,
325                                          RETRY_TIMEOUT, &socket,
326                                          RPCSMALLMSGSIZE,
327                                          RPCSMALLMSGSIZE);
328                 break;
329         case IPPROTO_TCP:
330                 clnt = clnttcp_create(saddr, PMAPPROG, PMAPVERS, &socket,
331                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
332                 break;
333         }
334         if (clnt != NULL) {
335                 struct pmap parms = {
336                         .pm_prog        = program,
337                         .pm_vers        = version,
338                         .pm_prot        = proto,
339                 };
340
341                 stat = clnt_call(clnt, PMAPPROC_GETPORT,
342                                  (xdrproc_t)xdr_pmap, (caddr_t)&parms,
343                                  (xdrproc_t)xdr_u_short, (caddr_t)&port,
344                                  TIMEOUT);
345                 if (stat) {
346                         clnt_geterr(clnt, &rpc_createerr.cf_error);
347                         rpc_createerr.cf_stat = stat;
348                 }
349                 clnt_destroy(clnt);
350                 if (stat != RPC_SUCCESS)
351                         port = 0;
352                 else if (port == 0)
353                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
354         }
355         close(socket);
356
357         return port;
358 }
359
360 /*
361  * Use the portmapper to discover whether or not the service we want is
362  * available. The lists 'versions' and 'protos' define ordered sequences
363  * of service versions and udp/tcp protocols to probe for.
364  */
365 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
366                         const unsigned int *protos)
367 {
368         struct sockaddr_in *saddr = &server->saddr;
369         struct pmap *pmap = &server->pmap;
370         const unsigned long prog = pmap->pm_prog, *p_vers;
371         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
372         const u_short port = (u_short) pmap->pm_port;
373         unsigned long vers = pmap->pm_vers;
374         unsigned short p_port;
375
376         p_prot = prot ? &prot : protos;
377         p_vers = vers ? &vers : versions;
378         rpc_createerr.cf_stat = 0;
379         for (;;) {
380                 p_port = getport(saddr, prog, *p_vers, *p_prot);
381                 if (p_port) {
382                         if (!port || port == p_port) {
383                                 saddr->sin_port = htons(p_port);
384                                 if (verbose) {
385                                         printf(_("%s: trying %s prog %ld vers "
386                                                 "%ld prot %s port %d\n"),
387                                                 progname,
388                                                 inet_ntoa(saddr->sin_addr),
389                                                 prog, *p_vers,
390                                                 *p_prot == IPPROTO_UDP ?
391                                                         _("UDP") : _("TCP"),
392                                                 p_port);
393                                 }
394                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
395                                         goto out_ok;
396                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
397                                         goto out_bad;
398                         }
399                 }
400                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED)
401                         goto out_bad;
402
403                 if (!prot) {
404                         if (*++p_prot)
405                                 continue;
406                         p_prot = protos;
407                 }
408                 if (vers == pmap->pm_vers) {
409                         p_vers = versions;
410                         vers = 0;
411                 }
412                 if (vers || !*++p_vers)
413                         break;
414         }
415
416 out_bad:
417         return 0;
418
419 out_ok:
420         if (!vers)
421                 pmap->pm_vers = *p_vers;
422         if (!prot)
423                 pmap->pm_prot = *p_prot;
424         if (!port)
425                 pmap->pm_port = p_port;
426         rpc_createerr.cf_stat = 0;
427         return 1;
428 }
429
430 static int probe_nfsport(clnt_addr_t *nfs_server)
431 {
432         struct pmap *pmap = &nfs_server->pmap;
433
434         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
435                 return 1;
436
437         if (nfs_mount_data_version >= 4)
438                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
439         else
440                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
441 }
442
443 static int probe_mntport(clnt_addr_t *mnt_server)
444 {
445         struct pmap *pmap = &mnt_server->pmap;
446
447         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
448                 return 1;
449
450         if (nfs_mount_data_version >= 4)
451                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
452         else
453                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
454 }
455
456 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
457 {
458         struct pmap *nfs_pmap = &nfs_server->pmap;
459         struct pmap *mnt_pmap = &mnt_server->pmap;
460         struct pmap save_nfs, save_mnt;
461         int res;
462         const unsigned long *probe_vers;
463
464         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
465                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
466         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
467                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
468         if (nfs_pmap->pm_vers)
469                 goto version_fixed;
470
471         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
472         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
473         probe_vers = (nfs_mount_data_version >= 4) ?
474                         probe_mnt3_first : probe_mnt1_first;
475
476         for (; *probe_vers; probe_vers++) {
477                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
478                 if ((res = probe_nfsport(nfs_server) != 0)) {
479                         mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
480                         if ((res = probe_mntport(mnt_server)) != 0)
481                                 return 1;
482                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
483                 }
484                 switch (rpc_createerr.cf_stat) {
485                 case RPC_PROGVERSMISMATCH:
486                 case RPC_PROGNOTREGISTERED:
487                         break;
488                 default:
489                         goto out_bad;
490                 }
491                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
492         }
493
494 out_bad:
495         return 0;
496
497 version_fixed:
498         if (!probe_nfsport(nfs_server))
499                 goto out_bad;
500         return probe_mntport(mnt_server);
501 }
502
503 static int probe_statd(void)
504 {
505         struct sockaddr_in addr;
506         unsigned short port;
507
508         memset(&addr, 0, sizeof(addr));
509         addr.sin_family = AF_INET;
510         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
511         port = getport(&addr, 100024, 1, IPPROTO_UDP);
512
513         if (port == 0)
514                 return 0;
515         addr.sin_port = htons(port);
516
517         if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
518                 return 0;
519
520         return 1;
521 }
522
523 /*
524  * Attempt to start rpc.statd
525  */
526 int start_statd(void)
527 {
528 #ifdef START_STATD
529         struct stat stb;
530 #endif
531
532         if (probe_statd())
533                 return 1;
534
535 #ifdef START_STATD
536         if (stat(START_STATD, &stb) == 0) {
537                 if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
538                         system(START_STATD);
539                         if (probe_statd())
540                                 return 1;
541                 }
542         }
543 #endif
544
545         return 0;
546 }
547
548 /*
549  * nfs_call_umount - ask the server to remove a share from it's rmtab
550  * @mnt_server: address of RPC MNT program server
551  * @argp: directory path of share to "unmount"
552  *
553  * Returns one if the unmount call succeeded; zero if the unmount
554  * failed for any reason.
555  *
556  * Note that a side effect of calling this function is that rpccreateerr
557  * is set.
558  */
559 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
560 {
561         CLIENT *clnt;
562         enum clnt_stat res = 0;
563         int msock;
564
565         switch (mnt_server->pmap.pm_vers) {
566         case 3:
567         case 2:
568         case 1:
569                 if (!probe_mntport(mnt_server))
570                         return 0;
571                 clnt = mnt_openclnt(mnt_server, &msock);
572                 if (!clnt)
573                         return 0;
574                 res = clnt_call(clnt, MOUNTPROC_UMNT,
575                                 (xdrproc_t)xdr_dirpath, (caddr_t)argp,
576                                 (xdrproc_t)xdr_void, NULL,
577                                 TIMEOUT);
578                 mnt_closeclnt(clnt, msock);
579                 if (res == RPC_SUCCESS)
580                         return 1;
581                 break;
582         default:
583                 res = RPC_SUCCESS;
584                 break;
585         }
586
587         if (res == RPC_SUCCESS)
588                 return 1;
589         return 0;
590 }
591
592 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
593 {
594         struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
595         struct pmap *mnt_pmap = &mnt_server->pmap;
596         CLIENT *clnt = NULL;
597
598         mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
599         *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
600                                 TRUE, FALSE);
601         if (*msock == RPC_ANYSOCK) {
602                 if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
603                         /*
604                          * Probably in-use by a TIME_WAIT connection,
605                          * It is worth waiting a while and trying again.
606                          */
607                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
608                 return NULL;
609         }
610
611         switch (mnt_pmap->pm_prot) {
612         case IPPROTO_UDP:
613                 clnt = clntudp_bufcreate(mnt_saddr,
614                                          mnt_pmap->pm_prog, mnt_pmap->pm_vers,
615                                          RETRY_TIMEOUT, msock,
616                                          MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
617                 break;
618         case IPPROTO_TCP:
619                 clnt = clnttcp_create(mnt_saddr,
620                                       mnt_pmap->pm_prog, mnt_pmap->pm_vers,
621                                       msock,
622                                       MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
623                 break;
624         }
625         if (clnt) {
626                 /* try to mount hostname:dirname */
627                 clnt->cl_auth = authunix_create_default();
628                 return clnt;
629         }
630         return NULL;
631 }
632
633 void mnt_closeclnt(CLIENT *clnt, int msock)
634 {
635         auth_destroy(clnt->cl_auth);
636         clnt_destroy(clnt);
637         close(msock);
638 }
639
640 /*
641  * Sigh... getport() doesn't actually check the version number.
642  * In order to make sure that the server actually supports the service
643  * we're requesting, we open and RPC client, and fire off a NULL
644  * RPC call.
645  */
646 int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
647                 const unsigned long vers, const unsigned int prot,
648                 struct sockaddr_in *caddr)
649 {
650         CLIENT *clnt = NULL;
651         int sock, stat;
652         static char clnt_res;
653         struct sockaddr dissolve;
654
655         rpc_createerr.cf_stat = stat = 0;
656         sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
657         if (sock == RPC_ANYSOCK) {
658                 if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
659                         /*
660                          * TCP timeout. Bubble up the error to see 
661                          * how it should be handled.
662                          */
663                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
664                 }
665                 return 0;
666         }
667
668         if (caddr) {
669                 /* Get the address of our end of this connection */
670                 socklen_t len = sizeof(*caddr);
671                 if (getsockname(sock, caddr, &len) != 0)
672                         caddr->sin_family = 0;
673         }
674
675         switch(prot) {
676         case IPPROTO_UDP:
677                 /* The socket is connected (so we could getsockname successfully),
678                  * but some servers on multi-homed hosts reply from
679                  * the wrong address, so if we stay connected, we lose the reply.
680                  */
681                 dissolve.sa_family = AF_UNSPEC;
682                 connect(sock, &dissolve, sizeof(dissolve));
683
684                 clnt = clntudp_bufcreate(saddr, prog, vers,
685                                          RETRY_TIMEOUT, &sock,
686                                          RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
687                 break;
688         case IPPROTO_TCP:
689                 clnt = clnttcp_create(saddr, prog, vers, &sock,
690                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
691                 break;
692         }
693         if (!clnt) {
694                 close(sock);
695                 return 0;
696         }
697         memset(&clnt_res, 0, sizeof(clnt_res));
698         stat = clnt_call(clnt, NULLPROC,
699                          (xdrproc_t)xdr_void, (caddr_t)NULL,
700                          (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
701                          TIMEOUT);
702         if (stat) {
703                 clnt_geterr(clnt, &rpc_createerr.cf_error);
704                 rpc_createerr.cf_stat = stat;
705         }
706         clnt_destroy(clnt);
707         close(sock);
708
709         if (stat == RPC_SUCCESS)
710                 return 1;
711         else
712                 return 0;
713 }