]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/network.c
mount.nfs: Always close the socket at the end of getport()
[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         /*
314          * Try to get a socket with a non-privileged port.
315          * clnt*create() will create one anyway if this
316          * fails.
317          */
318         socket = get_socket(saddr, proto, PMAP_TIMEOUT, FALSE, FALSE);
319         if (socket == RPC_ANYSOCK) {
320                 if (proto == IPPROTO_TCP && errno == ETIMEDOUT) {
321                         /*
322                          * TCP SYN timed out, so exit now.
323                          */
324                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
325                 }
326                 return 0;
327         }
328
329         switch (proto) {
330         case IPPROTO_UDP:
331                 clnt = clntudp_bufcreate(saddr,
332                                          PMAPPROG, PMAPVERS,
333                                          RETRY_TIMEOUT, &socket,
334                                          RPCSMALLMSGSIZE,
335                                          RPCSMALLMSGSIZE);
336                 break;
337         case IPPROTO_TCP:
338                 clnt = clnttcp_create(saddr, PMAPPROG, PMAPVERS, &socket,
339                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
340                 break;
341         }
342         if (clnt != NULL) {
343                 struct pmap parms = {
344                         .pm_prog        = program,
345                         .pm_vers        = version,
346                         .pm_prot        = proto,
347                 };
348
349                 stat = clnt_call(clnt, PMAPPROC_GETPORT,
350                                  (xdrproc_t)xdr_pmap, (caddr_t)&parms,
351                                  (xdrproc_t)xdr_u_short, (caddr_t)&port,
352                                  TIMEOUT);
353                 if (stat) {
354                         clnt_geterr(clnt, &rpc_createerr.cf_error);
355                         rpc_createerr.cf_stat = stat;
356                 }
357                 clnt_destroy(clnt);
358                 if (stat != RPC_SUCCESS)
359                         port = 0;
360                 else if (port == 0)
361                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
362         }
363         close(socket);
364
365         return port;
366 }
367
368 /*
369  * Use the portmapper to discover whether or not the service we want is
370  * available. The lists 'versions' and 'protos' define ordered sequences
371  * of service versions and udp/tcp protocols to probe for.
372  */
373 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
374                         const unsigned int *protos)
375 {
376         struct sockaddr_in *saddr = &server->saddr;
377         struct pmap *pmap = &server->pmap;
378         const unsigned long prog = pmap->pm_prog, *p_vers;
379         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
380         const u_short port = (u_short) pmap->pm_port;
381         unsigned long vers = pmap->pm_vers;
382         unsigned short p_port;
383
384         p_prot = prot ? &prot : protos;
385         p_vers = vers ? &vers : versions;
386         rpc_createerr.cf_stat = 0;
387         for (;;) {
388                 p_port = getport(saddr, prog, *p_vers, *p_prot);
389                 if (p_port) {
390                         if (!port || port == p_port) {
391                                 saddr->sin_port = htons(p_port);
392                                 if (verbose) {
393                                         printf(_("%s: trying %s prog %ld vers "
394                                                 "%ld prot %s port %d\n"),
395                                                 progname,
396                                                 inet_ntoa(saddr->sin_addr),
397                                                 prog, *p_vers,
398                                                 *p_prot == IPPROTO_UDP ?
399                                                         "udp" : "tcp",
400                                                 p_port);
401                                 }
402                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
403                                         goto out_ok;
404                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
405                                         goto out_bad;
406                         }
407                 }
408                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED)
409                         goto out_bad;
410
411                 if (!prot) {
412                         if (*++p_prot)
413                                 continue;
414                         p_prot = protos;
415                 }
416                 if (vers == pmap->pm_vers) {
417                         p_vers = versions;
418                         vers = 0;
419                 }
420                 if (vers || !*++p_vers)
421                         break;
422         }
423
424 out_bad:
425         return 0;
426
427 out_ok:
428         if (!vers)
429                 pmap->pm_vers = *p_vers;
430         if (!prot)
431                 pmap->pm_prot = *p_prot;
432         if (!port)
433                 pmap->pm_port = p_port;
434         rpc_createerr.cf_stat = 0;
435         return 1;
436 }
437
438 static int probe_nfsport(clnt_addr_t *nfs_server)
439 {
440         struct pmap *pmap = &nfs_server->pmap;
441
442         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
443                 return 1;
444
445         if (nfs_mount_data_version >= 4)
446                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
447         else
448                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
449 }
450
451 static int probe_mntport(clnt_addr_t *mnt_server)
452 {
453         struct pmap *pmap = &mnt_server->pmap;
454
455         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
456                 return 1;
457
458         if (nfs_mount_data_version >= 4)
459                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
460         else
461                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
462 }
463
464 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
465 {
466         struct pmap *nfs_pmap = &nfs_server->pmap;
467         struct pmap *mnt_pmap = &mnt_server->pmap;
468         struct pmap save_nfs, save_mnt;
469         int res;
470         const unsigned long *probe_vers;
471
472         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
473                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
474         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
475                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
476         if (nfs_pmap->pm_vers)
477                 goto version_fixed;
478
479         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
480         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
481         probe_vers = (nfs_mount_data_version >= 4) ?
482                         probe_mnt3_first : probe_mnt1_first;
483
484         for (; *probe_vers; probe_vers++) {
485                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
486                 if ((res = probe_nfsport(nfs_server) != 0)) {
487                         mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
488                         if ((res = probe_mntport(mnt_server)) != 0)
489                                 return 1;
490                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
491                 }
492                 switch (rpc_createerr.cf_stat) {
493                 case RPC_PROGVERSMISMATCH:
494                 case RPC_PROGNOTREGISTERED:
495                         break;
496                 default:
497                         goto out_bad;
498                 }
499                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
500         }
501
502 out_bad:
503         return 0;
504
505 version_fixed:
506         if (!probe_nfsport(nfs_server))
507                 goto out_bad;
508         return probe_mntport(mnt_server);
509 }
510
511 static int probe_statd(void)
512 {
513         struct sockaddr_in addr;
514         unsigned short port;
515
516         memset(&addr, 0, sizeof(addr));
517         addr.sin_family = AF_INET;
518         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
519         port = getport(&addr, 100024, 1, IPPROTO_UDP);
520
521         if (port == 0)
522                 return 0;
523         addr.sin_port = htons(port);
524
525         if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
526                 return 0;
527
528         return 1;
529 }
530
531 /*
532  * Attempt to start rpc.statd
533  */
534 int start_statd(void)
535 {
536 #ifdef START_STATD
537         struct stat stb;
538 #endif
539
540         if (probe_statd())
541                 return 1;
542
543 #ifdef START_STATD
544         if (stat(START_STATD, &stb) == 0) {
545                 if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
546                         system(START_STATD);
547                         if (probe_statd())
548                                 return 1;
549                 }
550         }
551 #endif
552
553         return 0;
554 }
555
556 /*
557  * nfs_call_umount - ask the server to remove a share from it's rmtab
558  * @mnt_server: address of RPC MNT program server
559  * @argp: directory path of share to "unmount"
560  *
561  * Returns one if the unmount call succeeded; zero if the unmount
562  * failed for any reason.
563  *
564  * Note that a side effect of calling this function is that rpccreateerr
565  * is set.
566  */
567 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
568 {
569         CLIENT *clnt;
570         enum clnt_stat res = 0;
571         int msock;
572
573         switch (mnt_server->pmap.pm_vers) {
574         case 3:
575         case 2:
576         case 1:
577                 if (!probe_mntport(mnt_server))
578                         return 0;
579                 clnt = mnt_openclnt(mnt_server, &msock);
580                 if (!clnt)
581                         return 0;
582                 res = clnt_call(clnt, MOUNTPROC_UMNT,
583                                 (xdrproc_t)xdr_dirpath, (caddr_t)argp,
584                                 (xdrproc_t)xdr_void, NULL,
585                                 TIMEOUT);
586                 mnt_closeclnt(clnt, msock);
587                 if (res == RPC_SUCCESS)
588                         return 1;
589                 break;
590         default:
591                 res = RPC_SUCCESS;
592                 break;
593         }
594
595         if (res == RPC_SUCCESS)
596                 return 1;
597         return 0;
598 }
599
600 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
601 {
602         struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
603         struct pmap *mnt_pmap = &mnt_server->pmap;
604         CLIENT *clnt = NULL;
605
606         mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
607         *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
608                                 TRUE, FALSE);
609         if (*msock == RPC_ANYSOCK) {
610                 if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
611                         /*
612                          * Probably in-use by a TIME_WAIT connection,
613                          * It is worth waiting a while and trying again.
614                          */
615                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
616                 return NULL;
617         }
618
619         switch (mnt_pmap->pm_prot) {
620         case IPPROTO_UDP:
621                 clnt = clntudp_bufcreate(mnt_saddr,
622                                          mnt_pmap->pm_prog, mnt_pmap->pm_vers,
623                                          RETRY_TIMEOUT, msock,
624                                          MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
625                 break;
626         case IPPROTO_TCP:
627                 clnt = clnttcp_create(mnt_saddr,
628                                       mnt_pmap->pm_prog, mnt_pmap->pm_vers,
629                                       msock,
630                                       MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
631                 break;
632         }
633         if (clnt) {
634                 /* try to mount hostname:dirname */
635                 clnt->cl_auth = authunix_create_default();
636                 return clnt;
637         }
638         return NULL;
639 }
640
641 void mnt_closeclnt(CLIENT *clnt, int msock)
642 {
643         auth_destroy(clnt->cl_auth);
644         clnt_destroy(clnt);
645         close(msock);
646 }
647
648 /*
649  * Sigh... getport() doesn't actually check the version number.
650  * In order to make sure that the server actually supports the service
651  * we're requesting, we open and RPC client, and fire off a NULL
652  * RPC call.
653  */
654 int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
655                 const unsigned long vers, const unsigned int prot,
656                 struct sockaddr_in *caddr)
657 {
658         CLIENT *clnt = NULL;
659         int sock, stat;
660         static char clnt_res;
661         struct sockaddr dissolve;
662
663         rpc_createerr.cf_stat = stat = errno = 0;
664         sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
665         if (sock == RPC_ANYSOCK) {
666                 if (errno == ETIMEDOUT) {
667                         /*
668                          * TCP timeout. Bubble up the error to see 
669                          * how it should be handled.
670                          */
671                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
672                 }
673                 return 0;
674         }
675
676         if (caddr) {
677                 /* Get the address of our end of this connection */
678                 socklen_t len = sizeof(*caddr);
679                 if (getsockname(sock, caddr, &len) != 0)
680                         caddr->sin_family = 0;
681         }
682
683         switch(prot) {
684         case IPPROTO_UDP:
685                 /* The socket is connected (so we could getsockname successfully),
686                  * but some servers on multi-homed hosts reply from
687                  * the wrong address, so if we stay connected, we lose the reply.
688                  */
689                 dissolve.sa_family = AF_UNSPEC;
690                 connect(sock, &dissolve, sizeof(dissolve));
691
692                 clnt = clntudp_bufcreate(saddr, prog, vers,
693                                          RETRY_TIMEOUT, &sock,
694                                          RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
695                 break;
696         case IPPROTO_TCP:
697                 clnt = clnttcp_create(saddr, prog, vers, &sock,
698                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
699                 break;
700         }
701         if (!clnt) {
702                 close(sock);
703                 return 0;
704         }
705         memset(&clnt_res, 0, sizeof(clnt_res));
706         stat = clnt_call(clnt, NULLPROC,
707                          (xdrproc_t)xdr_void, (caddr_t)NULL,
708                          (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
709                          TIMEOUT);
710         if (stat) {
711                 clnt_geterr(clnt, &rpc_createerr.cf_error);
712                 rpc_createerr.cf_stat = stat;
713         }
714         clnt_destroy(clnt);
715         close(sock);
716
717         if (stat == RPC_SUCCESS)
718                 return 1;
719         else
720                 return 0;
721 }