]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/network.c
umount.nfs: eliminate a nearly empty header file.
[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 "conn.h"
38 #include "xcommon.h"
39 #include "mount.h"
40 #include "nls.h"
41 #include "nfs_mount.h"
42 #include "mount_constants.h"
43 #include "network.h"
44
45 #ifdef HAVE_RPCSVC_NFS_PROT_H
46 #include <rpcsvc/nfs_prot.h>
47 #else
48 #include <linux/nfs.h>
49 #define nfsstat nfs_stat
50 #endif
51
52 #ifndef NFS_PORT
53 #define NFS_PORT 2049
54 #endif
55
56 extern int nfs_mount_data_version;
57 extern char *progname;
58 extern int verbose;
59
60 static const unsigned int probe_udp_only[] = {
61         IPPROTO_UDP,
62         0,
63 };
64
65 static const unsigned int probe_udp_first[] = {
66         IPPROTO_UDP,
67         IPPROTO_TCP,
68         0,
69 };
70
71 static const unsigned int probe_tcp_first[] = {
72         IPPROTO_TCP,
73         IPPROTO_UDP,
74         0,
75 };
76
77 static const unsigned long probe_nfs2_only[] = {
78         2,
79         0,
80 };
81
82 static const unsigned long probe_nfs3_first[] = {
83         3,
84         2,
85         0,
86 };
87
88 static const unsigned long probe_mnt1_first[] = {
89         1,
90         2,
91         0,
92 };
93
94 static const unsigned long probe_mnt3_first[] = {
95         3,
96         1,
97         2,
98         0,
99 };
100
101 int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
102 {
103         struct hostent *hp;
104
105         saddr->sin_family = AF_INET;
106         if (!inet_aton(hostname, &saddr->sin_addr)) {
107                 if ((hp = gethostbyname(hostname)) == NULL) {
108                         nfs_error(_("%s: can't get address for %s\n"),
109                                         progname, hostname);
110                         return 0;
111                 } else {
112                         if (hp->h_length > sizeof(*saddr)) {
113                                 nfs_error(_("%s: got bad hp->h_length\n"),
114                                                 progname);
115                                 hp->h_length = sizeof(*saddr);
116                         }
117                         memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length);
118                 }
119         }
120         return 1;
121 }
122
123 /*
124  * getport() is very similar to pmap_getport() with
125  * the exception this version uses a non-reserve ports
126  * instead of reserve ports since reserve ports
127  * are not needed for pmap requests.
128  */
129 unsigned short getport(struct sockaddr_in *saddr, unsigned long prog,
130                         unsigned long vers, unsigned int prot)
131 {
132         unsigned short port = 0;
133         int socket;
134         CLIENT *clnt = NULL;
135         struct pmap parms;
136         enum clnt_stat stat;
137
138         saddr->sin_port = htons (PMAPPORT);
139         socket = get_socket(saddr, prot, FALSE, FALSE);
140
141         switch (prot) {
142         case IPPROTO_UDP:
143                 clnt = clntudp_bufcreate(saddr,
144                                          PMAPPROG, PMAPVERS, TIMEOUT, &socket,
145                                          UDPMSGSIZE, UDPMSGSIZE);
146                 break;
147         case IPPROTO_TCP:
148                 clnt = clnttcp_create(saddr,
149                         PMAPPROG, PMAPVERS, &socket, 50, 500);
150                 break;
151         }
152         if (clnt != NULL) {
153                 parms.pm_prog = prog;
154                 parms.pm_vers = vers;
155                 parms.pm_prot = prot;
156                 parms.pm_port = 0;    /* not needed or used */
157
158                 stat = clnt_call(clnt, PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap,
159                         (caddr_t)&parms, (xdrproc_t)xdr_u_short, (caddr_t)&port, TIMEOUT);
160                 if (stat) {
161                         clnt_geterr(clnt, &rpc_createerr.cf_error);
162                         rpc_createerr.cf_stat = stat;
163                 }
164                 clnt_destroy(clnt);
165                 if (stat != RPC_SUCCESS)
166                         port = 0;
167                 else if (port == 0)
168                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
169         }
170         if (socket != 1)
171                 close(socket);
172
173         return port;
174 }
175
176 /*
177  * Use the portmapper to discover whether or not the service we want is
178  * available. The lists 'versions' and 'protos' define ordered sequences
179  * of service versions and udp/tcp protocols to probe for.
180  */
181 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
182                         const unsigned int *protos)
183 {
184         struct sockaddr_in *saddr = &server->saddr;
185         struct pmap *pmap = &server->pmap;
186         const unsigned long prog = pmap->pm_prog, *p_vers;
187         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
188         const u_short port = (u_short) pmap->pm_port;
189         unsigned long vers = pmap->pm_vers;
190         unsigned short p_port;
191
192         p_prot = prot ? &prot : protos;
193         p_vers = vers ? &vers : versions;
194         rpc_createerr.cf_stat = 0;
195         for (;;) {
196                 saddr->sin_port = htons(PMAPPORT);
197                 p_port = getport(saddr, prog, *p_vers, *p_prot);
198                 if (p_port) {
199                         if (!port || port == p_port) {
200                                 saddr->sin_port = htons(p_port);
201                                 if (verbose) {
202                                         printf(_("%s: trying %s prog %ld vers "
203                                                 "%ld prot %s port %d\n"),
204                                                 progname,
205                                                 inet_ntoa(saddr->sin_addr),
206                                                 prog, *p_vers,
207                                                 *p_prot == IPPROTO_UDP ?
208                                                         "udp" : "tcp",
209                                                 p_port);
210                                 }
211                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
212                                         goto out_ok;
213                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
214                                         goto out_bad;
215                         }
216                 }
217                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED)
218                         goto out_bad;
219
220                 if (!prot) {
221                         if (*++p_prot)
222                                 continue;
223                         p_prot = protos;
224                 }
225                 if (vers == pmap->pm_vers) {
226                         p_vers = versions;
227                         vers = 0;
228                 }
229                 if (vers || !*++p_vers)
230                         break;
231         }
232
233 out_bad:
234         return 0;
235
236 out_ok:
237         if (!vers)
238                 pmap->pm_vers = *p_vers;
239         if (!prot)
240                 pmap->pm_prot = *p_prot;
241         if (!port)
242                 pmap->pm_port = p_port;
243         rpc_createerr.cf_stat = 0;
244         return 1;
245 }
246
247 static int probe_nfsport(clnt_addr_t *nfs_server)
248 {
249         struct pmap *pmap = &nfs_server->pmap;
250
251         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
252                 return 1;
253
254         if (nfs_mount_data_version >= 4)
255                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
256         else
257                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
258 }
259
260 static int probe_mntport(clnt_addr_t *mnt_server)
261 {
262         struct pmap *pmap = &mnt_server->pmap;
263
264         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
265                 return 1;
266
267         if (nfs_mount_data_version >= 4)
268                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
269         else
270                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
271 }
272
273 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
274 {
275         struct pmap *nfs_pmap = &nfs_server->pmap;
276         struct pmap *mnt_pmap = &mnt_server->pmap;
277         struct pmap save_nfs, save_mnt;
278         int res;
279         const unsigned long *probe_vers;
280
281         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
282                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
283         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
284                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
285         if (nfs_pmap->pm_vers)
286                 goto version_fixed;
287
288         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
289         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
290         probe_vers = (nfs_mount_data_version >= 4) ?
291                         probe_mnt3_first : probe_mnt1_first;
292
293         for (; *probe_vers; probe_vers++) {
294                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
295                 if ((res = probe_nfsport(nfs_server) != 0)) {
296                         mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
297                         if ((res = probe_mntport(mnt_server)) != 0)
298                                 return 1;
299                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
300                 }
301                 switch (rpc_createerr.cf_stat) {
302                 case RPC_PROGVERSMISMATCH:
303                 case RPC_PROGNOTREGISTERED:
304                         break;
305                 default:
306                         goto out_bad;
307                 }
308                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
309         }
310
311 out_bad:
312         return 0;
313
314 version_fixed:
315         if (!probe_nfsport(nfs_server))
316                 goto out_bad;
317         return probe_mntport(mnt_server);
318 }
319
320 static int probe_statd(void)
321 {
322         struct sockaddr_in addr;
323         unsigned short port;
324
325         memset(&addr, 0, sizeof(addr));
326         addr.sin_family = AF_INET;
327         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
328         port = getport(&addr, 100024, 1, IPPROTO_UDP);
329
330         if (port == 0)
331                 return 0;
332         addr.sin_port = htons(port);
333
334         if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
335                 return 0;
336
337         return 1;
338 }
339
340 /*
341  * Attempt to start rpc.statd
342  */
343 int start_statd(void)
344 {
345 #ifdef START_STATD
346         struct stat stb;
347 #endif
348
349         if (probe_statd())
350                 return 1;
351
352 #ifdef START_STATD
353         if (stat(START_STATD, &stb) == 0) {
354                 if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
355                         system(START_STATD);
356                         if (probe_statd())
357                                 return 1;
358                 }
359         }
360 #endif
361
362         return 0;
363 }
364
365 /*
366  * nfs_call_umount - ask the server to remove a share from it's rmtab
367  * @mnt_server: address of RPC MNT program server
368  * @argp: directory path of share to "unmount"
369  *
370  * Returns one if the unmount call succeeded; zero if the unmount
371  * failed for any reason.
372  *
373  * Note that a side effect of calling this function is that rpccreateerr
374  * is set.
375  */
376 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
377 {
378         CLIENT *clnt;
379         enum clnt_stat res = 0;
380         int msock;
381
382         switch (mnt_server->pmap.pm_vers) {
383         case 3:
384         case 2:
385         case 1:
386                 if (!probe_mntport(mnt_server))
387                         return 0;
388                 clnt = mnt_openclnt(mnt_server, &msock);
389                 if (!clnt)
390                         return 0;
391                 res = clnt_call(clnt, MOUNTPROC_UMNT,
392                                 (xdrproc_t)xdr_dirpath, (caddr_t)argp,
393                                 (xdrproc_t)xdr_void, NULL,
394                                 TIMEOUT);
395                 mnt_closeclnt(clnt, msock);
396                 if (res == RPC_SUCCESS)
397                         return 1;
398                 break;
399         default:
400                 res = RPC_SUCCESS;
401                 break;
402         }
403
404         if (res == RPC_SUCCESS)
405                 return 1;
406         return 0;
407 }